基于vue3 + ts 开发的数字翻牌器组件

写了两个组件,一个是DigitalFlop.vue,一个是DigitalItem.vue

DigitalFlop.vue 的代码如下:

<template>
  <div class="digital-flop-component">
    <DigitalItem
      v-for="(item, index) in integerDigitalArray"
      :key="getItemKey(index)"
      :data="item"
      :animation="animation"
    ></DigitalItem>
    <DigitalItem
      v-for="(item, index) in decimalDigitalArray"
      :key="index"
      :data="item"
      :isDecimalPlace="true"
      :animation="animation"
    ></DigitalItem>
  </div>
</template>

<script setup lang="ts">
/**
 * @Description: 数字翻牌器组件
 * @version 1.0.0 2022-06-05 22:10:42
 */
import { ref, watch, nextTick, onMounted } from "vue";
import DigitalItem from "./DigitalItem.vue";

interface Props {
  value: number;
  minLength?: number | undefined;
  maxLength?: number | undefined;
  // 小数位数
  decimalPlaceNumber?: number;
  // 是否显示千分位
  isShowSeparator?: boolean;
  // 是否添加动画效果
  animation?: boolean;
  // 整数数字牌样式
  integerCardStyle?: any;
  // 小数位数字牌样式
  decimalPlaceCardStyle?: any;
}

const props = withDefaults(defineProps<Props>(), {
  value: 0,
  minLength: 4,
  isShowSeparator: false,
  animation: true,
  decimalPlaceNumber: 2
});

// 整数数组
const integerDigitalArray = ref<string[]>([]);
// 小数数组
const decimalDigitalArray = ref<string[]>([]);

watch(
  () => props.value,
  () => {
    initDigitalData(props.value);
  },
  { immediate: true }
);

/**
 * 设置整数数组
 * @param digitalValue
 */
function initDigitalData(digitalValue: number) {
  let integerArray = [];
  // 显示小数位
  if (props.decimalPlaceNumber > 0) {
    const value = digitalValue.toFixed(props.decimalPlaceNumber);
    const index = value.indexOf(".");
    decimalDigitalArray.value = value.substring(index + 1).split("");
    integerArray = value.substring(0, index).split("");
  } else {
    decimalDigitalArray.value = [];
    integerArray = digitalValue.toString().split("");
  }
  const addZero = (array: string[], minLength: number) => {
    if (array.length < minLength) {
      array.unshift("0");
      addZero(array, minLength);
    }
  };
  if (props.minLength) {
    addZero(integerArray, props.minLength);
  }
  if (props.maxLength && integerArray.length > props.maxLength) {
    console.error(
      `传入的数字${digitalValue}已经超过最大长度${props.maxLength}`
    );
  }
  // 添加千分位
  if (props.isShowSeparator) {
    integerArray = addSeparator(integerArray);
  }
  integerDigitalArray.value = integerArray;
}

/**
 * 添加千分号
 * @param array
 */
function addSeparator(array: Array<string>) {
  const dataArray = array.reverse();
  const separatorArray = [];
  while (dataArray.length) {
    separatorArray.push(dataArray.splice(0, 3).join(""));
  }
  return separatorArray.join(",").split("").reverse();
}

// 设置关键字,从小数位开始向左为1,避免出现数字变大时的显示问题
function getItemKey(index: number) {
  return integerDigitalArray.value.length - index;
}
</script>

<style lang="scss" scoped>
.digital-flop-component {
  display: flex;
  align-items: flex-end;
  flex-wrap: nowrap;

  .digital-item-component {
    margin-right: 5px;
  }

  .digital-item-component:last-child {
    margin-right: 0;
  }
}
</style>

DigitalItem.vue的代码如下:

<template>
  <div class="digital-item-component">
    <div
      v-if="isNumberString"
      class="box-item"
      :class="{ 'decimal-place-box-item': isDecimalPlace }"
    >
      <div class="number-text-container">
        <!--动态切换效果-->
        <template v-if="animation">
          <div
            v-for="num in 10"
            :key="num"
            class="number-value"
            :style="numberDynamicStyle"
          >
            {{ num - 1 }}
          </div>
        </template>
        <div class="number-value">{{ data }}</div>
      </div>
    </div>
    <div class="comma-item" v-else>{{ data }}</div>
  </div>
</template>

<script setup lang="ts">
import { computed } from "vue";

interface Props {
  data: string;
  // 是否小数位
  isDecimalPlace?: boolean;
  // 是否添加动画效果
  animation?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  data: "0",
  animation: true,
  isDecimalPlace: false
});

const isNumberString = computed(() => {
  const numberValue = Number(props.data);
  return !isNaN(numberValue);
});

// 数字动态样式
const numberDynamicStyle = computed(() => {
  let numberValue = Number(props.data);
  if (isNaN(numberValue)) {
    numberValue = 0;
  }
  return {
    transform: `translate(0, -${numberValue * 100}%)`,
    transition: "transform 1s ease-in-out"
  };
});
</script>

<style lang="scss">
// 数字翻牌器样式变量
:root {
  --da-digital-item-width: 40px;
  --da-digital-item-height: 60px;
  --da-digital-item-font-size: 40px;

  --da-digital-item-decimal-place-width: 28px;
  --da-digital-item-decimal-place-height: 40px;
  --da-digital-item-decimal-place-font-size: 28px;

  --da-digital-item-bg-color: #409eff;
}
</style>

<style lang="scss" scoped>
.digital-item-component {
  .box-item {
    width: var(--da-digital-item-width);
    height: var(--da-digital-item-height);
    font-size: var(--da-digital-item-font-size);
    font-weight: 500;
    line-height: 1;
    border-radius: 4px;
    color: #fff;
    background-color: var(--da-digital-item-bg-color);
    position: relative;
    overflow: hidden;

    .number-text-container {
      display: flex;
      flex-direction: column;
      flex-wrap: wrap;

      .number-value {
        flex: none;
        width: var(--da-digital-item-width);
        height: var(--da-digital-item-height);
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
  }

  .comma-item {
    padding: 0 5px;
    height: 100%;
    text-align: center;
    font-size: 30px;
    font-weight: bold;
    color: #000;
  }

  .decimal-place-box-item {
    width: var(--da-digital-item-decimal-place-width);
    height: var(--da-digital-item-decimal-place-height);

    .number-text-container {
      .number-value {
        width: var(--da-digital-item-decimal-place-width);
        height: var(--da-digital-item-decimal-place-height);
        font-size: var(--da-digital-item-decimal-place-font-size);
      }
    }
  }
}
</style>

用法:

HTML template部分
<DigitalFlop class="customize-digital-flop" :value="value"></DigitalFlop>
<el-button type="primary" @click="changeNumber">change number</el-button>


<script setup lang="ts"> 部分

const value = ref(0);

function changeNumber() {
  if (moneyValue.value === 5792.72) {
    moneyValue.value = 12305.34;
  } else {
    moneyValue.value = 5792.72;
  }
}

样式部分(可自定义样式)
<style lang="scss" scoped>
.customize-digital-flop {
  --da-digital-item-width: 30px;
  --da-digital-item-height: 40px;
  --da-digital-item-font-size: 30px;

  --da-digital-item-decimal-place-width: 24px;
  --da-digital-item-decimal-place-height: 30px;
  --da-digital-item-decimal-place-font-size: 24px;

  --da-digital-item-bg-color: #07368a;
}
</style>





效果如下:

 组件样式风格没有细致去修改,大家可以发挥自己特长继续优化哈。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值