今天小编跟大家讲解下有关当遇到动画需求的时候该使用什么方式实现呢 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关当遇到动画需求的时候该使用什么方式实现呢 的相关资料,希望小伙伴们看了有所帮助。
背景相信大家在平时的需求当中 都会遇到一些动画需求 那么大家又是如何抉择实现方式呢 这里我大概分为4种情况 gif图/animation/ae导出骨骼动画/ae导出canvas
1. gif图硬核解决没错!最简单的就是让我们的设计师直接给我们gif图 那么在我们前端看来就是一张图片的问题 只需控制展示和隐藏即可。简直是最舒服的方式了。那么gif图播放动画有什么缺陷呢
在添加一些效果的时候会出现锯齿 并且色值不丰富;如果在同一时间存在多个gif图并行播放 这时候就有问题了。下面我们来看一下是什么问题假设现在有这样的一个场景:在一个列表里 当用户点击每一个列表中的单项 都在该单项中间出现一个绽放的烟花。
简单啊!马上写给你!
export class Item extends Component { timer: Nodejs.Timeout playing() { if (this.timer) { clearTimeout(this.timer); } this.setState({ fire: true }); this.timer = setTimeout(() => { this.setState({ fire: false }); }, 1500); } render() { return ( <div> <div>我要放烟花!</div> { this.state.fire ? <img src="fire.png“ /> : null } </div> ); }}export class List extends Component { render() { return ( <div> { dataList.map((data) => { return <Item key={data.id} /> }) } </div> ); }}假设我们现在的烟花gif是1500ms循环播放 那么用户按照列表顺序点击item现象是什么
当用户点击了第一个item 此时第一个item的烟花绽放;用户在500ms后点击了第二个item 此时第二个item上的烟花会和第一个item的烟花保持一样的节奏 也就是第二个item的烟花从500ms开始播放 持续时间1000ms;用户在1000ms后点击了第三个item 此时第三个item的烟花和前面两个item的烟花节奏保持一致 也就是第三个item的烟花从1000ms开始播放 持续时间500ms;但是我们的定时器又傻乎乎的设定了1500ms 那么第二个item上的烟花就是绽放一次后 还有500ms的时间播放烟花的前500ms 第三个item上的烟花一样的道理;这就存在问题了 不仅不同时间点击的播放的烟花节奏一致 而且会出现错乱播放的情况。那么我们一定要使用gif图来实现呢 可不可以呢 可以。由于同一张gif图浏览器只会保存一个播放进度 导致不同时间开始的gif图都是第一张播放的进度。那么我们只需要让这一张烟花gif变成多张gif就可以。
export class Item extends Component { ... render() { return ( <div> <div>我要放烟花!</div> { this.state.fire ? <img src={`fire.png?${this.props.id}`} /> : null } </div> ); }}export class List extends Component { render() { return ( <div> { dataList.map((data) => { return <Item key={data.id} id={data.id} /> }) } </div> ); }}在gif后面加个唯一的id 那么每一个item上都是一张不同的gif图 这样每个烟花的播放进度各自维护 不会共享 上述问题就解决了。但是引来了另外一个问题 因为加了id 如果列表有10个单项 就会加载10张fire.png 所以如果是这样的场景 我不推荐大家使用gif图实现了。
2. animation相信大家都已经使用过@keyframe和animation实现过动画了 该功能强大无比 能实现绝多数简单的动画需求。
那么我们依旧是拿烟花的例子来分析 既然gif图片有多个播放进度共享的问题 那么使用animation切换background-position是不是ok了。
是的 我们让设计师将烟花导出一个序列帧 我们利用step进行位移背景 可以播放动画
$frame: 20;$ratio: 100% / $frame;@keyframes playing { @for $i from 0 to $frame { #{$i * $ratio} { background-position-x: -88px * $i; } } 100% { background-position-x: 0; }}.fire { width: 88px; height: 88px; background: fire.png; background-size: 88px; &.play { animation: playing 1500ms step-end }}是不是也很简单!不是!这里还是有坑的!大家想想 序列图跟background-position改变的方向是同一个方向还是不同方向好 (ps:序列图是左往右 background-position-x改变是同向。background-position-y改变是不同向)
答案是不同向的没问题。在我一开始接触一个需求我们使用以上方式进行实现的时候 郁闷地发现动画播放的时候在一些机型上会左右抖动 马上就知道了是由于编译转化成rem单位 导致在一些宽度的机型上rem转化为px的时候存在无敌的小数点取舍 导致上一帧向左偏移了1px 下一帧向右偏移了1px。所以我的第一个想法就是不让编译帮忙转化成rem了 但是这样做的话不同机型又不能做到适配。
后面将序列帧的方向改成了纵向的试试 惊奇地发现不抖动了。
突然一道闪电闪过 想明白了 原来是background-size在作祟。因为我们的序列帧图片比较长 所以我们要设置background-size为图片大小 然后改变background-position-x来实现动画。
由于我将序列帧改成了纵向排列
$frame: 20;$ratio: 100% / $frame;@keyframes playing { @for $i from 0 to $frame { #{$i * $ratio} { // 纵向改变位置 background-position-y: -88px * $i; } } 100% { background-position-y: 0; }}.fire { width: 88px; height: 88px; background: fire.png; background-size: 100%; &.play { animation: playing 1500ms step-end }}那么此时我们锁定的是图片的宽度 而改变的是纵向的位置 那么问题就得到了解决。
3. ae导出骨骼动画/ae导出canvas当动画非常复杂的时候 比如我们要实现一个猪的走动 我们要在猪停止走动的的时候停止头的晃动 在猪走路的时候让他的头晃动起来。利用序列帧使用上述animation也可也实现 但是毕竟序列帧图片较大 但是有没有更节省资源的方式呢。
设计师可以利用ae导出一只狗的分部位的动画 就是猪的头/身体/四肢/尾巴都是拆开的素材 这几个部位各自旋转/位移/缩放 组合在一起即可完成一只猪。
这种方式基本上将设计师的素材拿过来修修补补就可以了 而且动画效果很好 缺点就是transform能实现的效果之外的效果就不能使用了 比如形变。
ae导出canvas需要配合lottie库使用 工作量也都是在设计师那边。
来源:爱蒂网