vue实现一张竖着的图片动画帧组件

0 阅读1分钟

vue实现一张竖着的图片动画帧组件

  • 传入图片地址、总帧数、播放时间(秒)

组件代码

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'

// 父传子接收参数
const props = withDefaults(defineProps<{
    url: string,// 图片地址
    totalFrames: number,// 总帧数
    animationDuration: number// 持续时间秒
}>(), {
    url: () => (''),
    animationDuration: () => (1)
})
let requestAnimationFrameId: number;// 动画帧id
let currentFrame: number = 1;// 当前帧

const imgRef = ref<HTMLImageElement>();// 图片标签
let last: number = Date.now();// 上次执行的时间
const translateY = ref(0);// 移动的距离
// 动画
const animation = () => {
    requestAnimationFrameId = requestAnimationFrame(animation);
    const now = Date.now();// 当前时间
    const delta = now - last;// 走过的时间
    const interval = props.animationDuration * 1000 / props.totalFrames;// 每帧多少毫秒
    if (delta > interval) {
        /* 
            这里不能简单last = now,否则出现细微时间差问题。例如fps= 10,每帧100ms,
            而现在每16ms(60fps)执行一次draw。16*7=112>100,需要7次才实际绘制一帧。这个情况下,
            实际10帧需要 112*10=1120ms>1000ms 才绘制完成。
            当 now - (delta % interval) 时,会将时间修正 delta % interval = 12ms,
            实际10帧需要 112+(112-12)*9=1012ms 绘制完成。
        */
        last = now - (delta % interval);
        if (imgRef.value) {
            const h = imgRef.value.clientHeight;
            translateY.value = -(h / props.totalFrames) * currentFrame
        }
        currentFrame++;
        if (currentFrame === props.totalFrames) {
            currentFrame = 0;// 重新开始
        }
    }
};

onMounted(() => requestAnimationFrameId = requestAnimationFrame(animation));
onBeforeUnmount(() => cancelAnimationFrame(requestAnimationFrameId))
</script>

<template>
    <div class="image">
        <img ref="imgRef" :src="url" :style="{ 'transform': `translate(0, ${translateY}px)` }" />
    </div>
</template>

<style scoped lang="scss">
.image {
    width: 50px;
    height: 50px;
    display: inline-block;
    overflow: hidden;

    img {
        object-fit: cover;
        height: auto;
        max-width: 100%;
    }
}
</style>

使用

  • 10帧图片
<FrameImage style="width:86px;height:86px" :url="{地址}" :totalFrames="10" :animationDuration="1.5"/>

trends10002.png

  • 25帧图片
    <FrameImage style="width:80px;height:80px" :url="{地址}" :totalFrames="25" :animationDuration="2"/>

trends25002.png