第三方JS库地址:https://github.com/inorganik/CountUp.js
使用NPM进行安装:
npm install --save countup
根据官方回答,CountUp.js是一个无依赖,轻量级的Javascript类,可用于快速创建以更有趣的方式显示数值数据的动画。
在代码开始之前,先补充几个Vue.js的基础知识:
1.this._uid的使用:每个组件都有一个唯一的id,可以作为this._uid访问。
具体使用场景:组件是要在多个地方多次使用 ,而一个页面上每一个id值都要互不相同。如果id值固定的话 在多次使用这个组件的时候就会出现多个元素的id都是同一个值。
2.计算属性computed
设计它的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
使用官网的例子通俗易懂:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。所以,对于任何复杂逻辑,你都应当使用计算属性。
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div>
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } })
2.1计算属性和方法的区别:
计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值,两者的计算结果完全相同
2.1计算属性和侦听属性(Watch)的区别:
计算属性更适合于通常的场景,如下代码:
<div id="demo">{{ fullName }}</div>
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })
若改为计算属性则可以缩减代码量:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
3.组件的默认值写法。
当组件的属性类型时Object或者Array时,需要使用工厂函数返回'的默认值(注意括号):
props: { yourProp: { type: Object, default: () => ({ param: value, param2: value, }), }, }
props: { arr: { type: Array, default: function () { return [] } } } //ES6写法 props: { arr: { type: Array, default: () => [] } }
4.在Vue中获取并操作dom:
要在mounted中使用,因为只有在执行mounted的时候,vue已经渲染了dom节点,这个时候是可以获取dom节点的,vue中尽量不去操作dom元素,选用ref操作属性获取。
<button ref="btn">获取ref</button>
this.$refs.btn.style.backgroundColor="#ff0000"
5.理解Vue中的nextTick
应用场景:需要在视图更新之后,基于新的视图进行操作。
具体可参考:https://segmentfault.com/a/1190000012861862?utm_source=tag-newest
下面回到正题:
代码目录结构:
./src/components/count-to/count-to.less:
.count-to-number{ color: palevioletred; }
./src/components/count-to/count-to.vue:
<template> <div> <slot name="left"></slot><span ref="number" :class="countClass" :id="eleId"></span><slot name="right"></slot> </div> </template> <script> import CountUp from 'countup' export default { name: 'CountTo', computed: { eleId () { return `count_up_${this._uid}` }, countClass () { return [ 'count-to-number', this.className ] } }, data () { return { counter: {} } }, props: { /** * @description 起始值 */ startVal: { type: Number, default: 0 }, /** * @description 最终值 */ endVal: { type: Number, required: true }, /** * @description 小数点后保留几位小数 */ decimals: { type: Number, default: 0 }, /** * @description 动画延迟开始时间 */ delay: { type: Number, default: 0 }, /** * @description 渐变时长 */ duration: { type: Number, default: 1 }, /** * @description 是否使用变速效果 */ useEasing: { type: Boolean, default: false }, /** * @description 是否使用变速效果 */ useGrouping: { type: Boolean, default: true }, /** * @description 分组符号 */ separator: { type: String, default: ',' }, /** * @description 整数和小数分割符号 */ decimal: { type: String, default: '.' }, className: { type: String, default: '' } }, methods: { getCount () { return this.$refs.number.innerText }, emitEndEvent () { setTimeout(() => { this.$nextTick(() => { this.$emit('on-animation-end', Number(this.getCount())) }) }, this.duration * 1000 + 5) } }, watch: { endVal (newVal, oldVal) { this.counter.update(newVal) this.emitEndEvent() } }, mounted () { this.$nextTick(() => { this.counter = new CountUp(this.eleId, this.startVal, this.endVal, this.decimals, this.duration, { useEasing: this.useEasing, useGrouping: this.useGrouping, separator: this.separator, decimal: this.decimal }) setTimeout(() => { this.counter.start() this.emitEndEvent() }, this.delay) }) } } </script> <style lang="less"> @import './count-to.less'; </style>
./src/components/count-to/index.js:
import CountTo from './count-to.vue' export default CountTo
./src/router/router.js增加对count-to.vue的路由:
{ path: '/count-to', name: 'count_to', component: () => import('@/views/count-to.vue') }
./src/views/count-to.vue:
<template> <div> <count-to ref="countTo" :end-val="endVal" @on-animation-end="handleEnd"> <span slot="left">总金额:</span> <span slot="right">元</span> </count-to> <button @click="getNumber">获取数值</button> <button @click="up">更新值</button> </div> </template> <script> import CountTo from "@/components/count-to"; export default { name: "count_to", components: { CountTo }, data() { return { endVal: 100 }; }, methods: { getNumber() { this.$refs.countTo.getCount(); }, up() { this.endVal += Math.random() * 100; }, handleEnd(endVal) { console.log("end -> ", endVal); } } }; </script>
运行效果: