效果图

完整代码
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>养个鱼</title> <style> html{font-size:5vh;}@media screen and (min-height:400px){html{font-size:19px;}}body{display:flex;justify-content:center;align-items:center;height:100vh;background-color:#eb80b1;}@keyframes swimming{0%,100%{transform:translateX(0);}22.5%{transform:translateX(2.5rem) skewY(-5deg);}45%{transform:translateX(6rem) translateY(-1rem) skewY(5deg);}55%{transform:translateX(5rem) translateY(-0.5rem) scaleX(-1);}95%{transform:translateX(0) scaleX(-1) skewY(10deg);}}@keyframes dead{0%,100%{transform:translateX(2.5rem);}22.5%{transform:translateX(2.5rem) translateY(-1rem) skewY(-5deg);}45%{transform:translateX(2.5rem) skewY(5deg);}55%{transform:translateX(2.5rem) translateY(-1rem) skewY(-5deg);}95%{transform:translateX(2.5rem) skewY(5deg);}}.fishbowl{position:relative;width:15rem;height:15rem;}.fishbowl__background{position:relative;width:100%;height:100%;border-radius:50% 50% 40% 40%;background:linear-gradient(transparent 10%,#fff 150%);border-bottom:1px solid #fff;}.fishbowl:before{content:'';position:absolute;bottom:9.5rem;left:2rem;width:100%;height:30%;border-radius:50%;box-shadow:-4rem 10rem 1rem 0 rgba(0,0,56,0.3);transform:rotate(5deg);}.fishbowl:after{content:'';position:absolute;top:12.5%;left:2.5%;width:95%;height:85%;border-radius:40%;background:linear-gradient(135deg,transparent 50%,#fff 150%);}.fishbowl__bottom{position:absolute;bottom:5%;left:17.5%;width:65%;height:20%;border-radius:50%;background:linear-gradient(#f5fccd,#ff7d66 200%);}.fishbowl__decoration{position:absolute;top:20%;left:5%;width:90%;height:75%;}.fishbowl__seaweed{position:absolute;width:0;height:0;border-left:0.5rem solid transparent;border-right:0.5rem solid transparent;border-bottom:5rem solid #80c0a1;}.fishbowl__seaweed--1{bottom:15%;right:20%;border-bottom:5rem solid #80c0a1;}.fishbowl__seaweed--2{bottom:10%;right:30%;border-bottom:8rem solid #80c0a1;}.fishbowl__seaweed--3{bottom:15%;right:40%;border-bottom:6rem solid #80c0a1;}.fishbowl__water{position:absolute;bottom:5%;left:5%;width:90%;height:80%;border-radius:40% 40% 4.8rem 4.8rem;transition:height 0.3s ease;overflow:hidden;}.fishbowl__water-color{position:absolute;bottom:0;left:0;width:100%;height:calc(1% * var(--filling,90));background:linear-gradient(transparent -50%,#419197 250%);border-radius:20% 20% 4rem 4rem;transition:height 0.5s linear;}.fishbowl__water-color:after{content:'';position:absolute;top:0;left:5%;width:90%;height:3rem;border-radius:50%;background:linear-gradient(transparent 0%,#419197 250%);box-shadow:inset 0 -1px 0 0 rgba(255,255,255,0.5);}.fishbowl__top{position:absolute;top:5%;left:15%;width:70%;height:20%;border-radius:50%;box-shadow:0 2px 2px 3px rgba(255,255,255,0.3);}.fishbowl__fish{position:absolute;bottom:25%;left:18%;width:2rem;height:1rem;border-radius:50%;background:linear-gradient(#fff -200%,orange);box-shadow:0 2rem 4px -2px rgba(0,0,56,0.3);transition-property:bottom,transform,box-shadow;transition-duration:1s;transition-timing-function:ease;animation:swimming;animation-duration:5s;animation-iteration-count:infinite;animation-fill-mode:forwards;}.fishbowl__fish:after{content:'';position:absolute;top:15%;right:15%;width:0.25rem;height:0.25rem;border-radius:50%;background:radial-gradient(circle at 0 0,#fff -100%,#12486b);transition:height 0.5s ease;}.fishbowl__fish--dying{bottom:10%;box-shadow:0 1rem 4px -2px rgba(0,0,56,0.3);}.fishbowl__fish--dying .fishbowl__fish-tail{box-shadow:0 1rem 4px -2px rgba(0,0,56,0.3);}.fishbowl__fish--dead{animation:dead;animation-duration:2s;animation-iteration-count:2;animation-fill-mode:forwards;box-shadow:none;}.fishbowl__fish--dead .fishbowl__fish-tail{box-shadow:none;}.fishbowl__fish--dead:after{height:0.125rem;}.fishbowl__fish--floating{bottom:max(calc(var(--filling,0) * 1% - 15%),10%);transform:translateX(2.5rem);animation:none;box-shadow:none;}.fishbowl__fish--floating:after{height:2px;}.fishbowl__fish--floating .fishbowl__fish-tail{box-shadow:none;}.fishbowl__fish-tail{position:absolute;top:0;left:-0.75rem;width:0;height:0;border-top:0.5rem solid transparent;border-bottom:0.5rem solid transparent;border-left:0.94rem solid orange;box-shadow:0 2rem 4px -2px rgba(0,0,56,0.3);transition:box-shadow 1s ease;}.fishbowl__pool{position:absolute;right:0;bottom:-5%;width:50%;height:15%;border-radius:50%;background:linear-gradient(#fff -100%,#419197);opacity:0.5;}.fishbowl__pool:after{content:'';position:absolute;top:25%;left:25%;width:50%;height:50%;border-right:2px solid #fff;border-radius:50%;animation:wave;animation-duration:3s;animation-iteration-count:infinite;opacity:0.5;}@keyframes wave{from{top:25%;left:25%;width:50%;height:50%;}to{top:10%;left:10%;width:80%;height:80%;}}.fishbowl__tap{position:absolute;bottom:0;left:-3rem;width:12rem;height:15.9rem;cursor:pointer;}.fishbowl__tap--active .fishbowl__tap-stream{animation:stream;animation-duration:0.5s;}@keyframes stream{0%{height:0;}50%{top:2rem;height:calc(14rem - var(--filling) * 0.1rem);}100%{top:calc(2rem + 14rem - var(--filling) * 0.1rem);height:0;}}.fishbowl__tap--active .fishbowl__tap-handle{animation:handle;animation-duration:0.5s;}@keyframes handle{from{transform:rotate(45deg);}to{transform:rotate(405deg);}}.fishbowl__tap-base{position:absolute;bottom:0;left:0;width:2rem;height:14rem;border-radius:0 0 1.2rem 1.2rem;box-shadow:inset -1px -1px 0 0px rgba(255,255,255,0.5);background:linear-gradient(#919ea3,#66777f 150%);}.fishbowl__tap-base:before{content:'';position:absolute;z-index:-1;bottom:4rem;right:15rem;width:100%;height:100%;border-radius:1rem 1rem 0 0;box-shadow:-4rem 10rem 1rem 0 rgba(0,0,56,0.3);transform:rotate(-70deg);}.fishbowl__tap-base:after{content:'';position:absolute;bottom:0;left:0;width:100%;height:100%;background:linear-gradient( 90deg,transparent,rgba(255,255,255,0.4) 60%,transparent 200% );border-radius:0 0 1.2rem 1.2rem;}.fishbowl__tap-handle{position:absolute;left:-1rem;bottom:6rem;width:2rem;height:2rem;border-radius:0.2rem;border-top:1px solid #fff;border-right:1px solid rgba(255,255,255,0.5);background:radial-gradient(circle at 0 0,#fff -100%,#919ea3);transform:rotate(45deg);}.fishbowl__tap-handle:after{content:'';position:absolute;top:0.5rem;left:0.5rem;width:1rem;height:1rem;border-radius:50%;background:radial-gradient(circle at 0 0,#fff -200%,#419197);}.fishbowl__tap-stream{position:absolute;top:2rem;left:6.25rem;width:1.5rem;height:0;background:linear-gradient(#fff -100%,#419197);border-radius:1rem;opacity:0.3;}.fishbowl__tap-head{position:absolute;top:0;left:0;width:4rem;height:0rem;border-radius:50% 50% 0 0;border-top:2rem solid #919ea3;border-left:2rem solid #919ea3;border-right:2rem solid #919ea3;box-shadow:1px -1px 0 0 #fff;}.fishbowl__tap-head:before{content:'';position:absolute;z-index:-1;bottom:3rem;right:23rem;width:150%;height:200%;box-shadow:-4rem 10rem 1rem 1rem rgba(0,0,56,0.3);transform:rotate(2deg) skewX(60deg);}.fishbowl__tap-head:after{content:'';position:absolute;top:-2rem;left:-2rem;width:8rem;height:2rem;background:linear-gradient( transparent,rgba(255,255,255,0.4) 60%,transparent 200% );border-radius:50% 50% 0 50%;}.fishbowl__tap-end{position:absolute;left:6rem;top:1.5rem;width:2rem;height:1rem;border-radius:50%;background:linear-gradient(#fff -70%,#919ea3);}.fishbowl__tap-text{position:absolute;top:4rem;left:-6rem;color:#fff;font-family:'Arial',sans-serif;font-size:0.875rem;}.fishbowl__tap-text:after{content:'';position:absolute;bottom:-1rem;right:-1rem;width:2rem;height:1px;background-color:#fff;transform:rotate(45deg);}@media screen and (max-width:420px){.fishbowl{left:13%;}.fishbowl__tap-text{width:2rem;top:1rem;left:-3.5rem;}}</style> </head> <body> <div id="fishbowl"class="fishbowl"><div class="fishbowl__pool"></div><div class="fishbowl__background"></div><div class="fishbowl__bottom"></div><div class="fishbowl__decoration"><div class="fishbowl__seaweed fishbowl__seaweed--1"></div><div class="fishbowl__seaweed fishbowl__seaweed--2"></div><div class="fishbowl__seaweed fishbowl__seaweed--3"></div></div><div class="fishbowl__water"><div id="fish"class="fishbowl__fish"><div class="fishbowl__fish-tail"></div></div><div class="fishbowl__water-color"></div></div><div class="fishbowl__top"></div><div id="tap"class="fishbowl__tap"><div class="fishbowl__tap-base"></div><div class="fishbowl__tap-handle"></div><div class="fishbowl__tap-stream"></div><div class="fishbowl__tap-end"></div><div class="fishbowl__tap-head"></div><div class="fishbowl__tap-text">加点水吧</div></div></div> <script>let fill=90;let intervalId=null;const fishbowl=document.getElementById('fishbowl');const fish=document.getElementById('fish');const tap=document.getElementById('tap');const emptyingFn=()=>setInterval(()=>{fill=fill-1;fishbowl.style=`--filling:${fill}`;if(fill<=0){clearInterval(intervalId)}else if(fill<20){fish.classList.add('fishbowl__fish--dead')}else if(fill<50){fish.classList.add('fishbowl__fish--dying')}else{fish.classList.remove('fishbowl__fish--dying');fish.classList.remove('fishbowl__fish--dead')}},200);intervalId=emptyingFn();tap.addEventListener('click',()=>{tap.classList.add('fishbowl__tap--active');setTimeout(()=>tap.classList.remove('fishbowl__tap--active'),500);if(fill<=0){intervalId=emptyingFn();fish.classList.add('fishbowl__fish--floating')}fill=Math.min(fill+20,90)});</script> </body>
代码解释
HTML 部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>养个鱼</title>
<style>
/* 这里是大量的 CSS 样式定义,用于设置页面的布局、鱼缸和各种元素的样式、动画效果等 */
</style>
</head>
<body>
<div id="fishbowl" class="fishbowl">
<!-- 鱼缸的各个组成部分,如水池、背景、底部、装饰、水、鱼、水龙头等 -->
</div>
<script>
// 这里是 JavaScript 代码,用于控制鱼的状态和水的填充程度
let fill = 90;
let intervalId = null;
const fishbowl = document.getElementById('fishbowl');
const fish = document.getElementById('fish');
const tap = document.getElementById('tap');
const emptyingFn = () => setInterval(() => {
fill = fill - 1;
fishbowl.style = `--filling:${fill}`;
if (fill <= 0) {
clearInterval(intervalId)
} else if (fill < 20) {
fish.classList.add('fishbowl__fish--dead')
} else if (fill < 50) {
fish.classList.add('fishbowl__fish--dying')
} else {
fish.classList.remove('fishbowl__fish--dying');
fish.classList.remove('fishbowl__fish--dead')
}
}, 200);
intervalId = emptyingFn();
tap.addEventListener('click', () => {
tap.classList.add('fishbowl__tap--active');
setTimeout(() => tap.classList.remove('fishbowl__tap--active'), 500);
if (fill <= 0) {
intervalId = emptyingFn();
fish.classList.add('fishbowl__fish--floating')
}
fill = Math.min(fill + 20, 90)
});
</script>
</body>
</html>
CSS 部分
html{font-size:5vh;}
@media screen and (min-height:400px){html{font-size:19px;}}
body{display:flex;justify-content:center;align-items:center;height:100vh;background-color:#eb80b1;}
/* 以上部分设置了根元素和页面主体的一些基本样式 */
@keyframes swimming{...}
@keyframes dead{...}
/* 定义了鱼的游泳和死亡动画关键帧 */
.fishbowl{...}
/* 鱼缸的整体样式,包括位置、大小等 */
.fishbowl__background{...}
.fishbowl:before{...}
.fishbowl:after{...}
.fishbowl__bottom{...}
.fishbowl__decoration{...}
.fishbowl__seaweed{...}
/* 鱼缸的背景、装饰等元素的样式 */
.fishbowl__water{...}
.fishbowl__water-color{...}
.fishbowl__top{...}
/* 水和顶部的样式 */
.fishbowl__fish{...}
.fishbowl__fish:after{...}
.fishbowl__fish--dying{...}
.fishbowl__fish--dead{...}
.fishbowl__fish--floating{...}
.fishbowl__fish-tail{...}
/* 鱼的各种状态和尾巴的样式 */
.fishbowl__pool{...}
.fishbowl__pool:after{...}
/* 水池的样式和水池的动画 */
.fishbowl__tap{...}
.fishbowl__tap--active.fishbowl__tap-stream{...}
.fishbowl__tap--active.fishbowl__tap-handle{...}
.fishbowl__tap-base{...}
.fishbowl__tap-handle{...}
.fishbowl__tap-stream{...}
.fishbowl__tap-head{...}
.fishbowl__tap-end{...}
.fishbowl__tap-text{...}
/* 水龙头的各种样式和动画 */
@media screen and (max-width:420px){...}
/* 针对小屏幕的样式调整 */
这部分主要是通过大量的选择器和样式规则,精细地设置了鱼缸、鱼、水、水龙头等元素的外观、位置、动画效果等。
JavaScript 部分
let fill = 90;
let intervalId = null;
const fishbowl = document.getElementById('fishbowl');
const fish = document.getElementById('fish');
const tap = document.getElementById('tap');
const emptyingFn = () => setInterval(() => {
fill = fill - 1;
fishbowl.style = `--filling:${fill}`;
if (fill <= 0) {
clearInterval(intervalId)
} else if (fill < 20) {
fish.classList.add('fishbowl__fish--dead')
} else if (fill < 50) {
fish.classList.add('fishbowl__fish--dying')
} else {
fish.classList.remove('fishbowl__fish--dying');
fish.classList.remove('fishbowl__fish--dead')
}
}, 200);
intervalId = emptyingFn();
tap.addEventListener('click', () => {
tap.classList.add('fishbowl__tap--active');
setTimeout(() => tap.classList.remove('fishbowl__tap--active'), 500);
if (fill <= 0) {
intervalId = emptyingFn();
fish.classList.add('fishbowl__fish--floating')
}
fill = Math.min(fill + 20, 90)
});
这部分定义了一些变量和函数来控制水的减少、鱼的状态变化以及水龙头的点击事件。
定义了初始的水填充程度
fill为 90 ,并设置了一个定时器emptyingFn,每 200 毫秒减少fill的值,并根据fill的值更新鱼的状态。为水龙头元素添加了点击事件处理程序。点击时,水龙头显示激活状态一段时间,根据水的填充程度更新鱼的状态,并增加水的填充量(最多到 90)。
√完整版代码
1.关注公众号
2.后台留言”给鱼加水“即可

