探索Vue:数字翻滚动画的实现与优化

在前端开发中,动画效果总能为用户界面增添一抹生动与趣味。今天,我们将深入剖析一个基于Vue 3实现的数字翻滚动画组件,从代码结构到功能实现,再到性能优化,一步步揭开它的神秘面纱。

组件概述

这个Vue组件名为NumberFlipAnimation,旨在通过数字翻滚的效果来展示一个变化的数值。无论是整数还是小数,都能以翻滚动画的形式呈现,为数据展示增添一抹动感。

代码结构
  1. html和js部分
<template>
  <div ref="numberAnimationRef" class="count-flop" :key="compKey">
    <!--  -->
    <div :class="item !== '.' ? 'count-flop-box' : 'count-flop-point'" v-for="(item, index) in value" :key="index">
      <div v-if="item !== '.'" class="count-flop-content" :class="['rolling_' + item]">
        <div v-for="(item2, index2) in numberList" :key="index2" class="count-flop-num">
          {{ item2 }}
        </div>
      </div>
      <div v-else class="count-flop-content">.</div>
    </div>
    <!--  -->
    <div v-if="suffix" class="count-flop-unit">{{ suffix }}</div>
  </div>
</template>
<script setup>
  import { ref, defineProps, onMounted, onBeforeUnmount } from 'vue';

  const props = defineProps({
    val: {
      type: [Number, String],
      default: 0,
    },
    
    suffix: {
      type: String,
      default: '',
    },
  });

  // Data
  const value = ref([]);
  const compKey = ref(0);
  =const numberList = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
  const numberAnimationRef = ref(null);
  // Watcher
  const updateValue = () => {
    value.value = props.val.toString().split('');
    compKey.value += 1;
  };

  // Intersection Observer
  const observer = ref(null);

  onMounted(() => {
    observer.value = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          updateValue();
          observer.value.unobserve(entry.target);
        }
      });
    });

    if (numberAnimationRef.value) {
      observer.value.observe(numberAnimationRef.value);
    }
  });
  onBeforeUnmount(() => {
    if (observer.value && numberAnimationRef.value) {
      console.log('Unobserving element');
      compKey.value = 0;
      observer.value.unobserve(numberAnimationRef.value);
    }
  });
</script>

2. css动画部分

<style lang="stylus" scoped>
  .count-flop {
    display: inline-block;
    /* 可更改 */
    height: 4.3vw;
    font-size: 3.5vw;
    font-weight: 500;
    line-height: 4.3vw;
    color: #fff;
  }

  .count-flop > div {
    position: relative;
    display: inline-block;
    overflow: hidden;
    height: 100%;
  }

  .count-flop-box {
    /* 可更改 */
    //margin-right: 5px;
    width: 2vw;
    //border: 1px solid rgba(72, 152, 241, 0.3);
    /*
    这里的高度要比上面height的少2px
    */
    line-height: 4.3vw;
    //border-radius: 6px;
  }

  .count-flop-point {
    /* 可更改 */
    margin-right: 5px;
    width: 10px;
  }

  .count-flop-content {
    text-align: center;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    animation-fill-mode: forwards !important;
  }
  .rolling_0 {
    animation: rolling_0 0.7s ease;
  }

  @keyframes rolling_0 {
    from {
      transform: translateY(-90%);
    }
    to {
      transform: translateY(0);
    }
  }

  .rolling_1 {
    animation: rolling_1 0.7s ease;
  }

  @keyframes rolling_1 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-10%);
    }
  }

  .rolling_2 {
    animation: rolling_2 0.9s ease;
  }

  @keyframes rolling_2 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-20%);
    }
  }

  .rolling_3 {
    animation: rolling_3 1s ease;
  }

  @keyframes rolling_3 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-30%);
    }
  }

  .rolling_4 {
    animation: rolling_4 1.2s ease;
  }

  @keyframes rolling_4 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-40%);
    }
  }

  .rolling_5 {
    animation: rolling_5 1s ease;
  }

  @keyframes rolling_5 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-50%);
    }
  }

  .rolling_6 {
    animation: rolling_6 1.5s ease;
  }

  @keyframes rolling_6 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-60%);
    }
  }

  .rolling_7 {
    animation: rolling_7 2.1s ease;
  }

  @keyframes rolling_7 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-70%);
    }
  }

  .rolling_8 {
    animation: rolling_8 0.8s ease;
  }

  @keyframes rolling_8 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-80%);
    }
  }

  .rolling_9 {
    animation: rolling_9 1.6s ease;
  }

  @keyframes rolling_9 {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(-90%);
    }
  }

  /* 其他 rolling_X 动画略 */
</style>
代码结构解析
  1. 模板部分‌ (<template>):

    • 组件的HTML结构由外层的<div>容器包裹,内部通过v-for指令遍历value数组,为每个数字或小数点创建一个独立的<div>
    • 使用:key属性确保列表渲染的高效性。
    • 通过动态类名rolling_加上数字值,为每个数字应用特定的翻滚动画。
    • 若存在后缀(如单位),则在数字后显示。
  2. 脚本部分‌ (<script setup>):

    • 使用Vue 3的setup语法,定义组件的propsref和响应式数据。
    • props包括val(要展示的数字或字符串)、numberList(数字列表,用于翻滚动画)和suffix(后缀,如单位)。
    • updateValue函数用于将传入的val转换为字符串数组,并更新compKey以触发重新渲染。
    • 利用IntersectionObserver实现懒加载动画,当组件进入视口时才执行动画,提高性能。
    • 在组件卸载时,通过onBeforeUnmount钩子清除观察器,避免内存泄漏。
  3. 样式部分‌ (<style lang="stylus" scoped>):

    • 使用Stylus预处理器编写组件的样式,确保样式的作用域仅限于当前组件。
    • 定义了.count-flop及其子元素的样式,包括大小、布局和动画效果。
功能实现细节
  • 数字翻滚动画‌:通过动态类名和CSS动画实现。每个数字都有一套对应的翻滚动画,当value更新时,通过更改类名触发动画。
  • 懒加载优化‌:利用IntersectionObserver监听组件是否进入视口,只有在用户即将看到组件时才触发动画,减少不必要的计算和渲染。
  • 响应式更新‌:通过refwatch机制,确保当props变化时,组件能够响应并更新显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值