iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >vue3实现数字滚动特效实例详解
  • 244
分享到

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

2024-04-02 19:04:59 244人浏览 独家记忆
摘要

目录前言思路文件目录使用示例入口文件index.jsmain.js使用requestAnimationFrame.js思路完整代码:CountTo.Vue组件思路总结前言 vue3不

前言

vue3不支持vue-count-to插件,无法使用vue-count-to实现数字动效,数字自动分割,vue-count-to主要针对vue2使用,vue3按照会报错: TypeError: Cannot read properties of undefined (reading '_c') 的错误信息。这个时候我们只能自己封装一个CountTo组件实现数字动效。先来看效果图:

思路

使用Vue.component定义公共组件,使用window.requestAnimationFrame(首选,次选setTimeout)来循环数字动画,window.cancelAnimationFrame取消数字动画效果,封装一个requestAnimationFrame.js公共文件,CountTo.vue组件,入口导出文件index.js。

文件目录

使用示例

<CountTo
 :start="0" // 从数字多少开始
 :end="endCount" // 到数字多少结束
 :autoPlay="true" // 自动播放
 :duration="3000" // 过渡时间
 prefix="¥"   // 前缀符号
 suffix="rmb" // 后缀符号
 />

入口文件index.js

const UILib = {
  install(Vue) {
    Vue.component('CountTo', CountTo)
  }
}
export default UILib

main.js使用

import CountTo from './components/count-to/index';
app.use(CountTo)

requestAnimationFrame.js思路

  • 先判断是不是浏览器还是其他环境
  • 如果是浏览器判断浏览器内核类型
  • 如果浏览器不支持requestAnimationFrame,cancelAnimationFrame方法,改写setTimeout定时器
  • 导出两个方法 requestAnimationFrame, cancelAnimationFrame

各个浏览器前缀:let prefixes =  'WEBkit moz ms o';

判断是不是浏览器:let isServe = typeof window == 'undefined';

增加各个浏览器前缀:

let prefix;
let requestAnimationFrame;
let cancelAnimationFrame;
// 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式
    for (let i = 0; i < prefixes.length; i++) {
        if (requestAnimationFrame && cancelAnimationFrame) { break }
        prefix = prefixes[i]
        requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']
        cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']
    }
  //不支持使用setTimeout方式替换:模拟60帧的效果
  // 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
    if (!requestAnimationFrame || !cancelAnimationFrame) {
        requestAnimationFrame = function (callback) {
            const currTime = new Date().getTime()
            // 为了使setTimteout的尽可能的接近每秒60帧的效果
            const timeToCall = Math.max(0, 16 - (currTime - lastTime))
            const id = window.setTimeout(() => {
                callback(currTime + timeToCall)
            }, timeToCall)
            lastTime = currTime + timeToCall
            return id
        }
        cancelAnimationFrame = function (id) {
            window.clearTimeout(id)
        }
    }

完整代码:

requestAnimationFrame.js

let lastTime = 0
const prefixes = 'webkit moz ms o'.split(' ') // 各浏览器前缀
let requestAnimationFrame
let cancelAnimationFrame
// 判断是否是服务器环境
const isServer = typeof window === 'undefined'
if (isServer) {
    requestAnimationFrame = function () {
        return
    }
    cancelAnimationFrame = function () {
        return
    }
} else {
    requestAnimationFrame = window.requestAnimationFrame
    cancelAnimationFrame = window.cancelAnimationFrame
    let prefix
    // 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式
    for (let i = 0; i < prefixes.length; i++) {
        if (requestAnimationFrame && cancelAnimationFrame) { break }
        prefix = prefixes[i]
        requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']
        cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']
    }
    // 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
    if (!requestAnimationFrame || !cancelAnimationFrame) {
        requestAnimationFrame = function (callback) {
            const currTime = new Date().getTime()
            // 为了使setTimteout的尽可能的接近每秒60帧的效果
            const timeToCall = Math.max(0, 16 - (currTime - lastTime))
            const id = window.setTimeout(() => {
                callback(currTime + timeToCall)
            }, timeToCall)
            lastTime = currTime + timeToCall
            return id
        }
        cancelAnimationFrame = function (id) {
            window.clearTimeout(id)
        }
    }
}
export { requestAnimationFrame, cancelAnimationFrame }

CountTo.vue组件思路

首先引入requestAnimationFrame.js,使用requestAnimationFrame方法接受count函数,还需要格式化数字,进行正则表达式转换,返回我们想要的数据格式。

引入 import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame.js'

需要接受的参数:

const props = defineProps({
  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;
    }
  }
})

启动数字动效

const startCount = () => {
  state.localStart = props.start
  state.startTime = null
  state.localDuration = props.duration
  state.paused = false
  state.rAF = requestAnimationFrame(count)
}

核心函数,对数字进行转动

  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 {
    emits('callback')
  }
}
// 格式化数据,返回想要展示的数据格式
const formatNumber = (val) => {
  val = val.toFixed(props.default)
  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
}

取消动效

// 组件销毁时取消动画
onUnmounted(() => {
  cancelAnimationFrame(state.rAF)
})

完整代码

<template>
  {{ state.displayValue }}
</template>
<script setup>  // vue3.2新的语法糖, 编写代码更加简洁高效
import { onMounted, onUnmounted, Reactive } from "@vue/runtime-core";
import { watch, computed } from 'vue';
import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame.js'
// 定义父组件传递的参数
const props = defineProps({
  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;
    }
  }
})
const isNumber = (val) => {
  return !isNaN(parseFloat(val))
}
// 格式化数据,返回想要展示的数据格式
const formatNumber = (val) => {
  val = val.toFixed(props.default)
  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
}
// 相当于vue2中的data中所定义的变量部分
const state = reactive({
  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 emits = defineEmits(['onMountedcallback', 'callback'])
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()
  }
  emits('onMountedcallback')
})
// 暂停计数
const pause = () => {
  cancelAnimationFrame(state.rAF)
}
// 恢复计数
const resume = () => {
  state.startTime = null
  state.localDuration = +state.remaining
  state.localStart = +state.printVal
  requestAnimationFrame(count)
}
const pauseResume = () => {
  if (state.paused) {
    resume()
    state.paused = false
  } else {
    pause()
    state.paused = true
  }
}
const reset = () => {
  state.startTime = null
  cancelAnimationFrame(state.rAF)
  state.displayValue = formatNumber(props.start)
}
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 {
    emits('callback')
  }
}
// 组件销毁时取消动画
onUnmounted(() => {
  cancelAnimationFrame(state.rAF)
})
</script>

总结

自己封装数字动态效果需要注意各个浏览器直接的差异,手动pollyfill,暴露出去的props参数需要有默认值,数据的格式化可以才有正则表达式的方式,组件的驱动必须是数据变化,根据数据来驱动页面渲染,防止页面出现卡顿,不要强行操作dom,引入的组件可以全局配置,后续组件可以服用

demo演示

后续的线上demo演示会放在 demo演示 

以上就是vue3实现数字滚动特效实例详解的详细内容,更多关于vue3数字滚动的资料请关注编程网其它相关文章!

--结束END--

本文标题: vue3实现数字滚动特效实例详解

本文链接: https://www.lsjlt.com/news/167087.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • vue3实现数字滚动特效实例详解
    目录前言思路文件目录使用示例入口文件index.jsmain.js使用requestAnimationFrame.js思路完整代码:CountTo.vue组件思路总结前言 vue3不...
    99+
    2024-04-02
  • vue3数据可视化实现数字滚动特效代码
    目录前言思路文件目录使用示例入口文件index.jsmain.js使用requestAnimationFrame.js思路完整代码:CountTo.vue组件思路总结前言 vue3不...
    99+
    2024-04-02
  • jquery实现楼层滚动特效
    本文实例为大家分享了jquery实现楼层滚动的具体代码,供大家参考,具体内容如下 效果图 html <div id="floorNav">     <ul>...
    99+
    2024-04-02
  • JavaScript实现楼梯滚动特效(jQuery实现)
    想必大家都用过JD,在它的首页里面有个很常见的特性:就是 楼梯特效 。 对于程序员的我们,可以说是万物皆可盘。那么,我们就来盘一下它。 先上要实现的效果图: 效果功能描述:当点击右...
    99+
    2024-04-02
  • React实现数字滚动组件numbers-scroll的示例详解
    目录一、设计原理二、实现方式三、使用方式四、参数说明数字滚动组件,也可以叫数字轮播组件,这个名字一听就是非常普通常见的组件,第一反应就是想找找网上大佬的东西顶礼膜拜一下,这一搜,还真...
    99+
    2023-03-10
    React数字滚动组件 React数字滚动 React滚动
  • Vue3基于countUp.js实现数字滚动的插件
    目录countUp 简介countUp 组件封装文末countUp 简介 CountUp.js 是一种无依赖项的轻量级 JavaScript 类,可用于快速创建以更有趣的方式显示数字...
    99+
    2023-05-17
    Vue3 countUp.js数字滚动 Vue3 数字滚动
  • jquery数字滚动效果怎么实现
    您可以使用jQuery的.animate()方法来实现数字滚动效果。首先,您需要一个HTML元素来显示数字。例如,一个div元素:`...
    99+
    2023-08-09
    jquery
  • JavaScript实现余额数字滚动效果
    目录1.实现背景2.实现思路3.实现过程1.实现背景 上周在一个完成任务领取红包的活动需求中,需要实现一个用户点击按钮弹出领取红包弹窗后,在关 闭弹窗返回原来的页面时,页面余额数字...
    99+
    2024-04-02
  • python数字滚动效果怎么实现
    要实现数字滚动效果,可以使用Python的Tkinter库来创建一个简单的窗口应用程序。以下是一个示例代码,演示如何实现数字滚动效果...
    99+
    2024-03-01
    python
  • vue实现文字滚动效果
    本文实例为大家分享了vue实现文字滚动效果的具体代码,供大家参考,具体内容如下 项目需求:系统公告,要从右忘左循环播放的牛皮广告效果。 实现: 方案一:使用定时器和CSS3的过渡属性...
    99+
    2024-04-02
  • vue3实现CSS无限无缝滚动效果
    本文实例为大家分享了vue3实现CSS无限无缝滚动效果的具体代码,供大家参考,具体内容如下 template 双层div嵌套,进行隐藏滚动显示 <div class="li...
    99+
    2024-04-02
  • 使用javascript怎么实现一个文字滚动特效
    这篇文章将为大家详细讲解有关使用javascript怎么实现一个文字滚动特效,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。JavaScript是什么JavaScript是一种直译式的脚本语言...
    99+
    2023-06-14
  • 原生JS实现目录滚动特效
    分享一个用原生JS实现的文字滚动效果,这种效果通常用在网页中一些局部展示信息,如新闻、动态、充值记录等,效果如下: 实现代码如下: <!DOCTYPE html> ...
    99+
    2024-04-02
  • jquery怎么实现楼层滚动特效
    这篇“jquery怎么实现楼层滚动特效”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“jquery怎么实现楼层滚动特效”文章吧...
    99+
    2023-06-30
  • Qt实现字幕滚动效果的示例代码
    目录一、项目介绍二、项目基本配置三、UI界面设计四、主程序实现4.1 widget.h头文件4.2 widget.cpp源文件五、效果演示一、项目介绍 利用QTimer实现字幕滚动功...
    99+
    2024-04-02
  • Unity实现卡片循环滚动效果的示例详解
    目录简介定义卡片的摆放规则调整卡片的层级关系调整卡片的尺寸大小动态调整位置、层级和大小移动动画按钮事件简介 功能需求如图所示,点击下一个按钮,所有卡片向右滚动,其中最后一张需要变更为...
    99+
    2022-12-09
    Unity卡片循环滚动效果 Unity循环滚动效果 Unity 滚动
  • js实现文字滚动的效果
    本文实例为大家分享了js实现文字滚动的效果的具体代码,供大家参考,具体内容如下 在之前小编已经和大家介绍了一些常用的js动画效果,在此,和大家介绍一种可能不太常用的动画效果。该动画效...
    99+
    2024-04-02
  • JavaScript如何实现余额数字滚动效果
    今天就跟大家聊聊有关JavaScript如何实现余额数字滚动效果,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1.实现背景上周在一个完成任务领取红包的活动需求中,需要实现一个用户点击...
    99+
    2023-06-22
  • JavaScript怎么实现余额数字滚动效果
    这篇文章主要介绍“JavaScript怎么实现余额数字滚动效果”,在日常操作中,相信很多人在JavaScript怎么实现余额数字滚动效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大...
    99+
    2024-04-02
  • 基于Vue3实现列表虚拟滚动效果
    目录前言完成效果思路和需要解决的问题vue3+setup 写的组件使用组件前言 近期在做一个网页播放器项目中,用到很多需要展示歌单的列表 一个歌单动辄千百首歌曲,页面中的元素太多导致...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作