有点累,不想说太多,以下是几个关键点,有兴趣自己看代码吧。
- 月亮是用工具画的,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>