实现数字滚动计数效果

389 阅读2分钟

数据可视化大屏开发的过程中,需要实现一种滚动数字的效果。
实现代码示例如下:

<template>
  <span>
    {{ value }}
  </span>
</template>

<script>
export default {
  props: {
    startVal: {
      type: Number,
      default: 0,
    },
    endVal: {
      type: Number,
      default: 9999,
    },
    duration: {
      type: Number,
      default: 1000,
    },
    autoplay: {
      type: Boolean,
      default: true,
    },
    decimals: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      localStartVal: 0,
      startTime: null,
      timestamp: null,
      ret: null,
      printVal: 0,
      value: 0,
    };
  },
  mounted() {
    this.start();
  },
  methods: {
    count(timestamp) {
      if (!this.startTime) this.startTime = timestamp;
      this.timestamp = timestamp;
      const progress = timestamp - this.startTime;
      if (this.countDown) {
        this.printVal =
          this.localStartVal -
          (this.localStartVal - this.endVal) * (progress / this.localDuration);
      } else {
        this.printVal =
          this.localStartVal +
          (this.endVal - this.localStartVal) * (progress / this.localDuration);
      }
      if (this.countDown) {
        this.printVal =
          this.printVal < this.endVal ? this.endVal : this.printVal;
      } else {
        this.printVal =
          this.printVal > this.endVal ? this.endVal : this.printVal;
      }
      this.value = this.printVal.toFixed(this.decimals);
      if (progress < this.localDuration) {
        this.ret = requestAnimationFrame(this.count);
      } else {
        // this.$emit('callback');
        return;
      }
    },
    start() {
      this.localStartVal = this.startVal;
      this.startTime = null;
      this.localDuration = this.duration;
      this.paused = false;
      this.ret = requestAnimationFrame(this.count);
    },
  },
  computed: {
    countDown() {
      return this.startVal > this.endVal;
    },
  },
  watch: {
    startVal() {
      if (this.autoplay) {
        this.start();
      }
    },
    endVal() {
      if (this.autoplay) {
        this.start();
      }
    },
  },
};
</script>
属性描述类型默认值
startVal开始值Number0
endVal结束值Number9999
duration动画时长Number1000
autoplay是否自动开启动画Booleantrue
decimals展示小数点后的个数Number0


requestAnimationFrame

是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘。 requestAnimationFrame的优势,在于充分利用显示器的刷新机制,比较节省系统资源。显示器有固定的刷新频率(60Hz或75Hz),也就是说,每秒最多只能重绘60次或75次,requestAnimationFrame的基本思想就是与这个刷新频率保持同步,利用这个刷新频率进行页面重绘。此外,使用这个API,一旦页面不处于浏览器的当前标签,就会自动停止刷新。这就节省了CPU、GPU和电力。

requestAnimationFrame使用一个回调函数作为参数。这个回调函数会在浏览器重绘之前调用。

各个支持requestAnimationFrame的浏览器有些还是自己的私有实现,以及不支持requestAnimationFrame的浏览器需要做兼容

解决兼容性问题

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 }

vue-count-to

vue-count-to是一个没有依赖的轻量级vue组件,可以自行覆盖EasingFn。 可以设置 startVal 和 endVal,它会自动判断计数进行数字渲染。
附上地址www.npmjs.com/package/vue…