前端图片懒加载,使用标签实现序列帧动画

754 阅读1分钟

在前端实现序列帧动画的时候,可以批量引入图片,并使用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>