在前端实现序列帧动画的时候,可以批量引入图片,并使用img标签对图片进行渲染,用定时器循环给图片设置visibility为‘visible’/‘hidden’,可是实现序列帧动画。基于项目中可能会多处使用,这里统一封装为一个组件。
组件代码实现:
<template>
<div
ref="imgAnimation" class="img-animation"
:class="animationClass"
>
<!--所有图片加载完成之前,先展示第一张图片-->
<img
v-if="!isAnimation && imgList.length" :class="animationClass"
:src="imgList[0]"
>
</div>
</template>
<script>
export default {
name: 'ImgAnimation',
props: {
// 每张图片展示的时间
timeRange: {
type: Number,
default: 100
},
// 自定图片的样式
animationClass: {
type: String,
default: 'sphere-animation'
},
// 图片list
imgList: {
type: Array,
default: () => []
}
},
data() {
return {
timer: null,
promiseList: [],
// 动画是否开启
isAnimation: false
};
},
watch: {
imgList: {
handler(value) {
if (!_.isEmpty(value)) {
this.$nextTick(() => {
this.loadImg();
});
}
},
deep: true,
immediate: true
}
},
destroyed() {
this.clearTimer();
},
methods: {
loadImg() {
const container = this.$refs.imgAnimation;
// 判断图片是否全部加载完毕
this.promiseList = this.imgList.map((img, index) => {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = img;
// 给每张图片配置唯一id,确保循环的顺序正确
image.id = img;
image.className = this.animationClass;
image.style.visibility = 'hidden';
image.onload = () => {
container.appendChild(image);
resolve(image);
};
image.onerror = () => reject(new Error(img + ' load error'));
});
});
Promise.all(this.promiseList).then(() => {
// 图片加载完开启定时器
this.startTimer();
}).catch(err => {
console.log(err);
});
},
startTimer() {
this.$nextTick(() => {
if (this.timer) {
this.clearTimer();
}
let i = 0;
// 循环通过id展示图片,为了使图片的播放顺序正确
// 如果使用this.$refs.imgAnimation.childNodes循环,可能会导致图片播放顺序错位问题
this.timer = setInterval(() => {
this.isAnimation = true;
document.getElementById(this.imgList[i]).style.visibility = 'hidden';
(i >= this.promiseList.length - 1) ? (i = 0) : (i++);
document.getElementById(this.imgList[i]).style.visibility = 'visible';
}, this.timeRange);
});
},
// 清空定时器
clearTimer() {
this.isAnimation = false;
_.forEach(this.imgList, (i, index) => {
document.getElementById(this.imgList[i]).style.visibility = 'hidden';
});
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = null;
}
}
};
</script>
<style lang="scss" scoped>
/deep/.sphere-animation {
z-index: -2;
position: absolute;
max-width: 100%;
max-height: 100%;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.img-animation{
width: 100%;
height: 100%;
}
</style>
组建使用:
<template>
<img-animation :img-list="sphereList"/>
</template>
<script>
import ImgAnimation from '@/components/ImgAnimation';
const sphereContext = require.context('@/assets/video/sphere', false, /.png$/); // 引用文件
export default {
name: 'SphereAnimation',
components: {
ImgAnimation
},
data() {
return {
sphereList: sphereContext.keys().map(sphereContext)
};
}
};
</script>