前言
vue 翻页时钟制作基于 kuan-vue-flip-clock 插件,由于插件的样式比较固定,所以想要改变其样式需要自定义
效果
实现
vue2第一种方法
1.安装依赖
npm i kuan-vue-flip-clock
2.vue单文件,我这里是局部注册
<template>
<div class="test-clock-container">
<flip-clock />
</div>
</template>
<script>
import FlipClock from "kuan-vue-flip-clock";
export default {
components: {
FlipClock,
},
};
</script>
<style lang="scss">
.test-clock-container {
font: normal 14px "Helvetica Neue", Helvetica, sans-serif;
user-select: none;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
background: radial-gradient(ellipse at center, #969696 0%, #595959 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
</style>
参考文档:kuan-vue-flip-clock - npm
vue2第二种方法
新建下面几个文件,我是放在一个文件夹里,展示的话就是FlipClock.vue
FlipClock.vue
<template>
<div class="clock-container">
<flip-item :total="2" :current="timeArr[0]"/>
<flip-item :total="9" :current="timeArr[1]"/>
<div class="colon"></div>
<flip-item :total="5" :current="timeArr[2]"/>
<flip-item :total="9" :current="timeArr[3]"/>
<div class="colon"></div>
<flip-item :total="5" :current="timeArr[4]"/>
<flip-item :total="9" :current="timeArr[5]"/>
</div>
</template>
<script>
import FlipItem from './FlipItem.vue'
import { getTimeArr } from './utils'
export default {
components: {
FlipItem
},
data() {
return {
timeArr: getTimeArr()
}
},
mounted() {
this.startTimer()
},
beforeDestroy() {
this.stopTimer()
},
methods: {
startTimer() {
this.timer = setTimeout(() => {
this.stopTimer()
this.timeArr = getTimeArr()
this.startTimer()
}, 1000)
},
stopTimer() {
clearTimeout(this.timer)
}
}
}
</script>
<style lang='scss' scoped>
.clock-container {
display: flex;
align-items: center;
}
.colon {
height: 50px;
padding: 0 10px;
display: flex;
justify-content: space-around;
flex-direction: column;
&::after,
&::before {
content: '';
display: block;
width: 10px;
height: 10px;
background: rgba(0, 0, 0, 0.7);
border-radius: 50%;
}
}
</style>
FlipItem.vue
<template>
<div :class="{play: isPlay}">
<ul class="flip">
<li
class="item"
v-for="(item, key) in total + 1"
:class="{active: current === key, before: key === before}"
:key="item"
>
<div class="up">
<div class="shadow"></div>
<div class="inn">{{key}}</div>
</div>
<div class="down">
<div class="shadow"></div>
<div class="inn">{{key}}</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
total: {
type: Number,
default: 9
},
current: {
type: Number,
default: -1
}
},
data() {
return {
before: this.total === this.current ? -1 : this.total,
isPlay: false
}
},
watch: {
current(current, preCurrent) {
this.before = preCurrent
if (!this.isPlay) {
this.isPlay = true
}
}
}
}
</script>
<style lang="scss" scoped>
$width: 60px;
$height: 90px;
$fontSize: 80px;
$lineWidth: 3px;
$radius: 6px;
.flip {
position: relative;
margin: 5px;
width: $width;
height: $height;
font-size: $fontSize;
font-weight: bold;
line-height: $height - $lineWidth;
border-radius: $radius;
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.7);
.item {
list-style: none;
z-index: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
perspective: 200px;
transition: opacity 0.3s;
&.active {
z-index: 2;
}
&:first-child {
z-index: 2;
}
.up,
.down {
z-index: 1;
position: absolute;
left: 0;
width: 100%;
height: 50%;
overflow: hidden;
}
.up {
transform-origin: 50% 100%;
top: 0;
&:after {
content: '';
position: absolute;
top: ($height - $lineWidth) / 2;
left: 0;
z-index: 5;
width: 100%;
height: $lineWidth;
background-color: rgba(0, 0, 0, 0.4);
}
}
.down {
transform-origin: 50% 0%;
bottom: 0;
transition: opacity 0.3s;
}
.inn {
position: absolute;
left: 0;
z-index: 1;
width: 100%;
height: 200%;
color: #ccc;
text-shadow: 0 1px 2px #000;
text-align: center;
background-color: #333;
border-radius: $radius;
}
.up .inn {
top: 0;
}
.down .inn {
bottom: 0;
}
}
}
.play {
.item {
&.before {
z-index: 3;
}
&.active {
animation: asd 0.5s 0.5s linear both;
z-index: 2;
}
&.before .up {
z-index: 2;
animation: turn-up 0.5s linear both;
}
&.active .down {
z-index: 2;
animation: turn-down 0.5s 0.5s linear both;
}
}
}
@keyframes turn-down {
0% {
transform: rotateX(90deg);
}
100% {
transform: rotateX(0deg);
}
}
@keyframes turn-up {
0% {
transform: rotateX(0deg);
}
100% {
transform: rotateX(-90deg);
}
}
@keyframes asd {
0% {
z-index: 2;
}
5% {
z-index: 4;
}
100% {
z-index: 4;
}
}
.play {
.shadow {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
}
.before .up .shadow {
background: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%);
animation: show 0.5s linear both;
}
.active .up .shadow {
background: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%);
animation: hide 0.5s 0.3s linear both;
}
.before .down .shadow {
background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.1) 100%);
animation: show 0.5s linear both;
}
.active .down .shadow {
background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.1) 100%);
animation: hide 0.5s 0.3s linear both;
}
}
@keyframes show {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes hide {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>
index.js
import FlipClock from './FlipClock.vue'
export default FlipClock
utils.js
/**
* @description: 剩余时间
*/
export function getTimeArr(now = new Date()) {
// const Y = now.getFullYear()
// const M = now.getMonth() + 1
// const D = now.getDate()
const h = now.getHours()
const m = now.getMinutes()
const s = now.getSeconds()
return [
// ...toArr(Y),
// ...toArr(M),
// ...toArr(D),
...toArr(h),
...toArr(m),
...toArr(s)
]
}
// 更换数组类型
function toArr(n) {
return n >= 10 ? ('' + n).split('').map(item => Number(item)) : [0, n]
}
vue3中实现
新建下面几个文件,我是放在一个文件夹里,展示的话就是FlipClock.vue
FlipClock.vue
<template>
<div class="clock-container">
<flip-item :total="2" :current="timeArr[0]" />
<flip-item :total="9" :current="timeArr[1]" />
<div class="colon"></div>
<flip-item :total="5" :current="timeArr[2]" />
<flip-item :total="9" :current="timeArr[3]" />
<div class="colon"></div>
<flip-item :total="5" :current="timeArr[4]" />
<flip-item :total="9" :current="timeArr[5]" />
</div>
<div></div>
</template>
<script setup>
import FlipItem from "./FlipItem.vue";
import { getTimeArr } from "./utils";
import { ref, onMounted, onBeforeUnmount } from "vue";
const timeArr = ref(getTimeArr());
var timer;
onMounted(() => {
startTimer();
});
onBeforeUnmount(() => {
stopTimer();
});
function startTimer() {
console.log("启动定时器+++++++++++++++++++++++++++++");
timer = setInterval(() => {
// timeArr.value = getTimeArr(); // 使用 timeArr.value 更新值
timeArr.value = getTimeArr();
console.log("时间更新为:", timeArr.value);
}, 1000);
}
function stopTimer() {
console.log("停止定时器----------------------------");
clearInterval(timer);
}
</script>
<style scoped>
.clock-container {
display: flex;
align-items: center;
}
.colon {
height: 50px;
padding: 0 10px;
display: flex;
justify-content: space-around;
flex-direction: column;
}
.colon::after,
.colon::before {
content: "";
display: block;
width: 10px;
height: 10px;
background: rgba(0, 0, 0, 0.7);
border-radius: 50%;
}
</style>
FlipItem.vue
<template>
<div :class="{ play: isPlay }">
<ul class="flip">
<li
class="item"
v-for="(item, key) in total + 1"
:class="{ active: current == key, before: key == before }"
:key="item"
>
<div class="up">
<div class="shadow"></div>
<div class="inn">{{ key }}</div>
</div>
<div class="down">
<div class="shadow"></div>
<div class="inn">{{ key }}</div>
</div>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, watch, defineProps, computed } from "vue";
// const props = defineProps();
// const total = props.total;
// const current = props.current;
const total = 9;
const props = defineProps(["current"]);
const current = computed(() => {
return props.current;
});
const before = ref(total === current.value ? -1 : total);
const isPlay = ref(false);
console.log(current, "fffffffffffff ");
// watch(current, (newValue, oldValue) => {
// console.log('oldValue',current)
// before.value = oldValue;
// if (!isPlay.value) {
// isPlay.value = true;
// }
// });
// 监听 current 属性的变化
watch(
() => props.current,
(newValue, oldValue) => {
console.log("current 属性的值发生了变化");
before.value = oldValue;
if (!isPlay.value) {
isPlay.value = true;
}
}
);
// console.log(props)
// watch(current, (newValue, oldValue) => {
// console.log('watch triggered!');
// console.log('oldValue', newValue);
// console.log('oldValue', oldValue);
// before.value = oldValue;
//
// if (!isPlay.value) {
// isPlay.value = true;
// }
// });
</script>
<style lang="scss" scoped>
$width: 60px;
$height: 90px;
$fontSize: 80px;
$lineWidth: 3px;
$radius: 6px;
.flip {
position: relative;
margin: 5px;
width: $width;
height: $height;
font-size: $fontSize;
font-weight: bold;
line-height: $height - $lineWidth;
border-radius: $radius;
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.7);
.item {
list-style: none;
z-index: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
perspective: 200px;
transition: opacity 0.3s;
&.active {
z-index: 2;
}
&:first-child {
z-index: 2;
}
.up,
.down {
z-index: 1;
position: absolute;
left: 0;
width: 100%;
height: 50%;
overflow: hidden;
}
.up {
transform-origin: 50% 100%;
top: 0;
&:after {
content: "";
position: absolute;
.top: calc(($height - $lineWidth) / 2);
left: 0;
z-index: 5;
width: 100%;
height: $lineWidth;
background-color: rgba(0, 0, 0, 0.4);
}
}
.down {
transform-origin: 50% 0%;
bottom: 0;
transition: opacity 0.3s;
}
.inn {
position: absolute;
left: 0;
z-index: 1;
width: 100%;
height: 200%;
color: #ccc;
text-shadow: 0 1px 2px #000;
text-align: center;
background-color: #333;
border-radius: $radius;
}
.up .inn {
top: 0;
}
.down .inn {
bottom: 0;
}
}
}
.play {
.item {
&.before {
z-index: 3;
}
&.active {
animation: asd 0.5s 0.5s linear both;
z-index: 2;
}
&.before .up {
z-index: 2;
animation: turn-up 0.5s linear both;
}
&.active .down {
z-index: 2;
animation: turn-down 0.5s 0.5s linear both;
}
}
}
@keyframes turn-down {
0% {
transform: rotateX(90deg);
}
100% {
transform: rotateX(0deg);
}
}
@keyframes turn-up {
0% {
transform: rotateX(0deg);
}
100% {
transform: rotateX(-90deg);
}
}
@keyframes asd {
0% {
z-index: 2;
}
5% {
z-index: 4;
}
100% {
z-index: 4;
}
}
.play {
.shadow {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
}
.before .up .shadow {
background: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%);
animation: show 0.5s linear both;
}
.active .up .shadow {
background: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%);
animation: hide 0.5s 0.3s linear both;
}
.before .down .shadow {
background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.1) 100%);
animation: show 0.5s linear both;
}
.active .down .shadow {
background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.1) 100%);
animation: hide 0.5s 0.3s linear both;
}
}
@keyframes show {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes hide {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>
utils.js
/**
* @description: 剩余时间
*/
export function getTimeArr(now = new Date()) {
// const Y = now.getFullYear()
// const M = now.getMonth() + 1
// const D = now.getDate()
const h = now.getHours();
const m = now.getMinutes();
const s = now.getSeconds();
return [
// ...toArr(Y),
// ...toArr(M),
// ...toArr(D),
...toArr(h),
...toArr(m),
...toArr(s),
];
}
// 更换数组类型
function toArr(n) {
return n >= 10 ? ("" + n).split("").map((item) => Number(item)) : [0, n];
}
下班~