年终活动h5动画总结
css3 + react-id-swiper + react + redux + saga
采用postcss的Autoprefixer插件,即可满足大多数oppo,vivo手机的兼容性问题。
1. 动画相关主要内容:
- 流星
- 闪烁星星
- 字的晃动
- 人的移动(动画 + 监听动画结束时间)
- 桥的铺垫
- 开启旅程按钮缩放
- 旋转(+ 兼容)
- react-id-swiper
- swiper配合css3实现切换
2. css3动画语法
- animation
animation-name
animation-duration
animation-timing-function 速度曲线
animation-delay
animation-iteration-count
animation-direction
play-state
fill-mode
- transform
transform 属性向元素应用 2D 或 3D 转换。该属性允许我们对元素进行旋转、缩放、移动或倾斜。
其中 transform-origin (属性改变被转换元素的中心)。
- transition
三者区别: animation 动画,关键帧,往复性。 transition 过渡, 属性,触发动作,一过性。 transform 变换, 复杂的变换参数。
3. 示例 https://github.com/hytStart/R...
- 流星
改变位置translate3d
,透明度opacity
和大小scale
。
流星尾巴采用伪元素元素:after
旋转-45deg
(旋转基点为左transform-origin: left;
);采用border
可以实现,靠近头部越亮,靠近尾部越暗。
.star {
display: block;
width: 5px;
height: 5px;
border-radius: 50%;
background: #FFF;
top: 10px;
left: 200px;
position: relative;
animation: star-ani 6s infinite ease-out;
box-shadow: 0 0 5px 5px rgba(255, 255, 255, .3);
opacity: 1;
}
.star:after {
content: '';
display: block;
top: 0px;
left: 40%;
border: 0px solid #fff;
border-width: 0px 90px 2px 90px;
border-color: transparent transparent transparent rgba(255, 255, 255, .3);
transform: rotate(-45deg) translate3d(1px, 3px, 0);
box-shadow: 0 0 1px 0 rgba(255, 255, 255, .1);
transform-origin: left;
}
@keyframes star-ani {
0% {
opacity: 0;
transform: scale(0) rotate(0) translate3d(0, 0, 0);
}
50% {
opacity: 1;
transform: scale(1) rotate(0) translate3d(-100px, 100px, 0);
}
100% {
opacity: 0;
transform: scale(1) rotate(0) translate3d(-200px, 200px, 0);
}
}
- 闪烁星星
改变透明度
.shine {
background: url('../../../../images/action/icon-star1.png') no-repeat center;
background-size: 100%;
width: 30px;
height: 40px;
position: absolute;
top: 90px;
left: 100px;
opacity: 0;
animation: opacity-change 0.5s ease-in-out infinite;
}
@keyframes opacity-change {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
- 字
分为两段动画,下降和上升。translateY
改变即可。
.text-item-0 {
position: absolute;
width: 25px;
height: 75px;
top: 60px;
left: 100px;
background: url('../../../../images/action/S-start.png') no-repeat center;
background-size: 100%;
animation: letter-0 1.5s ease-in-out both, letter-0-1 2.0s ease-in-out 1.5s both;
}
@keyframes letter-0 {
0% {
transform: translateY(0)
}
50% {
transform: translateY(80px)
}
100% {
transform: translateY(0px)
}
}
@keyframes letter-0-1 {
0% {
opacity: 1;
}
100% {
top: -80px;
opacity: 0;
}
}
- 人移动
切换dom,添加类控制移动和暂停,以及切换背景人物。监听animationend
事件。
jsx
{
peopleMove ?
<div
className={`${style.people_move} ${!pausedState && style.people_paused}`}
ref={start2 => { this.start2 = start2 }}
/>
:
<div className={style.people} />
}
css
.people {
width: 20px;
height: 64px;
position: absolute;
left: 10px;
top: 130px;
background: url('../../../../images/action/people.png') no-repeat center;
background-size: 100%;
opacity: 0;
animation: peopleUp 1s ease-in-out 0.5s both;
}
.people_move {
background: url('../../../../images/action/people_moveleft.gif');
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
width: 20px;
height: 64px;
position: absolute;
left: 10px;
top: 130px;
opacity: 1;
animation: PeopleMove 1.5s linear 0s both;
}
.people_paused {
width: 20px;
height: 64px;
background: url('../../../../images/action/people.png');
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
animation-play-state: paused;
}
@keyframes peopleUp {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes PeopleMove {
0% {
left: 10px;
top: 130px;
}
100% {
top: 20px;
left: 180px;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
}
// 监听动画结束时机
componentDidUpdate() {
const { peopleMove } = this.state
if (peopleMove) {
this.start2.addEventListener('animationend', this.end)
this.start2.addEventListener('webkitAnimationEnd', this.end)
this.start2.addEventListener('mozAnimationEnd', this.end)
this.start2.addEventListener('oAnimationEnd', this.end)
}
}
- 桥的出现
配合background-size: cover;
属性实现。
.brige {
width: 0px;
background: url('../../../../images/action/bridge.png');
background-size: cover;
background-repeat: no-repeat;
height: 100px;
position: absolute;
top: 40px;
left: 30px;
animation: BridgeFadeIn 3s linear both;
opacity: 0;
}
@keyframes BridgeFadeIn {
0% {
width: 0px;
opacity: 0;
}
100% {
width: 200px;
opacity: 1;
}
}
- 渐变大(开始旅程)
利用transform scale
2D 缩放转换。
.icon-ciecle {
display: block;
position: absolute;
left: 100px;
top: 80px;
width: 30px;
height: 30px;
background: url('../../../../images/action/icon-light.png') no-repeat center;
background-size: 100%;
animation: warn 1.2s ease-in-out 0s infinite both;
}
@keyframes warn {
0% {
transform: scale(0.1);
opacity: 0.0;
}
25% {
transform: scale(0.2);
opacity: 0.3;
}
50% {
transform: scale(0.4);
opacity: 0.5;
}
75% {
transform: scale(0.6);
opacity: 0.7;
}
100% {
transform: scale(0.8);
opacity: 0.0;
}
}
- 旋转
ios的animation-play-state: paused;
不起作用,且animation
动画不可写在新增类里,必须写在一个类里。(测试中发现,这里有疑问。)
.music_img {
width: 40px;
height: 40px;
display: block;
position: absolute;
left: 100px;
top: 80px;
animation: rotating 3s linear infinite;
animation-play-state: running;
}
.rotate-pause {
animation-play-state: paused;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
- react-id-swiper
Swiper的react版本,基本api都支持,具实验目测相当于Swiper v4。
- 不一定通过api。也可以通过重写css实现。
- 重写css切换效果的话,会影响它本身的动画效果,所以可通过添加不可滑动来控制。
1.
.swiper-wrapper {
transition-delay: 1.6s;
}
2.
noSwiping: true,
noSwipingClass: 'stop-swiping',
- Swiper实现的迷宫切换
1.swiper的effect
属性与控制背景图片的opacity
,利用时间差实现最终效果。包括迷宫的切换,背包男的出现与消失,弹窗的出现与消失。
2.swiper属性的activeIndex
,可以得到滑动到第几页,通过改变dom
或者改变类,控制第几页动画的发生。
// 根据activeIndex拿到的页数,控制该页数state的改变,通过切换dom和添加类的方式,达到进场与退场的动画效果。
// 添加类stop-swiping控制不可滑动,动画完成后,才可继续滑动下一页。
const mazeStyle = classNames({
[styles['maze-1']]: true,
[styles['maze-out']]: firstMazeOut,
})
const peopleStyle = classNames({
[styles['people-1']]: true,
[styles['people-out']]: firstMazeOut,
})
const popCardStyle = classNames({
[styles['pop-card-container']]: true,
[styles['pop-card-out']]: firstMazeOut,
})
{
firstMazeIn ?
<div className={!isSlideFirst && "stop-swiping"}>
<div className={styles['maze-container']}>
<div className={mazeStyle} />
<div className={peopleStyle} />
</div>
<div className={popCardStyle}>
<ModalScene {...this.props} />
</div>
</div>
:
<div /> // 不添加会影响swiper自身页数的判断
}
4. 动画总结
别人写的真牛X,自己只会opacity
,translate
。
5. 待完善
- html2canvas
- 资源加载
- jsbridge使用与注意事项
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。