loading动效实现

在站上闲逛发现一个非常有意思的loading效果,跟着大佬仿写了一下Vue版本的。

https://blog.csdn.net/tianjian4592/article/details/44538605

loading动效实现_第1张图片
直接放源码

<script setup>
import {ref, defineProps, watch} from "vue";

const props = defineProps({
    isShow: {
        type: Boolean,
        default: false
    },
    leafNum: {
        type: Number,
        default: 5
    }  // 叶子数量
})

const isLoading = ref(false);
const progressNum = ref(0);
let progress;
let leafIntervalArr;  // 保存leaf的定时器

watch(() => props.isShow, () => {
    if (!props.isShow) {
        // 触发关闭loading的时候延迟1.4s关闭,确保动画完整
        progressNum.value = 100;
        clearInterval(progress);
        setTimeout(() => {
            isLoading.value = false;
            progressNum.value = 0;
            // 结束动画后清空定时器
            for (let i = 0; i < leafIntervalArr.length; i++) {
                clearInterval(leafIntervalArr[i]);
            }
        }, 1400)
    } else {
        isLoading.value = true;
        setInterval(() => {
            if (progressNum.value <= 90) {
                progressNum.value += 1;
            }
        }, 300);
        setTimeout(() => {
            randomLeaf();
        }, 1000)
    }
})

function randomLeaf() {
    leafIntervalArr = new Array(props.leafNum);
    for (let i = 0; i < props.leafNum; i++) {
        let leaf = document.createElement('img');
        leaf.className = 'leaf';
        leaf.src = '/src/assets/images/loading/leaf.png';
        leaf.alt = '叶子';
        console.log(document.querySelector('.loading-progress'));
        document.querySelector('.loading-progress').appendChild(leaf);
        // 随机时间 1~5s
        setTimeout(() => {
            let activeLeaf = document.querySelectorAll('.leaf')[i];
            let x = 260, y = 0;
            activeLeaf.style.left = x + "px"; //初始坐标x
            activeLeaf.style.top = y + "px"; //初始坐标y

            leafIntervalArr[i] = setInterval(function () {
                x = x - 5; //运动速度
                y = Math.floor(Math.sin(x / 260 * 2 * Math.PI) * Math.floor(Math.random() * 10)) + Math.floor(Math.random() * 10); //运动的高度
                if (x < 0) x = 260;
                activeLeaf.style.left = x + "px";
                activeLeaf.style.top = y + "px";
                activeLeaf.style.transform = "rotate(" + Math.floor(Math.random() * 360) + "deg)";
            }, 100);
        }, Math.floor(Math.random() * 5000) + 1);
    }
}
</script>

<template>
    <transition>
        <div class="loading-container" v-if="isLoading">
            <div class="loading-text">loading...</div>
            <div class="loading-wrapper">
                <div class="loading-progress">
                    <div class="loading-progress-content" :style="{width: progressNum + '%'}"></div>
                    <!-- 叶子区域 -->
                </div>
                <div class="loading-fan fan-container">
                    <img class="fan" :class="{'fan-close':progressNum === 100}"
                         src="../../assets/images/loading/fan.png" alt="风扇">
                    <img class="max" v-if="progressNum === 100" src="../../assets/images/loading/100.png"
                         alt="100%">
                </div>
            </div>
        </div>
    </transition>
</template>

<style scoped>
.loading-container {
    width: 100vw;
    height: 100vh;
    background-color: #E3B12FD0;
    display: flex;
    justify-content: center;
    align-content: center;
    align-items: center;
    flex-wrap: wrap;
    position: fixed;
    top: 0;
    right: 0;
}

.loading-text {
    width: 100%;
    text-align: center;
    font-size: 20px;
    font-weight: bold;
    color: rgb(255, 168, 0);
    margin-bottom: 20px;
}

.loading-wrapper {
    width: 300px;
    height: 48px;
    background-color: #EED385D2;
    border-radius: 24px;
    position: relative;
    z-index: 9;
}

.loading-progress {
    width: 260px;
    height: 36px;
    background-color: #EED385D2;
    border-top-left-radius: 16px;
    border-bottom-left-radius: 16px;
    margin: 6px 0 6px 6px;
    overflow: hidden;
    position: absolute;
    top: 0;
    left: 0;
}

.loading-progress-content {
    width: 0;
    height: 36px;
    background-color: rgb(255, 168, 0);
    transition: all 0.5s;
    position: relative;
    z-index: 9;
}

.loading-fan {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background-color: rgb(251, 205, 81);
    border: 3px solid rgba(255, 255, 255, 0.6);
    box-sizing: border-box;
    position: absolute;
    top: 0;
    right: 0;
    z-index: 9;
}

.fan {
    width: 36px;
    height: 36px;
    position: absolute;
    top: 3px;
    left: 4px;
    animation: spin 2s infinite linear;
}

.fan-close {
    animation: fanClose 1.3s 1 linear;
    animation-fill-mode: forwards;
}

.max {
    width: 36px;
    height: 36px;
    position: absolute;
    top: 3px;
    left: 3px;
    animation: maxFont 1.3s 1 linear;
    animation-fill-mode: forwards;
}

@keyframes spin {
    from {
        transform: rotate(360deg);
    }
    to {
        transform: rotate(0deg);
    }
}

@keyframes fanClose {
    from {
        transform: rotate(360deg) scale(1);
    }
    to {
        transform: rotate(0deg) scale(0);
    }
}

@keyframes maxFont {
    from {
        transform: scale(0);
    }
    to {
        transform: scale(1);
    }
}
</style>
<style>
.leaf {
    width: 15px;
    height: 15px;
    position: absolute;
    top: 0;
    left: 260px;
    z-index: 5;
    transition: all 0.01s;
}
</style>

核心思想:

  1. css动画旋转风扇,根据时间慢慢增加进度条的宽度。
  2. 生成最大数量的叶子,随机事件、随机振幅、随机旋转角度从右向左沿正弦曲线运动

你可能感兴趣的:(Vue,javascript,前端,vue.js)