<tbody id="86a2i"></tbody>


<dd id="86a2i"></dd>
<progress id="86a2i"><track id="86a2i"></track></progress>

<dd id="86a2i"></dd>
<em id="86a2i"><ruby id="86a2i"><u id="86a2i"></u></ruby></em>

    <dd id="86a2i"></dd>

    前言

    最近開發有個需求需要酷炫的文字滾動效果,發現vue2版本的CountTo組件不適用與Vue3,沒有*咋辦,那咱造一個唄。其實大多數版本更替導致公共組件不可用,最簡單的做法就是在原版本的基礎上進行修改調整,總體來講花費的時間成本以及精力成本最低。

    思考

    Vue3+TS實現數字滾動效果CountTo組件

    先看下效果,明確需求,然后開始搬磚。

    明確基礎功能

    • 有開始值、結束值以及動畫持續時間
    • 默認分隔符、自動播放

    擴展功能

    • 自動播放可配置
    • 分隔符可自定義
    • 前、后綴
    • 動畫配置項

    實踐

    定義參數

    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
        )
      }
    })
    原文地址:https://juejin.cn/post/7166484108636356621

    相關文章:

    免费一级a片在线播放视频|亚洲娇小性XXXX色|曰本无码毛片道毛片视频清|亚洲一级a片视频免费观看
    <tbody id="86a2i"></tbody>

    
    
    <dd id="86a2i"></dd>
    <progress id="86a2i"><track id="86a2i"></track></progress>

    <dd id="86a2i"></dd>
    <em id="86a2i"><ruby id="86a2i"><u id="86a2i"></u></ruby></em>

      <dd id="86a2i"></dd>