使用 React Hooks 在 CSS Grid 中创建动画元素
一般来说,grid-row无法grid-columns进行过渡,而且很难创建动态动画元素。
我发现使用创建元素进行计算是可行的。
例如,我尝试复现UpLabs SmoothBottomBar。
而且它能够成功
迈步
1. 创建基础网格
首先,我创建裸网格。
预览
完整代码
https://stackblitz.com/edit/react-ts-animation-grid-1
描述
主网格在这里。我用的是它react-icons。它非常实用。
那个icons数组可能有点棘手。
import { FiUser, FiHome, FiInbox } from "react-icons/fi"
export const Menu1 = () => {
const icons = [FiHome, FiInbox, FiUser]
return (
<Container>
<MenuGrid>
{icons.map((Icon) => (
<IconWrap>
<Icon />
</IconWrap>
))}
</MenuGrid>
</Container>
)
}
2. 创建用于计算的元素。
接下来,添加用于计算的单元格。
预览
完整代码
https://stackblitz.com/edit/react-ts-animation-grid-2
描述
我需要向网格列中添加图标,并获取单元格位置状态。
// Item got props that passed to `grid-column`
const Item = styled.div`
${({ x }) => css`
grid-column: ${x};
`}
grid-row: 1;
`
export const Menu2 = () => {
const [gridPosition, setGridPosition] = useState<number>(1)
// ...
{icons.map((Icon, i) => (
<Item x={i + 1} onMouseOver={(e) => setGridPosition(i + 1)}>
<IconWrap>
<Icon />
</IconWrap>
</Item>
))}
// ...
}
并附加名为“.”的单元格PositionCalcurator。
// PositionCalcurator extends Item. That can set row and cell.
const PositionCalcurator = styled(Item)`
border: 1px solid red; // for debug
`
export const Menu2 = () => {
const [gridPosition, setGridPosition] = useState<number>(1)
const calcuratorRef = useRef<HTMLElement>(null) // calucrator has `ref`
const icons = [FiHome, FiInbox, FiUser]
return (
<Container>
{/* ... */}
<PositionCalcurator ref={calcuratorRef} x={gridPosition} />
{/* ... */}
</Container>
)
}
3. 添加动画光标。
接下来,添加悬停光标。
预览
完整代码
https://stackblitz.com/edit/react-ts-animation-grid-3
描述
我已将其设置ref为PositionCalcurator,现在我们可以获得与网格位置相同的计算参考矩形位置。
我创建了cursorRect一个状态和一个钩子useEffect。当状态改变时,这个效果会被调用gridPosition。(但使用时可能会有警告eslint-plugin-react-hooks)
export const Menu3 = () => {
const [gridPosition, setGridPosition] = useState<number>(1)
const [cursorRect, setCursor] = useState<null | Rect>(null) // append
const calcuratorRef = useRef<HTMLElement>(null)
useEffect(() => {
if (!calcuratorRef.current) return
const top = calcuratorRef.current.offsetTop
const left = calcuratorRef.current.offsetLeft
const width = calcuratorRef.current.clientWidth
const height = calcuratorRef.current.clientHeight
const cursor = { top, left, width, height }
setCursor(cursor)
}, [gridPosition])
我添加了Cursor传递过来的组件cursorRect。该元素启用了动画功能,因为它与网格隔离,position:absolute并且设置了动画属性。
const Cursor = styled.div`
position: absolute;
transition: 0.4s;
transition-timing-function: ease-in-out;
background: green;
opacity: 0.5;
${({ top, left, width, height }) => css`
top: ${top}px;
left: ${left}px;
width: ${width}px;
height: ${height}px;
`};
`
export const Menu3 = () => {
const [cursorRect, setCursor] = useState<null | Rect>(null) // append
// ...
return (
<Container>
{/* Append first for z-index! */}
{cursorRect && <Cursor {...cursorRect} />}
<Cursor>Container由于 z-index 的原因,需要将第一个元素放在前面。
4. 文字动画。
这与网格动画无关,但我模仿了原作。
预览
完整代码
https://stackblitz.com/edit/react-ts-animation-grid-4
描述
我只想使用:hover伪类,但我添加了active属性,因为它无法与光标状态同步。
export const Menu4 = () => {
const [gridPosition, setGridPosition] = useState<number>(1)
const isActive = useCallback((x) => gridPosition === x, [gridPosition])
const icons = [["Home", FiHome], ["Inbox", FiInbox], ["Profile", FiUser]]
return (
{/* ... */}
{icons.map(([text, Icon], i) => (
<Item x={i + 1} onMouseOver={(e) => setGridPosition(i + 1)}>
<AnimateIcon
x={i + 1}
onMouseOver={(e) => setGridPosition(i + 1)}
text={text}
active={isActive(i + 1)}
>
<Icon />
</AnimateIcon>
</Item>
))}
)
和变化AnimateIcon
const AnimateIconInner = styled(IconWrap)`
transition: 0.5s;
::after {
font-size: 0.6em;
transition: 0.5s;
overflow: hidden;
content: attr(data-text);
${({ active }) => css`
width: ${active ? "100%" : "0px"};
`}
}
`
const AnimationContainer = styled.div`
width: auto;
`
const AnimateIcon = ({ x, onMouseOver, active, children, text }) => {
return (
<Item x={x} onMouseOver={onMouseOver}>
<AnimateIconInner active={active} data-text={text}>
<AnimationContainer>{children}</AnimationContainer>
</AnimateIconInner>
</Item>
)
}
5. 车削和精加工。
这里是修改后的版本(与第一个演示版相同)
完整代码:https://stackblitz.com/edit/react-ts-animation-grid-final
结论
我找不到使用纯 CSS 实现 CSS Grid 动画的方法。
另一方面,能够使用网格进行坐标计算似乎很有用。
我创建了另一个类似《洛克人》关卡选择界面的模式。https
://amination-grid-menu.netlify.com/
使用 CSS Grid 实现快乐动画!
文章来源:https://dev.to/terrierscript/create-animating-element-on-css-grid-with-react-hooks-50g9





