时钟翻牌器 vue3

在这里插入图片描述

在这里插入图片描述

index.vue

<template>
  <div class="FlipClock">
    <Flipper ref="flipperHour" />
    <em>:</em>
    <Flipper ref="flipperMinute" />
    <em>:</em>
    <Flipper ref="flipperSecond" />
  </div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue"
import Flipper from "./Flipper.vue"
const timer = ref<any>(null)
const flipObjs = ref<any[]>([])
const flipperHour = ref()
const flipperMinute = ref()
const flipperSecond = ref()
// 初始化数字
function init() {
  let now = new Date()
  let nowTimeStr = formatDate(new Date(now.getTime()), "hh-ii-ss")
  nowTimeStr = nowTimeStr.split("-")
  for (let i = 0; i < flipObjs.value.length; i++) {
    flipObjs.value[i].value.setFront(nowTimeStr[i])
  }
}
// 开始计时
function run() {
  timer.value = setInterval(() => {
    // 获取当前时间
    let now = new Date()
    let nowTimeStr = formatDate(new Date(now.getTime() - 1000), "hh-ii-ss")
    nowTimeStr = nowTimeStr.split("-")
    let nextTimeStr = formatDate(now, "hh-ii-ss")
    nextTimeStr = nextTimeStr.split("-")
    for (let i = 0; i < flipObjs.value.length; i++) {
      if (nowTimeStr[i] === nextTimeStr[i]) {
        continue
      }
      flipObjs.value[i].value.flipDown(nowTimeStr[i], nextTimeStr[i])
    }
  }, 1000)
}
// 正则格式化日期
function formatDate(date, dateFormat) {
  /* 单独格式化年份,根据y的字符数量输出年份
     * 例如:yyyy => 2019
            yy => 19
            y => 9
     */
  if (/(y+)/.test(dateFormat)) {
    dateFormat = dateFormat.replace(
      RegExp.$1,
      (date.getFullYear() + "").substr(4 - RegExp.$1.length)
    )
  }
  // 格式化月、日、时、分、秒
  let o = {
    "m+": date.getMonth() + 1,
    "d+": date.getDate(),
    "h+": date.getHours(),
    "i+": date.getMinutes(),
    "s+": date.getSeconds()
  }
  for (let k in o) {
    if (new RegExp(`(${k})`).test(dateFormat)) {
      // 取出对应的值
      let str = o[k] + ""
      /* 根据设置的格式,输出对应的字符
       * 例如: 早上8时,hh => 08,h => 8
       * 但是,当数字>=10时,无论格式为一位还是多位,不做截取,这是与年份格式化不一致的地方
       * 例如: 下午15时,hh => 15, h => 15
       */
      dateFormat = dateFormat.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str))
    }
  }
  return dateFormat
}
// 日期时间补零
function padLeftZero(str) {
  return str.toString().padStart(2, "0")
}
onMounted(() => {
  flipObjs.value = [flipperHour, flipperMinute, flipperSecond]
  init()
  run()
})
</script>

<style lang="scss" scoped>
.FlipClock {
  text-align: center;
}
.FlipClock .M-Flipper {
  margin: 0 3px;
}
.FlipClock em {
  display: inline-block;
  line-height: 102px;
  font-size: 66px;
  font-style: normal;
  vertical-align: top;
}
</style>

Flipper.vue

<template>
  <div class="M-Flipper" :class="[flipType, { go: isFlipping }]">
    <div class="digital front" :class="_textClass(frontTextFromData)"></div>
    <div class="digital back" :class="_textClass(backTextFromData)"></div>
  </div>
</template>
<script setup lang="ts">
import { ref } from "vue"

const props = defineProps({
  // front paper text
  // 前牌文字
  frontText: {
    type: [Number, String],
    default: "00"
  },
  // back paper text
  // 后牌文字
  backText: {
    type: [Number, String],
    default: "01"
  },
  // flipping duration, please be consistent with the CSS animation-duration value.
  // 翻牌动画时间,与CSS中设置的animation-duration保持一致
  duration: {
    type: Number,
    default: 600
  }
})
const isFlipping = ref(false)
const flipType = ref("down")
const frontTextFromData = ref(props.frontText)
const backTextFromData = ref(props.backText)

function _textClass(number) {
  return "number" + number
}
function _flip(type, front, back) {
  // 如果处于翻转中,则不执行
  if (isFlipping.value) {
    return false
  }
  frontTextFromData.value = front
  backTextFromData.value = back
  // 根据传递过来的type设置翻转方向
  flipType.value = type
  // 设置翻转状态为true
  isFlipping.value = true
  setTimeout(() => {
    // 设置翻转状态为false
    isFlipping.value = false
    frontTextFromData.value = back
  }, props.duration)
}
// 下翻牌
function flipDown(front, back) {
  _flip("down", front, back)
}
// 上翻牌
function flipUp(front, back) {
  _flip("up", front, back)
}
// 设置前牌文字
function setFront(text) {
  frontTextFromData.value = text
}
// 设置后牌文字
function setBack(text) {
  backTextFromData.value = text
}
defineExpose({
  flipDown,
  flipUp,
  setFront,
  setBack
})
</script>

<style lang="scss" scoped>
.M-Flipper {
  display: inline-block;
  position: relative;
  width: 120px;
  height: 100px;
  line-height: 100px;
  border: solid 1px #000;
  border-radius: 10px;
  background: #fff;
  font-size: 66px;
  color: #fff;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
  text-align: center;
  font-family: "Helvetica Neue";
}

.M-Flipper .digital:before,
.M-Flipper .digital:after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  background: #000;
  overflow: hidden;
  box-sizing: border-box;
}

.M-Flipper .digital:before {
  top: 0;
  bottom: 50%;
  border-radius: 10px 10px 0 0;
  border-bottom: solid 1px #666;
}

.M-Flipper .digital:after {
  top: 50%;
  bottom: 0;
  border-radius: 0 0 10px 10px;
  line-height: 0;
}

/*向下翻*/
.M-Flipper.down .front:before {
  z-index: 3;
}

.M-Flipper.down .back:after {
  z-index: 2;
  transform-origin: 50% 0%;
  transform: perspective(160px) rotateX(180deg);
}

.M-Flipper.down .front:after,
.M-Flipper.down .back:before {
  z-index: 1;
}

.M-Flipper.down.go .front:before {
  transform-origin: 50% 100%;
  animation: frontFlipDown 0.6s ease-in-out both;
  box-shadow: 0 -2px 6px rgba(255, 255, 255, 0.3);
  backface-visibility: hidden;
}

.M-Flipper.down.go .back:after {
  animation: backFlipDown 0.6s ease-in-out both;
}

/*向上翻*/
.M-Flipper.up .front:after {
  z-index: 3;
}

.M-Flipper.up .back:before {
  z-index: 2;
  transform-origin: 50% 100%;
  transform: perspective(160px) rotateX(-180deg);
}

.M-Flipper.up .front:before,
.M-Flipper.up .back:after {
  z-index: 1;
}

.M-Flipper.up.go .front:after {
  transform-origin: 50% 0;
  animation: frontFlipUp 0.6s ease-in-out both;
  box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3);
  backface-visibility: hidden;
}

.M-Flipper.up.go .back:before {
  animation: backFlipUp 0.6s ease-in-out both;
}

@keyframes frontFlipDown {
  0% {
    transform: perspective(160px) rotateX(0deg);
  }

  100% {
    transform: perspective(160px) rotateX(-180deg);
  }
}

@keyframes backFlipDown {
  0% {
    transform: perspective(160px) rotateX(180deg);
  }

  100% {
    transform: perspective(160px) rotateX(0deg);
  }
}

@keyframes frontFlipUp {
  0% {
    transform: perspective(160px) rotateX(0deg);
  }

  100% {
    transform: perspective(160px) rotateX(180deg);
  }
}

@keyframes backFlipUp {
  0% {
    transform: perspective(160px) rotateX(-180deg);
  }

  100% {
    transform: perspective(160px) rotateX(0deg);
  }
}
.M-Flipper {
  @for $i from 0 through 59 {
    $classPrefix: if($i <= 9, "number0", "number");
    $content: if($i <= 9, "0" + $i, $i);
    .digital.#{$classPrefix}#{$i}:before,
    .digital.#{$classPrefix}#{$i}:after {
      // 你的样式
      content: "#{$content}";
    }
  }
}
</style>

感谢大神_Nealyang_博客
感谢大神_Nealyang_Github

相关推荐

  1. 微信小程序案例-03时钟-3

    2024-06-06 20:06:01       42 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-06 20:06:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-06 20:06:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-06 20:06:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-06 20:06:01       20 阅读

热门阅读

  1. 时序数据库介绍及应用场景,C#实例

    2024-06-06 20:06:01       6 阅读
  2. vscode编译和调试wsl环境的c语言程序

    2024-06-06 20:06:01       9 阅读
  3. 公有云服务器部署springboot工程详细步骤

    2024-06-06 20:06:01       11 阅读
  4. golang结构与结构方法实现示例

    2024-06-06 20:06:01       9 阅读
  5. 如何完全清除docker

    2024-06-06 20:06:01       9 阅读
  6. 速率限制中间件AspNetCoreRateLimit

    2024-06-06 20:06:01       7 阅读
  7. 云原生周刊:Gateway API v1.1 发布 | 2024.6.3

    2024-06-06 20:06:01       9 阅读
  8. 设计模式详解(八):外观模式——Facade

    2024-06-06 20:06:01       10 阅读
  9. layui实现鼠标移入/移出时显示/隐藏tips

    2024-06-06 20:06:01       7 阅读