uniapp 翻页倒计时动画

285 阅读2分钟
<template>
    <view class="content" style="margin: 20px auto 40px auto;">
        <view class="flip-container">
            <view class="flip-items" v-for="(unit,unitIndex) of timeArr" :key="unitIndex">
                <view class="item" v-for="(item,index) of unit.max + 1" :key="index"
                    :class="{current: unit.current == index, past: unit.current + 1 == index || index==unit.max&&unit.current==0}">
                    <view class="up">
                        <view class="inner">{{index}}</view>
                        <view class="shadow"></view>
                    </view>
                    <view class="down">
                        <view class="inner">{{index}}</view>
                        <view class="shadow"></view>
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>

<script>
    export default {
        props: ['residue_time'],
        data() {
            return {
                timeStr: '',
                residueTime: '',
                timer: null
            }
        },
        computed: {
            timeArr() {
                let timeFormat= [...this.timeStr].map((unit, index) => {
                    let max;
                    if (3==index || 5 ==index) { //分秒的个位
                        max = 9
                    } else if (index == 0) { //时十位
                        max = 2
                    }else if (index == 1) { //时个位
                        max = 3
                    }  else if (index == 2 || index == 4) { //分秒的十位
                        max = 5
                    }
                    return {
                        max,
                        current: Number(unit),
                    }
                })
                return timeFormat
            }
        },
        methods: {
            init(residue_time) {
                this.residueTime = residue_time
                this.countDown();
                if(this.residueTime>0){
                    this.timer = setInterval(() => {
                        if(0>Number(this.residueTime)){
                            this.clearTimer();
                            // 倒计时结束处理
                            this.$emit('timeup')
                        }
                        this.countDown();
                    }, 1000)
                }
            },
            countDown() {
                const {
                    h,
                    i,
                    s
                } = this.secondsFormat(this.residueTime)
                this.timeStr = h + i + s;
                this.residueTime--
            },
            secondsFormat(seconds) {
                let [day, hour, minute, second] = [0, 0, 0, 0]
                if (seconds > 0) {
                    day = Math.floor(seconds / (60 * 60 * 24))
                    hour = Math.floor(seconds / (60 * 60)) - (day * 24)
                    minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
                    second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
                }
                if (day < 10) {
                    day = '0' + day
                }
                if (hour < 10) {
                    hour = '0' + hour
                }
                if (minute < 10) {
                    minute = '0' + minute
                }
                if (second < 10) {
                    second = '0' + second
                }
                return {
                    d: day,
                    h: hour,
                    i: minute,
                    s: second
                };
            },
            clearTimer(){
                 clearInterval(this.timer)
                this.timer=null;
            }
        },

    };
</script>
<style lang="stylus">
    $width = 100rpx;
    $time = 1s
    $height = $width * 1.5;
    $lineWidth = ($width / 60);
    $radius = ($width / 10);
    $perspective = $width * 5;
    $gap= $width * 0.2

    .flip-container
        display flex
        justify-content center
        padding 0 20rpx
        position relative
        .flip-items
            position relative
            width $width
            height $height
            font-size 60rpx
            font-weight bold
            border-radius $radius
            box-shadow: 0 2rpx 18rpx rgba(0, 0, 0, 0.7)
            &:nth-of-type(2n+1)
                margin-right 10rpx
            &:nth-of-type(2),&:nth-of-type(4)
                margin-right 50rpx
                &::after,&::before
                    position absolute
                    right -(@margin-right / 2)
                    content ''
                    transform translateX(50%)
                    width 12rpx
                    height @width
                    border-radius 50%
                    background-color  #E22A2A
                &::before
                    top 35%
                &::after
                    bottom 35%
			
            .item
                z-index 1
                position absolute
                top 0
                left 0
                right 0
                bottom 0
                perspective $perspective
                &:before
                    content: ''
                    position absolute
                    top (($height - $lineWidth) / 2)
                    left 0
                    z-index 9
                    width: 100%
                    height: $lineWidth
                    min-height 2px
                    background-color rgba(0, 0, 0, 0.6)
                .up,.down
                    position absolute;
                    left 0;
                    right 0
                    height 50%;
                    overflow hidden
                .up
                    transform-origin 50% 100%
                    top 0
                .down
                    transform-origin 50% 0%
                    bottom 0
                .inner 
                    position: absolute;
                    left: 0;
                    width: 100%;
                    height: $height
                    line-height $height
                    color: #E22A2A;
                    font-size:50rpx;
                    text-shadow: 0 2rpx 4rpx #000
                    text-align: center;
                    background-color: #222
                    border-radius: $radius
                .up .inner 
                    top 0
                .down .inner 
                    bottom 0
                .up .shadow
                    border-top-left-radius $radius
                    border-top-right-radius $radius
                .down .shadow
                    border-bottom-left-radius $radius
                    border-bottom-right-radius $radius
        .flip-items .item
            &.past {
              z-index: 3;
            }
            &.current {
              //反转到中间时候当前秒层级最大
              animation: highter-level ($time/2) ($time/2) linear forwards;
              z-index: 2;
            }
            &.past .up {
              animation: flip-past-up ($time/2) linear both;
            }
            &.current .down {
              animation: flip-current-down ($time/2) ($time/2) linear both;
            }
            @keyframes flip-current-down {
              from{
                transform: rotateX(90deg);
              }
              to {
                transform: rotateX(0deg);
              }
            }
            @keyframes flip-past-up {
              from{
                transform: rotateX(0deg);
              }
              to {
                transform: rotateX(-90deg);
              }
            }    
            @keyframes highter-level {
              from{
                z-index: 4;
              }
              to {
                z-index: 4;
              }
            }
				
        // 控制阴影
        .flip-items .item
            .shadow {
              position: absolute;
              width: 100%;
              height: 100%;
            }
            &.past .up .shadow {
              background: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%);
              animation: show ($time/2) linear both;
            }
            &.past .down .shadow {
              background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.1) 100%);
              animation: show ($time/2) linear both;
            }
            &.current .up .shadow {
              background: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%);
              animation: hide ($time/2) 0.3s linear both;
            }
            &.current .down .shadow {
              background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.1) 100%);
              animation: hide ($time/2) 0.3s linear both;
            }
        
        @keyframes show {
          from{
            opacity: 0;
          }
          to {
            opacity: 1;
          }
        }
        @keyframes hide {
          from{
            opacity: 1;
          }
          to {
            opacity: 0;
          }
        }
</style>

效果图:

image.png

参考链接:ext.dcloud.net.cn/plugin?id=8…