Vue3+TS实现数字滚动效果CountTo组件

目录
  • 前言
  • 思考
  • 实践
    • 定义参数
    • 定义一个开始函数
    • 核心方法
  • 配置项
  • 功能
  • 组件

前言

最近开发有个需求需要酷炫的文字滚动效果,发现vue2版本的CountTo组件不适用与Vue3,没有轮子咋办,那咱造一个呗。其实大多数版本更替导致公共组件不可用,最简单的做法就是在原版本的基础上进行修改调整,总体来讲花费的时间成本以及精力成本最低。

思考

先看下效果,明确需求,然后开始搬砖。

明确基础功能

  • 有开始值、结束值以及动画持续时间
  • 默认分隔符、自动播放

扩展功能

  • 自动播放可配置
  • 分隔符可自定义
  • 前、后缀
  • 动画配置项

实践

定义参数

const props = {
  start: {
    type: Number,
    required: false,
    default: 0
  },
  end: {
    type: Number,
    required: false,
    default: 0
  },
  duration: {
    type: Number,
    required: false,
    default: 5000
  },
  autoPlay: {
    type: Boolean,
    required: false,
    default: true
  },
  decimals: {
    type: Number,
    required: false,
    default: 0,
    validator(value) {
      return value >= 0
    }
  },
  decimal: {
    type: String,
    required: false,
    default: '.'
  },
  separator: {
    type: String,
    required: false,
    default: ','
  },
  prefix: {
    type: String,
    required: false,
    default: ''
  },
  suffix: {
    type: String,
    required: false,
    default: ''
  },
  useEasing: {
    type: Boolean,
    required: false,
    default: true
  },
  easingFn: {
    type: Function,
    default(t, b, c, d) {
      return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
    }
  }
}

定义一个开始函数

    // 定义一个计算属性,当开始数字大于结束数字时返回true
    const stopCount = computed(() => {
      return props.start > props.end
    })
    const startCount = () => {
      state.localStart = props.start
      state.startTime = null
      state.localDuration = props.duration
      state.paused = false
      state.rAF = requestAnimationFrame(count)
    }
    watch(() => props.start, () => {
      if (props.autoPlay) {
        startCount()
      }
    })

    watch(() => props.end, () => {
      if (props.autoPlay) {
        startCount()
      }
    })
    // dom挂在完成后执行一些操作
    onMounted(() => {
      if (props.autoPlay) {
        startCount()
      }
      emit('onMountedcallback')
    })
     // 组件销毁时取消动画
    onUnmounted(() => {
      cancelAnimationFrame(state.rAF)
    })

核心方法

    const count = (timestamp) => {
      if (!state.startTime) state.startTime = timestamp
      state.timestamp = timestamp
      const progress = timestamp - state.startTime
      state.remaining = state.localDuration - progress
      // 是否使用速度变化曲线
      if (props.useEasing) {
        if (stopCount.value) {
          state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)
        } else {
          state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)
        }
      } else {
        if (stopCount.value) {
          state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))
        } else {
          state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)
        }
      }
      if (stopCount.value) {
        state.printVal = state.printVal < props.end ? props.end : state.printVal
      } else {
        state.printVal = state.printVal > props.end ? props.end : state.printVal
      }

      state.displayValue = formatNumber(state.printVal)
      if (progress < state.localDuration) {
        state.rAF = requestAnimationFrame(count)
      } else {
        emit('callback')
      }
    }

配置项

属性 描述 类型 默认值
startVal 开始值 Number 0
endVal 结束值 Number 0
duration 持续时间 Number 0
autoplay 自动播放 Boolean true
decimals 要显示的小数位数 Number 0
decimal 十进制分割 String ,
separator 分隔符 String ,
prefix 前缀 String ''
suffix 后缀 String ''
useEasing 使用缓和功能 Boolean true
easingFn 缓和回调 Function -

注:当autoplay:true时,它将在startVal或endVal更改时自动启动

功能

函数名 描述
mountedCallback 挂载以后返回回调
start 开始计数
pause 暂停计数
reset 重置countTo

组件

组件同步在git组件库了https://github.com/kinoaa/kinoaa-components/tree/main/countTo

import {
  defineComponent, reactive, computed, onMounted, watch, onUnmounted
} from 'vue'
const props = {
  start: {
    type: Number,
    required: false,
    default: 0
  },
  end: {
    type: Number,
    required: false,
    default: 2022
  },
  duration: {
    type: Number,
    required: false,
    default: 5000
  },
  autoPlay: {
    type: Boolean,
    required: false,
    default: true
  },
  decimals: {
    type: Number,
    required: false,
    default: 0,
    validator(value) {
      return value >= 0
    }
  },
  decimal: {
    type: String,
    required: false,
    default: '.'
  },
  separator: {
    type: String,
    required: false,
    default: ','
  },
  prefix: {
    type: String,
    required: false,
    default: ''
  },
  suffix: {
    type: String,
    required: false,
    default: ''
  },
  useEasing: {
    type: Boolean,
    required: false,
    default: true
  },
  easingFn: {
    type: Function,
    default(t, b, c, d) {
      return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
    }
  }
}
export default defineComponent({
  name: 'CountTo',
  props: props,
  emits: ['onMountedcallback', 'callback'],
  setup(props, {emit}) {
    const isNumber = (val) => {
      return !isNaN(parseFloat(val))
    }
    // 格式化数据,返回想要展示的数据格式
    const formatNumber = (val) => {
      val = val.toFixed(props.start)
      val += ''
      const x = val.split('.')
      let x1 = x[0]
      const x2 = x.length > 1 ? props.decimal + x[1] : ''
      const rgx = /(\d+)(\d{3})/
      if (props.separator && !isNumber(props.separator)) {
        while (rgx.test(x1)) {
          x1 = x1.replace(rgx, '$1' + props.separator + '$2')
        }
      }
      return props.prefix + x1 + x2 + props.suffix
    }
    const state = reactive<{
      localStart: number
      displayValue: number|string
      printVal: any
      paused: boolean
      localDuration: any
      startTime: any
      timestamp: any
      remaining: any
      rAF: any
    }>({
      localStart: props.start,
      displayValue: formatNumber(props.start),
      printVal: null,
      paused: false,
      localDuration: props.duration,
      startTime: null,
      timestamp: null,
      remaining: null,
      rAF: null
    })
    // 定义一个计算属性,当开始数字大于结束数字时返回true
    const stopCount = computed(() => {
      return props.start > props.end
    })
    const startCount = () => {
      state.localStart = props.start
      state.startTime = null
      state.localDuration = props.duration
      state.paused = false
      state.rAF = requestAnimationFrame(count)
    }

    watch(() => props.start, () => {
      if (props.autoPlay) {
        startCount()
      }
    })

    watch(() => props.end, () => {
      if (props.autoPlay) {
        startCount()
      }
    })
    // dom挂在完成后执行一些操作
    onMounted(() => {
      if (props.autoPlay) {
        startCount()
      }
      emit('onMountedcallback')
    })
    const count = (timestamp) => {
      if (!state.startTime) state.startTime = timestamp
      state.timestamp = timestamp
      const progress = timestamp - state.startTime
      state.remaining = state.localDuration - progress
      // 是否使用速度变化曲线
      if (props.useEasing) {
        if (stopCount.value) {
          state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)
        } else {
          state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)
        }
      } else {
        if (stopCount.value) {
          state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))
        } else {
          state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)
        }
      }
      if (stopCount.value) {
        state.printVal = state.printVal < props.end ? props.end : state.printVal
      } else {
        state.printVal = state.printVal > props.end ? props.end : state.printVal
      }

      state.displayValue = formatNumber(state.printVal)
      if (progress < state.localDuration) {
        state.rAF = requestAnimationFrame(count)
      } else {
        emit('callback')
      }
    }
    // 组件销毁时取消动画
    onUnmounted(() => {
      cancelAnimationFrame(state.rAF)
    })
    return () => (
      state.displayValue
    )
  }
})

到此这篇关于Vue3+TS实现数字滚动效果CountTo组件的文章就介绍到这了,更多相关Vue3数字滚动效果内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue3基于countUp.js实现数字滚动的插件

    目录 countUp 简介 countUp 组件封装 文末 countUp 简介 CountUp.js 是一种无依赖项的轻量级 JavaScript 类,可用于快速创建以更有趣的方式显示数字数据的动画.CountUp 可以在两个方向上进行计数,具体取决于传递的开始和结束值. 虽然现在市面上基于 countUp.js 二次封装的Vue组件不在少数, 但我个人是不太喜欢使用这些第三方封装的,因为第三方组件的更新频率很难保证,也许作者只是一时兴起封装上传了, 并未打算继续维护,如果使用了 等于后续根本

  • vue3实现数字滚动特效实例详解

    目录 前言 思路 文件目录 使用示例 入口文件index.js main.js使用 requestAnimationFrame.js思路 完整代码: CountTo.vue组件思路 总结 前言 vue3不支持vue-count-to插件,无法使用vue-count-to实现数字动效,数字自动分割,vue-count-to主要针对vue2使用,vue3按照会报错: TypeError: Cannot read properties of undefined (reading '_c') 的错误信息

  • vue3数据可视化实现数字滚动特效代码

    目录 前言 思路 文件目录 使用示例 入口文件index.js main.js使用 requestAnimationFrame.js思路 完整代码: CountTo.vue组件思路 总结 前言 vue3不支持vue-count-to插件,无法使用vue-count-to实现数字动效,数字自动分割,vue-count-to主要针对vue2使用,vue3按照会报错:TypeError: Cannot read properties of undefined (reading '_c')的错误信息.这

  • countUp.js实现数字滚动效果

    本文实例为大家分享了countUp.js数字滚动效果展示的具体代码,供大家参考,具体内容如下 1. 概述 1.1 说明 在项目过程中,有时候需要动态的去展示一些数据的加载状态,如一个数字为10000,需要5秒时间滚动加载完成.此时使用countup.js就能够很方便的处理此类功能问题. 1.2 countup.js countup.js是一个无依赖性.轻量级的javascript类,可用于快速创建动画,以更有趣的方式显示数字/数据.详见countup.js 1.3 countup.js使用 np

  • JavaScript实现余额数字滚动效果

    目录 1.实现背景 2.实现思路 3.实现过程 1.实现背景 上周在一个完成任务领取红包的活动需求中,需要实现一个用户点击按钮弹出领取红包弹窗后,在关 闭弹窗返回原来的页面时,页面余额数字部分要展示一个每一位数字滚动后的效果. 因为之前没做过这样的效果,一开始也不知道要如何实现,本来想在GitHub上找一下相关的库,看到一个最高star的库,但发现它是依赖jQuery的,而且不可以npm包引入.感觉就很没有必要,本来项目是react框架的,就是要尽量少的操作DOM,为了解决这个滚动就要引入jQu

  • vue实现数字滚动效果

    本文实例为大家分享了vue实现数字滚动的具体代码,供大家参考,具体内容如下 <template> <div class="num-block"> <div class="num-block_show"> <div class="num-block_numbers" :class="{'ellipsis': !isNum(item)}" v-for="(item, key) i

  • 基于Vue3实现列表虚拟滚动效果

    目录 前言 完成效果 思路和需要解决的问题 vue3+setup 写的组件 使用组件 前言 近期在做一个网页播放器项目中,用到很多需要展示歌单的列表 一个歌单动辄千百首歌曲,页面中的元素太多导致热重载的时候 chrome 直接崩了 于是无限滚动列表提上日程 写的有点乱,也是第一次用 typescript 写项目,先记录一下 完成效果 思路和需要解决的问题 与懒加载不同,虚拟滚动需要一次性获取所有数据,但是只显示屏幕可见范围内的数据 要做到这些我需要知道: 一行的高度 屏幕范围内能显示的行数 列表

  • 基于jQuery实现数字滚动效果

    滚动方向:上下 源代码下载 以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

  • Vue transition组件简单实现数字滚动

    目录 实现效果 Scrip 布局 动画 逻辑控制 总结 实现效果 Scrip <template> <button @click="addCount">点我滚动数字</button> <div class="roll-container"> <transition name="roll"> <div class="roll-number" :key="

  • Vue.js实现大屏数字滚动翻转效果

    大屏数字滚动翻转效果来源于最近工作中element后台管理页面一张大屏的UI图,该UI图上有一个模块需要有数字往上翻动的效果,以下是最终实现的效果: 整体思路: 在实现此效果之前,我们先来捋一下思路,用思维导图来设计一下我们的实现步骤,如下: 你可以审查元素,下载数字背景图片,复制图片地址,或者使用其他背景图片.背景颜色 有了以上的设计流程,我们先来简单实现一下: // CSS代码 <style> .box-item { position: relative; display: inline-

  • react 不用插件实现数字滚动的效果示例

    突然要实现个数字滚动效果,网上一搜,一大堆都是用组件的.我只是想实现个简单的效果而已,决定还是自己搞搞吧. 先来看看效果吧 也不多说了,实现起来不难,但是有点细节问题需要自己好好琢磨一下 大概思路, 1.一开始为0,获取第一次数据,记录下来 2.和前一次数据进行对比 3.然后transform 4.最后收工 好了,附上代码. export default class Number extends React.Component { constructor(props) { super(props

  • React实现数字滚动组件numbers-scroll的示例详解

    目录 一.设计原理 二.实现方式 三.使用方式 四.参数说明 数字滚动组件,也可以叫数字轮播组件,这个名字一听就是非常普通常见的组件,第一反应就是想找找网上大佬的东西顶礼膜拜一下,这一搜,还真是没找到趁手的╮(╯▽╰)╭. 最近接了大屏的需求,数字滚动肯定是免不了的,所以开始撸袖子,造轮子了( numbers-scroll ). 首先给大家看下轮子的效果吧: 一.设计原理 如果要做到数字滚动效果,就一定要让数字有从下往上移动的感觉.如果只是纯粹的数字变化,显示出来的效果就会比较普通了,没有什么视

  • 微信小程序实现数字滚动动画

    本文实例为大家分享了微信小程序实现数字滚动效果的具体代码,供大家参考,具体内容如下 效果图 实现思路 1.为了实现数字的无限滚动效果,每个数字框的内部,其实包含了两组0~9的view,每个View的高度都一样2.数字框内使用绝对定位,通过调整top位置,显示出指定的数字3.使用transtion动画,top发生变化时就会滚动,然后通过指定动画的delay.duration,使数字之间延时动画 项目代码 js文件 // components/scroll-number/index.js Compo

随机推荐