因为活动期间内切换的利益点太多(图片是运营自行配置的),一个是设计的工作量太大,另一方面是帧动画体积太大,即使我制作完压缩后,一次往返动画也有接近2兆,因为运维的限制,图片超过200K不予上传,所以,自行写一个动效来实现这个帧动画
切换动效最后呈现结果从设计稿视觉可以得到几个信息点:
- 图片被切分为多个碎片,成为10*10
- 图片轮换以列为单位,列动画执行存在延时,视觉上最多有两列在发生翻转
- 每列10个碎片均以x轴进行翻转
实现思路:
- 填充容器,每个碎片都有3D翻转视觉,因此每个小碎片都有正反两面
- 将碎片容器进行定位,按照列行循环的方式进行定位
- 按列数进行延时动画触发(比如setTimeout,不过我用了tweenMax,所以是用的自带方法)
额外说明:入参里并没有对图片数量进行限制,但实现只拿第一第二张图,因为项目时间而且动效复用可能性极低,所以不会过多的完善与拓展,有需要的同学自行拓展
实现代码如下:
<template>
<div ref="container" class="v-container">
<div ref="panel" class="v-panel"></div>
</div>
</template>
<script>
import Tool from '@/mod/util/tool/1.0/tool.js';
import { TweenMax } from 'gsap'; // gsap动画库
export default {
name: 'v-effect',
props: {
// 接收图片路径,只能是两个图片,这里没做限制,原因上面说了
imgList: {
type: Array,
default: () => []
}
},
data () {
return {
imgNum: 0,
rows: 10,
colums: 10,
container: null,
panel: null
};
},
methods: {
// 图片碎片化,填充容器,暂时只支持两张图片互相切换,不可多张轮换
_generatePanels () {
let vDom = '';
let count = this.rows * this.colums;
let [x, y] = [0, 0];
let {clientWidth: cw, clientHeight: ch} = this.container; // containerWidth, containerHeight: 容器宽高
let [fw, fh] = [~~(cw / this.colums), ~~(ch / this.rows)]; // fragmentWidth, fragmengHeight: 碎片宽高,必须取整,否则小数点能让效果出现间距
for (let i = 0; i < count; i++) {
vDom += `
<div id="item_${i}" class="panelItem trans3d" style="left: ${x * fw}px; top: ${y * fh}px; width: ${fw}px; height: ${fh}px;">
<div class="front" style="background: url(${Tool.addDomain(this.imgList[this.imgNum])}) -${x * fw}px -${y * fh}px no-repeat / ${cw}px ${ch}px;"></div>
<div class="back" style="background: url(${Tool.addDomain(this.imgList[this.imgNum + 1])}) -${x * fw}px -${y * fh}px no-repeat / ${cw}px ${ch}px;;"></div>
</div>`;
y++;
if (y === this.rows) {
y = 0;
x++;
}
}
this.panel.innerHTML = vDom;
},
// 碎片动效
_effect () {
this._generatePanels();
let panelsRows = this.panel.querySelectorAll('.panelItem');
let arrPanelsRows = Array.prototype.slice.call(panelsRows);
for (let i = 0; i < this.colums; i++) {
TweenMax.delayedCall(0.2 * i, () => {
let groupPanel = arrPanelsRows.slice(i * this.rows, this.rows * (i + 1));
TweenMax.staggerTo(groupPanel, 0.3, {rotationX: 180, yoyo: true, repeat: -1, repeatDelay: 5});
});
}
},
init () {
this.container = this.$refs.container;
this.panel = this.$refs.panel;
this._effect();
}
},
mounted () {
this.init();
}
};
</script>
<style lang="less">
/* 底层有pxToRem,自行webpack引入插件 */
.v-container {
position:absolute;
top: 548px;
left: 125px;
width: 500px;
height: 170px;
perspective: 600px;
}
.v-panel {
position:absolute;
width: 100%;
height: 100%;
transform-style: preserve-3d;
}
.panelItem {
position: absolute;
transform-style: preserve-3d;
}
.front,
.back {
position: absolute;
top: 0;
left: 0;
.size(100%);
backface-visibility: hidden;
}
.back {
transform: rotateX(180deg);
}
</style>
这里有几个要点需要说明:
- 图片的容器必须取整,因为移动端转为rem单位的原因,我们js中获取容器大小是会出现小数点的。小数点必须处理掉,否则后续的图片定位会有出现间隙
- 行数与列数的分割刚好设计稿为10 * 10,假设结果带小数点的,碎片的衔接大概率会出现间距。这也是为什么上面说必须取整,因此碎片定位的时候都是整数,不会出现衔接间隙
网友评论