2. CSS 动画综合实践(太空舱)2.1 太空舱动画介绍先简单介绍一下这个动画的一些背景:
- 用户点击中间的抽奖按钮后会展示抽奖动画,然后展示将要前进的步数,小人会在跑道上跑动到相应的位置,到达此次终点后,小人展示停止动画并且显示中奖结果 。
- 用户还可以通过长按中奖按钮触发显示五连抽和十连抽 , 当点击五连抽时,首先会展示抽奖动画,然后展示将要前进的步数,小人会在跑道上瞬间移动到相应的位置,然后展示停止的动画 , 紧接着展示中奖动画 。十连抽的过程和五连抽相同 。
大体思路是将各个功能封装为类,并且控制每个类实现的功能较?。?这样可以更大程度地实现类的复用 。像下面介绍的
Animate CSS
这个动画库也是封装了很多类,想要什么效果直接引用即可,这里也是同理 。并在此基础上定义了一些函数对跑动等功能进行了封装,整体流程如下图所示 。文章插图
结合上图对太空舱动画中的一些点做一些简单的说明 。
跑动的实现
通过关键帧动画,将各个方向相邻两格之间的跑动封装为一个类 。每一帧代表跑动的姿态,并且每一帧通过 translate 向前进的方向移动,最后一帧刚好到达下一格 。
以向南方向的跑动为例 。
- 向南奔跑的类
@translateSouthX
和@translateSouthY
代表向南每两格 X 和 Y 方向的距离,.run-south-animation
是一个mixin
,代表向南跑动的动画 。@import "./animation.less";@translateSouthX: -25px;@translateSouthY: 13.5px;// 向南方向跑动.run-south {background: url("...");background-size: 1680px 255px;.run-south-animation(@translateSouthX, @translateSouthY);}
- 向南奔跑的动画
.run-south-animation
这个mixin
是向南奔跑动画的实现,注意animation-timing-function
的值要设置为
steps(1)
, 因为这里每两帧之间不需要补间动画 。forwards
表示最后的状态保持为动画结束时的状态 。animation-iteration-count
的值为 1 表示动画只执行一次不需要重复,因为这里是为了封装一个移动一格的动画,每次使用这个封装好的类时只应该跑动一格 。关键帧
runSouth
中,经过多次测试最终选择了使用 28 帧,因为在此帧数下,动画较为流畅,图片的大小也可以接受 。在关键帧动画中 , 帧数的选取至关重要,要根据具体的需求反复测试,太多或太少都不合适 。@animationDuration: .7s// 向南奔跑动画.run-south-animation(@translateX, @translateY) {animation: runSouth @animationDuration forwards;animation-iteration-count: 1;animation-timing-function: steps(1);@keyframes runSouth {0% {transform: translate(0, 0);background-position: 0px -2*(@singleAstronautWidth+@verticalInterval);}...50.4% {transform: translate(@translateX / 28 * 14, @translateY / 28 * 14);background-position: -840px -2*(@singleAstronautWidth+@verticalInterval);}...100% {transform: translate(@translateX / 28 * 28, @translateY / 28 * 28);background-position: -1680px -2*(@singleAstronautWidth+@verticalInterval);}}}
如何再次触发跑动动画当第一次将跑动的类(假设向南)赋予展示小人的元素时小人可以正常跑动,但是如果紧接着再将这个类赋予这个元素时,即使组件中的 state 改变了,也不会触发小人继续向南跑动了 。
为了验证这一点,写了一个小 demo,如下代码所示,在 3 s 后改变 state 中的 cls 和 doc , 由于 cls 没有发生变化 , 即使 doc 发生了变化,也不会触发
first
类中的动画重新执行 , 在线源码和演示 。class App extends React.Component {state = {cls: 'first',doc: 'hello hello'}componentDidMount() {setTimeout(() => {this.setState({cls: 'first',doc: 'world world'})}, 3000)}render() {return <div><h1 className={this.state.cls}>{this.state.doc}</h1></div>;}}ReactDOM.render(<App />, document.getElementById("root"));
body {height: 100vh;margin: 0;display: grid;place-items: center;max-width: 250px;margin: 0 auto;}.first {animation: myfirst 2s linear both;animation-iteration-count: 1;}@keyframes myfirst{from {background: red;}to {background: yellow;}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 为什么CSS中的calc函数可能会不生效?
- day03-CSS
- 二 Three光线检测-实现摄像机向鼠标点击位置滑动动画
- React动画实现方案之 Framer Motion,让你的页面“自己”动起来
- CSS 渐变锯齿消失术
- 前端开发日常——CSS动画无限轮播
- Vue3 JS 与 SCSS 变量相互使用
- 超强的纯 CSS 鼠标点击拖拽效果
- 迪士尼所有卡通人物 迪士尼所有人物大全
- 前端三件套 HTML+CSS+JS基础知识内容笔记