perf: 优化翻牌组件

This commit is contained in:
tnt group 2022-09-29 19:11:54 +08:00
parent 5a79fc4f2d
commit 965f734618

View File

@ -1,24 +1,28 @@
<template> <template>
<div class="M-Flipper" :class="[flipType, { go: isFlipping }]"> <div class="M-Flipper" :class="[flipType, { go: isFlipping }]">
<div class="digital front" :data-front="frontTextFromData"></div> <div class="digital front" :data-front="frontTextFromData || 0"></div>
<div class="digital back" :data-back="backTextFromData"></div> <div class="digital back" :data-back="backTextFromData || 0"></div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref, PropType, watch } from 'vue'
type FlipType = 'up' | 'down' type FlipType = 'up' | 'down'
const name = 'Flipper'
const props = defineProps({ const props = defineProps({
flipType: {
type: Object as PropType<FlipType>,
default: () => {
return 'down'
}
},
frontText: { frontText: {
type: [Number, String], type: [Number, String],
default: 0 default: 0
}, },
backText: { backText: {
type: [Number, String], type: [Number, String],
default: 1 default: 0
}, },
duration: { duration: {
type: Number, type: Number,
@ -47,97 +51,101 @@ const props = defineProps({
}) })
const isFlipping = ref(false) const isFlipping = ref(false)
const flipType = ref<FlipType>('down')
const frontTextFromData = ref(props.frontText) const frontTextFromData = ref(props.frontText)
const backTextFromData = ref(props.backText) const backTextFromData = ref(props.backText)
const _flip = (type: FlipType, front: string | number, back: string | number) => { //
const flip = (front: string | number, back: string | number) => {
// //
if (isFlipping.value) return if (isFlipping.value) return
frontTextFromData.value = front //
backTextFromData.value = back backTextFromData.value = back
// type frontTextFromData.value = front
flipType.value = type
// true // true
isFlipping.value = true isFlipping.value = true
//
setTimeout(() => { setTimeout(() => {
// false isFlipping.value = false // false
isFlipping.value = false
frontTextFromData.value = back frontTextFromData.value = back
}, props.duration) }, props.duration)
} }
//
const flipDown = (front: string | number, back: string | number) => _flip('down', front, back) watch(
// () => props.backText,
const flipUp = (front: string | number, back: string | number) => _flip('up', front, back) (newVal, oldVal) => {
// console.log('watch:props.backText', newVal)
const setFront = (text: string | number) => { flip(newVal, oldVal as string | number)
frontTextFromData.value = text },
} {
// immediate: true
const setBack = (text: string | number) => { }
backTextFromData.value = text )
}
defineExpose({ defineExpose({
name, flip
flipDown,
flipUp
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// #region
@keyframes frontFlipDown { @keyframes frontFlipDown {
0% { 0% {
transform: perspective(160px) rotateX(0deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(0deg);
} }
100% { 100% {
transform: perspective(160px) rotateX(-180deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(-180deg);
} }
} }
@keyframes backFlipDown { @keyframes backFlipDown {
0% { 0% {
transform: perspective(160px) rotateX(180deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(180deg);
} }
100% { 100% {
transform: perspective(160px) rotateX(0deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(0deg);
} }
} }
@keyframes frontFlipUp { @keyframes frontFlipUp {
0% { 0% {
transform: perspective(160px) rotateX(0deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(0deg);
} }
100% { 100% {
transform: perspective(160px) rotateX(180deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(180deg);
} }
} }
@keyframes backFlipUp { @keyframes backFlipUp {
0% { 0% {
transform: perspective(160px) rotateX(-180deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(-180deg);
} }
100% { 100% {
transform: perspective(160px) rotateX(0deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(0deg);
} }
} }
// #endregion
$frontColor: v-bind('props.frontColor'); $frontColor: v-bind('props.frontColor');
$backColor: v-bind('props.backColor'); $backColor: v-bind('props.backColor');
$radius: v-bind('props.radius'); $radius: v-bind('`${props.radius}px`');
$width: v-bind('`${props.width}px`');
$height: v-bind('`${props.height}px`');
$shadowColor: #000000;
$lineColor: #ffffff;
.M-Flipper { .M-Flipper {
display: inline-block; display: inline-block;
position: relative; position: relative;
width: v-bind('`${width}px`'); width: $width;
height: v-bind('`${height}px`'); height: $height;
line-height: v-bind('`${height}px`'); line-height: $height;
border: solid 1px $backColor; border: solid 1px $backColor;
border-radius: v-bind('`${radius}px`'); border-radius: $radius;
background: $frontColor; background: $frontColor;
font-size: v-bind('`${width * 1.1}px`'); font-size: v-bind('`${props.width * 1.1}px`');
color: v-bind('props.frontColor'); color: $frontColor;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); //
text-align: center; text-align: center;
font-family: 'Helvetica Neue'; // font-family: 'Helvetica Neue';
.digital:before, .digital:before,
.digital:after { .digital:after {
@ -145,7 +153,7 @@ $radius: v-bind('props.radius');
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
background: v-bind('props.backColor'); background: $backColor;
overflow: hidden; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
} }
@ -160,13 +168,13 @@ $radius: v-bind('props.radius');
.digital:before { .digital:before {
top: 0; top: 0;
bottom: 50%; bottom: 50%;
border-radius: v-bind('`${radius}px`') v-bind('`${radius}px`') 0 0; border-radius: $radius $radius 0 0;
border-bottom: solid 1px #666; border-bottom: solid 1px rgba($color: $lineColor, $alpha: 0.5); // 线
} }
.digital:after { .digital:after {
top: 50%; top: 50%;
bottom: 0; bottom: 0;
border-radius: 0 0 v-bind('`${radius}px`') v-bind('`${radius}px`'); border-radius: 0 0 $radius $radius;
line-height: 0; line-height: 0;
} }
/*向下翻*/ /*向下翻*/
@ -176,7 +184,7 @@ $radius: v-bind('props.radius');
&.down .back:after { &.down .back:after {
z-index: 2; z-index: 2;
transform-origin: 50% 0%; transform-origin: 50% 0%;
transform: perspective(v-bind('`${height * 1.6}px`')) rotateX(180deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(180deg);
} }
&.down .front:after, &.down .front:after,
&.down .back:before { &.down .back:before {
@ -185,7 +193,7 @@ $radius: v-bind('props.radius');
&.down.go .front:before { &.down.go .front:before {
transform-origin: 50% 100%; transform-origin: 50% 100%;
animation: frontFlipDown v-bind('`${props.duration / 1000}s`') ease-in-out both; animation: frontFlipDown v-bind('`${props.duration / 1000}s`') ease-in-out both;
box-shadow: 0 -2px 6px rgba(255, 255, 255, 0.3); box-shadow: 0 -2px 6px rgba($color: $lineColor, $alpha: 0.3);
backface-visibility: hidden; backface-visibility: hidden;
} }
&.down.go .back:after { &.down.go .back:after {
@ -198,7 +206,7 @@ $radius: v-bind('props.radius');
&.up .back:before { &.up .back:before {
z-index: 2; z-index: 2;
transform-origin: 50% 100%; transform-origin: 50% 100%;
transform: perspective(v-bind('`${height * 1.6}px`')) rotateX(-180deg); transform: perspective(v-bind('`${props.height * 1.6}px`')) rotateX(-180deg);
} }
&.up .front:before, &.up .front:before,
&.up .back:after { &.up .back:after {
@ -207,7 +215,7 @@ $radius: v-bind('props.radius');
&.up.go .front:after { &.up.go .front:after {
transform-origin: 50% 0; transform-origin: 50% 0;
animation: frontFlipUp v-bind('`${props.duration / 1000}s`') ease-in-out both; animation: frontFlipUp v-bind('`${props.duration / 1000}s`') ease-in-out both;
box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3); box-shadow: 0 2px 6px rgba($color: $lineColor, $alpha: 0.3);
backface-visibility: hidden; backface-visibility: hidden;
} }
&.up.go .back:before { &.up.go .back:before {