刘嘉威
2022-10-17 e4bdfcb0571b51b4d1d55f99df1d1f8ed21c35e3
feat: 增加数字滚动组件 展示时间显示
6个文件已修改
3个文件已添加
219 ■■■■■ 已修改文件
package-lock.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/count-up/count-up.vue 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/count-up/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/scale-screen/scale-screen.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/HomeView.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/header.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.d.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -520,6 +520,11 @@
      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
      "dev": true
    },
    "countup.js": {
      "version": "2.3.2",
      "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.3.2.tgz",
      "integrity": "sha512-dQ7F/CmKGjaO6cDfhtEXwsKVlXIpJ89dFs8PvkaZH9jBVJ2Z8GU4iwG/qP7MgY8qwr+1skbwR6qecWWQLUzB8Q=="
    },
    "cross-spawn": {
      "version": "6.0.5",
      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -544,6 +549,11 @@
      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
      "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
    },
    "dayjs": {
      "version": "1.11.5",
      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz",
      "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA=="
    },
    "de-indent": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
package.json
@@ -10,6 +10,8 @@
  },
  "dependencies": {
    "axios": "^1.1.2",
    "countup.js": "^2.3.2",
    "dayjs": "^1.11.5",
    "echarts": "^5.4.0",
    "mockjs": "^1.1.0",
    "pinia": "^2.0.21",
src/assets/css/main.scss
@@ -3,5 +3,7 @@
    width: 100%;
}
#app{
    .content_wrap{
        color: #d3d6dd;
    }
}
src/components/count-up/count-up.vue
New file
@@ -0,0 +1,146 @@
<script lang="ts">
export type { CountUp as ICountUp, CountUpOptions } from 'countup.js'
export default {
  name: 'CountUp'
}
</script>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { CountUp } from 'countup.js'
import type { CountUpOptions } from 'countup.js'
const props = withDefaults(
  defineProps<{
    // 结束数值
    endVal: number | string
    // 开始数值
    startVal?: number | string
    // 动画时长,单位 s
    duration?: number | string
    // 是否自动计数
    autoplay?: boolean
    // 循环次数,有限次数 / 无限循环
    loop?: boolean | number | string
    // 延时,单位 s
    delay?: number
    // countup 配置项
    options?: CountUpOptions
  }>(),
  {
    startVal: 0,
    duration: 2.5,
    autoplay: true,
    loop: false,
    delay: 0,
    options: undefined
  }
)
const emits = defineEmits<{
  // countup init complete
  (event: 'init', countup: CountUp): void
  // count complete
  (event: 'finished'): void
}>()
let elRef = ref<HTMLElement>()
let countUp = ref<CountUp>()
const initCountUp = () => {
  if (!elRef.value) return
  const startVal = Number(props.startVal)
  const endVal = Number(props.endVal)
  const duration = Number(props.duration)
  countUp.value = new CountUp(elRef.value, endVal, {
    startVal,
    duration,
    ...props.options
  })
  if (countUp.value.error) {
    console.error(countUp.value.error)
    return
  }
  emits('init', countUp.value)
}
const startAnim = (cb?: () => void) => {
  countUp.value?.start(cb)
}
// endVal change & autoplay: true, restart animate
watch(
  () => props.endVal,
  (value) => {
    if (props.autoplay) {
      countUp.value?.update(value)
    }
  }
)
// loop animation
const finished = ref(false)
let loopCount = 0
const loopAnim = () => {
  loopCount++
  startAnim(() => {
    const isTruely = typeof props.loop === 'boolean' && props.loop
    if (isTruely || props.loop > loopCount) {
      delay(() => {
        countUp.value?.reset()
        loopAnim()
      }, props.delay)
    } else {
      finished.value = true
    }
  })
}
watch(finished, (flag) => {
  if (flag) {
    emits('finished')
  }
})
onMounted(() => {
  initCountUp()
  if (props.autoplay) {
    loopAnim()
  }
})
onUnmounted(() => {
  cancelAnimationFrame(dalayRafId)
  countUp.value?.reset()
})
let dalayRafId: number
// delay to execute callback function
const delay = (cb: () => unknown, seconds = 1) => {
  let startTime: number
  function count(timestamp: number) {
    if (!startTime) startTime = timestamp
    const diff = timestamp - startTime
    if (diff < seconds * 1000) {
      dalayRafId = requestAnimationFrame(count)
    } else {
      cb()
    }
  }
  dalayRafId = requestAnimationFrame(count)
}
const restart = () => {
  initCountUp()
  startAnim()
}
defineExpose({
  init: initCountUp,
  restart
})
</script>
<template>
  <div class="countup-wrap">
    <slot name="prefix"></slot>
    <span ref="elRef"> </span>
    <slot name="suffix"></slot>
  </div>
</template>
src/components/count-up/index.ts
New file
@@ -0,0 +1,3 @@
import CountUp from "./count-up.vue"
export default CountUp
src/components/scale-screen/scale-screen.vue
@@ -19,7 +19,7 @@
 * @returns {() => void}
 */
function debounce(fn: Function, delay: number): () => void {
  // let timer: NodeJS.Timeout;
  // let timer: NodeJS.Timer;
  let timer: any;
  return function (...args: any[]): void {
    if (timer) clearTimeout(timer);
src/views/HomeView.vue
@@ -11,7 +11,7 @@
    height="1080"
    :delay="500"
    :fullScreen="false"
    :boxStyle="{ background: '#000' }"
    :boxStyle="{ background: '#03050C' }"
    :wrapperStyle="wrapperStyle"
  >
    <div class="content_wrap">
src/views/header.vue
@@ -1,13 +1,22 @@
<script setup lang="ts">
import { reactive } from "vue";
const dateData = reactive({
  dateDay: null,
  dateYear: null,
  dateWeek: null,
  weekday: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
import dayjs from 'dayjs';
import type {DateDataType} from "./index.d"
const dateData = reactive<DateDataType>({
  dateDay: "",
  dateYear: "",
  dateWeek: "",
  timing:null
});
const weekday= ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
const timeFn = () => {
  dateData.timing = setInterval(() => {
    dateData.dateDay = dayjs().format("YYYY-MM-DD hh : mm : ss");
    dateData.dateWeek = weekday[dayjs().day()];
  }, 1000);
};
timeFn()
const showSetting = () => {};
</script>
src/views/index.d.ts
New file
@@ -0,0 +1,27 @@
export interface DateDataType {
    dateDay: string,
    dateYear: string,
    dateWeek: string,
    timing: NodeJS.Timer
}
interface CountUpOptions {
    startVal?: number; // number to start at (0)
    decimalPlaces?: number; // number of decimal places (0)
    duration?: number; // animation duration in seconds (2)
    useGrouping?: boolean; // example: 1,000 vs 1000 (true)
    useEasing?: boolean; // ease animation (true)
    smartEasingThreshold?: number; // smooth easing for large numbers above this if useEasing (999)
    smartEasingAmount?: number; // amount to be eased for numbers above threshold (333)
    separator?: string; // grouping separator (',')
    decimal?: string; // decimal ('.')
    // easingFn: easing function for animation (easeOutExpo)
    easingFn?: (t: number, b: number, c: number, d: number) => number;
    formattingFn?: (n: number) => string; // this function formats result
    prefix?: string; // text prepended to result
    suffix?: string; // text appended to result
    numerals?: string[]; // numeral glyph substitution
    enableScrollSpy?: boolean; // start animation when target is in view
    scrollSpyDelay?: number; // delay (ms) after target comes into view
    scrollSpyOnce?: boolean; // run only once
  }