anime.js实现一个天气动画

这篇博客展示了一个使用SVG和CSS构建的天气动画,包括下雨效果(大雨和小雨)、闪电动画以及日出场景。通过Vue.js实现元素动态显示与隐藏,以及颜色和位置的动画变化,提供了代码示例和交互按钮来切换不同天气状态。
摘要由CSDN通过智能技术生成

在这里插入图片描述
有点累,不想说太多,以下是几个关键点,有兴趣自己看代码吧。

  • 月亮是用工具画的,svg格式
  • 太阳是用css代码写的
  • 每一颗雨滴都是一个单独的动画
  • 闪电是png图片,仔细看会有点锯齿
  • 大雨和小雨的切换是通过控制雨滴的v-show
  • 动画部分的代码有点乱,自己练习用的,不想整理
  • 山峰是用工具画的svg图,方便控制其颜色动画
  • 用vue实现起来简单太多了,推荐使用

参考代码 链接

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            display: flex;
            justify-content: center;
        }

        .container {
            position: relative;
            width: 300px;
            height: 400px;
            background-color: #171c2b;
            overflow: hidden;
        }

        .bigWater,
        .mediumWater,
        .smallWater {
            position: absolute;
            top: -10px;
            border-radius: 50%;
        }

        .bigWater::after,
        .mediumWater::after,
        .smallWater::after {
            content: "";
            position: absolute;
            top: -3px;
            left: 2px;
            border-radius: 50%;
        }

        .bigWater {
            height: 10px;
            width: 10px;
            background-color: #7fc1f9;
        }

        .bigWater::after {
            height: 7px;
            width: 7px;
            background-color: #7fc1f9;
        }

        .mediumWater {
            height: 8px;
            width: 8px;
            background-color: #8ec3f1;
        }

        .mediumWater::after {
            height: 5px;
            width: 5px;
            background-color: #8ec3f1;
        }

        .smallWater {
            height: 5px;
            width: 5px;
            background-color: #98bbda;
        }

        .smallWater::after {
            height: 3px;
            width: 3px;
            background-color: #98bbda;
        }

        .moon {
            position: absolute;
            left: -80px;
            bottom: 70px;
        }

        .fil0 {
            fill: #FFF8C7
        }

        .fil1 {
            fill: #EDE7B6
        }

        .fil2 {
            fill: #0f2850
        }

        .fil3 {
            fill: #173563
        }

        .fil4 {
            fill: #0f2850
        }

        .lightning {
            position: absolute;
            right: -3px;
            top: -3px;
            opacity: 0;
        }
        .moutain {
            position: absolute;
            bottom: -40px;
        }

        .sun {
            position: absolute;
            width: 71px;
            height: 71px;
            border-radius: 50%;
            right: -80px;
            bottom: 70px;
            background-color: #ffb9a1;
            box-shadow: 0 0 30px 0 #ffffff;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="container">

            <svg class="moon" width="75px" height="75px" version="1.1" viewBox="0 0 75 75">
                <g>
                    <circle class="fil0" cx="37" cy="38" r="35" />
                    <circle class="fil1" cx="27" cy="14" r="2" />
                    <ellipse class="fil1" cx="49" cy="16" rx="5" ry="4" />
                    <circle class="fil1" cx="43" cy="38" r="7" />
                    <circle class="fil1" cx="63" cy="43" r="3" />
                    <ellipse class="fil1" cx="37" cy="68" rx="5" ry="4" />
                    <circle class="fil1" cx="23" cy="45" r="3" />
                    <circle class="fil1" transform="matrix(0.460443 -0.887689 0.718639 0.372757 12.5924 28.1206)"
                        r="5" />
                </g>
            </svg>

            <div class="sun"></div>

            <img src="./lightning.png" class="lightning" alt="">

            <svg class="moutain" width="300px" height="400px" viewBox="0 0 300 400">
                <g>
                    <path class="fil2"
                        d="M-97 370c21,-25 57,-71 85,-94 28,-23 48,-24 67,-11 19,13 38,40 54,65 16,25 30,50 37,62 7,12 7,12 -40,12 -47,0 -141,-1 -186,-3 -44,-2 -38,-6 -17,-31z" />
                    <path class="fil3"
                        d="M36 369c16,-19 44,-54 65,-72 21,-18 37,-18 52,-8 15,10 29,31 42,50 13,20 23,38 28,48 5,9 5,9 -31,9 -36,0 -109,-1 -143,-2 -34,-2 -29,-5 -13,-24z" />
                    <path class="fil4"
                        d="M186 319c27,-26 52,-45 69,-57 17,-11 26,-14 37,-9 11,5 25,19 44,44 19,24 44,59 57,80 14,21 16,27 -2,32 -18,4 -56,7 -98,7 -42,0 -87,-2 -115,-3 -28,-1 -40,-2 -49,-2 -10,-1 -18,-2 -8,-18 10,-16 38,-48 65,-73z" />
                </g>
            </svg>

            <div v-for="(item,i) in bigArr" :key="i" v-show="item.show" :style="{left: item.left+'px'}" :class="[bigWater, item.class]"></div>
            <div v-for="(item,i) in mediumArr" :key="i" v-show="item.show" :style="{left: item.left+'px'}" :class="[mediumWater, item.class]"></div>
            <div v-for="(item,i) in smallArr" :key="i" v-show="item.show" :style="{left: item.left+'px'}" :class="[smallWater, item.class]"></div>
        </div>
        <button @click="onLightning">闪电</button>
        <button @click="onBigRain">大雨</button>
        <button @click="onSmallRain">小雨</button>
        <button @click="onSunny">晴天</button>
    </div>
</body>
<!-- 2.6.x版 -->
<script src="./vue.min.js"></script>
<!-- 3.1.x版 -->
<script src="./anime.min.js"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            state: 'rain',
            bigWater: 'bigWater',
            mediumWater: 'mediumWater',
            smallWater: 'smallWater',
            bigAmount: 10,          // 大雨滴数量
            mediumAmount: 20,      // 中雨滴数量
            smallAmount: 30,       // 小雨滴数量
            bigArr: [],
            mediumArr: [],
            smallArr: []
        },
        created() {
            this.generateData()
        },
        mounted() {
            this.startAnimeWater()
            setTimeout(() => {
                this.animeMoon()
            }, 500);
        },
        methods: {
            generateData() {
                for (let i = 1; i < this.bigAmount; i++) {
                    let obj = {
                        class: `bigWater${i}`,
                        show: true,
                        left: anime.random(30, 280),
                        delay: anime.random(0, 1000) //值越大,雨滴越分散
                    }
                    this.bigArr.push(obj)
                }
                for (let i = 1; i < this.mediumAmount; i++) {
                    let obj = {
                        class: `mediumWater${i}`,
                        show: true,
                        left: anime.random(30, 300),
                        delay: anime.random(0, 1000)
                    }
                    this.mediumArr.push(obj)
                }
                for (let i = 1; i < this.smallAmount; i++) {
                    let obj = {
                        class: `smallWater${i}`,
                        show: true,
                        left: anime.random(10, 320),
                        delay: anime.random(0, 2000)
                    }
                    this.smallArr.push(obj)
                }
            },
            startAnimeWater() {
                const keyframeBig = [
                    {
                        translateX: -40, //相对值
                        translateY: 400,
                        duration: 750,   //下落速度
                    },
                    {
                        scaleX: 3,
                        duration: 100,
                    },
                ]
                this.animeWater(this.bigArr, keyframeBig)
                const keyframeMedium = [
                    {
                        translateX: -40,
                        translateY: 400,
                        duration: 1300,
                    }
                ]
                this.animeWater(this.mediumArr, keyframeMedium)
                const keyframeSmall = [
                    {
                        translateX: -40,
                        translateY: 400,
                        duration: 2000,
                    }
                ]
                this.animeWater(this.smallArr, keyframeSmall)
            },
            animeWater(arr, keyframes) {
                for (let i = 0; i < arr.length; i++) {
                    anime({
                        targets: `.${arr[i].class}`,
                        keyframes: keyframes,
                        delay: arr[i].delay,
                        easing: 'linear',
                        loop: true
                    })
                }
            },
            animeMoon() {
                anime({
                    targets: '.moon',
                    translateX: 120,
                    translateY: -230,
                    duration: 800,
                    easing: 'linear'
                })
            },
            onLightning() {
                if (this.state === 'sunny') {
                    return
                }
                anime({
                    targets: '.lightning',
                    keyframes: [
                        {
                            opacity: 1,
                            duration: 300,
                        }, {
                            opacity: 0,
                            duration: 500,
                        }
                    ]
                })
                anime({
                    targets: '.container',
                    keyframes: [
                        {
                            backgroundColor: '#fff',
                            delay: 200,
                            easing: 'spring(50, 100, 50, 0)'
                        }, {
                            backgroundColor: '#1a2238',
                            duration: 1000,
                        }
                    ]
                })
                anime({
                    targets: '.moutain',
                    keyframes: [
                        {
                            opacity: 0.3,
                            delay: 300,
                            duration: 1000,
                        }, {
                            opacity: 1,
                            duration: 1000,
                        }
                    ]
                })
            },
            onBigRain() {
                if (this.state === 'sunny') {
                    this.state = 'rain'
                    anime({
                        targets: '.container',
                        backgroundColor: '#171c2b',
                        duration: 1500,
                        easing: 'linear'
                    })
                    anime({
                        targets: '.sun',
                        translateX: 120,
                        translateY: 230,
                        duration: 1000,
                        easing: 'linear'
                    })
                    anime({
                        targets: '.fil2',
                        fill: '#0f2850',
                        duration: 1500,
                        easing: 'linear'
                    })
                    anime({
                        targets: '.fil3',
                        fill: '#173563',
                        duration: 1500,
                        easing: 'linear'
                    })
                    anime({
                        targets: '.fil4',
                        fill: '#0f2850',
                        duration: 1500,
                        easing: 'linear'
                    })
                    this.animeMoon()
                }
                this.bigArr.forEach((v,i) => {
                    v.show = true
                })
                this.mediumArr.forEach((v,i) => {
                    v.show = true
                })
                this.smallArr.forEach((v,i) => {
                    v.show = true
                })
            },
            onSmallRain() {
                this.bigArr.forEach((v,i) => {
                    if (i % 2 === 0) {
                        v.show = false
                    }
                })
                this.mediumArr.forEach((v,i) => {
                    if (i % 2 === 0) {
                        v.show = false
                    }
                })
                this.smallArr.forEach((v,i) => {
                    if (i % 2 === 0) {
                        v.show = false
                    }
                })
            },
            onSunny() {
                this.state = 'sunny'
                this.bigArr.forEach((v,i) => {
                    v.show = false
                })
                this.mediumArr.forEach((v,i) => {
                    v.show = false
                })
                this.smallArr.forEach((v,i) => {
                    v.show = false
                })
                anime({
                    targets: '.container',
                    backgroundColor: '#fdf8d4',
                    duration: 1500,
                    easing: 'linear'
                })
                anime({
                    targets: '.fil2',
                    fill: '#6da75c',
                    duration: 1500,
                    easing: 'linear'
                })
                anime({
                    targets: '.fil3',
                    fill: '#4cb52e',
                    duration: 1500,
                    easing: 'linear'
                })
                anime({
                    targets: '.fil4',
                    fill: '#4f8d3d',
                    duration: 1500,
                    easing: 'linear'
                })
                anime({
                    targets: '.moon',
                    translateX: -120,
                    translateY: 230,
                    duration: 1000,
                    easing: 'linear'
                })
                anime({
                    targets: '.sun',
                    translateX: -120,
                    translateY: -230,
                    duration: 1000,
                    easing: 'linear'
                })
                // keyframe不支持loop,只能播放一遍
                anime({
                    targets: '.sun',
                    backgroundColor: '#fffc4b',
                    loop: true,
                    duration: 2000,
                    delay: 1000,
                    direction: 'alternate',
                    easing: 'linear'
                })
            }

        }
    })
</script>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值