转载自前端备忘录-江湖术士
我们来看看看看,搭配 anime.js
,并采用领域驱动,如何做动画吧~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function useAnime(props: AnimeParams = {}) { const ref = useRef<any>(); const animationRef = useRef<AnimeInstance>(); useLayoutEffect(() => { if (!ref.current) { console.warn("please bind the anime ref while useAnime"); return; } animationRef.current = anime({ ...props, targets: [ref.current], }); }, [ref, animationRef, props]); return { ref, animationRef }; }
|
给两个 可变 ref
出来,为啥是 ref
?因为不涉及响应式呀~
用的时候,只需要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export default function Test() { const { ref, animationRef } = useAnime({ translateX: 300, loop: true, duration: 2000, }); return ( <div> <div ref={ref} style={{ width: "100px", height: "100px", backgroundColor: "black" }} ></div> </div> ); }
|
如果你想要手动控制动画的运行:
1 2 3 4 5 6 7 8 9 10 11
| const { ref, animationRef } = useAnime({ translateX: 300, loop: true, duration: 2000, autoplay: false, }); useEffect(() => { setTimeout(() => { animationRef.current?.play?.(); }, 3000); }, [animationRef]);
|
改一下,按照正常逻辑来写就行了
而且 anime.js
提供的动画控制相当不讲道理:
对了,如果你想要的进出动画,那么封装两个 useAnime
作为服务:
1 2 3 4 5 6 7 8 9 10
| function useSomeEnterAnimation(exist: any) { // 进入动画 const { ref, enterAnimationRef } = useAnime({ /* ... *}) // 销毁动画 注意加入 const {ref,leaveAnimationRef} = useAnime({/* ... */ }); return { ref, animationRef, leaveAnimationRef }; } export const SomeEnterAnimation = createContext(null);
|
然后封装一个组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function Enter(props: PropsWithChildren<{ AnimationProvider: Context }>) { const { ref, enterAnimationRef, leaveAnimationRef } = useContext( AnimationProvider ); const children = useRef<any>(); useEffect(() => { if (props.children) { children.current.ref = props.children; enterAnimationRef.current?.play?.(); } else { leaveAnimationRef.current?.play?.(); setTimeout(() => { children.current = props.children; }, 3000); } }, [props]); return <div ref={ref}>{children.current}</div>; }
|
搞定,然后把你想要实现的进出动画,随意用起来:
1 2 3
| <Enter AnimationProvider={SomeEnterAnimation}> <div>see me dance</div> </Enter>
|
关键是,除了进出动画组件需要自己定义以外,其它的都不需要你去封装,而这个可以预先写好或者交给其他工具
接下来,还有更有意思的:
我们知道,animejs
的 typescript
支持是有问题的,为啥?
这是 animejs
的类型声明
首先其带了个 [anyAnimateProperty:string] :any
怎么办?
初始化问题,类型问题,当你需要代码具有解释性的时候,想想面向对象吧~
1 2 3 4 5 6 7 8 9 10 11 12
| class CustomAnimeConfig { // 自定义全局配置 duration: 2000; keyframes: []; autoplay: true; // ... }
// 使用时 const animeProps = new CustomAnimeConfig(); animeProps.autoplay = true; const { ref, animationRef } = useAnime(animeProps);
|
同理,类似这样的思想,还可以用来处理图标库,全局粘贴,二维码之类的奇葩
DDD
有一个原则:
任何复杂问题,独立拆分开,都不复杂
只要做了状态逻辑复用(服务),所有你认为很难得问题,都可以降级到非常简单的几个模块
你对第三方的依赖程度将会大大降低
你想怎么用,你就可以怎么用
更多的时候,你在思考你自己的业务逻辑,并非其他事务
这时候,你会发现,很多第三方工具其实并没有帮你什么忙,你只是上个时代需要他,而且他们大部分都是在做跳过框架实现逻辑复用的事情