😷😊🤺🤺🤺前期回顾
打造极简风格动效 —— 5 分钟轻松实现惊艳、震撼人心的视觉效果_彩色之外的博客-CSDN博客
😁
css动画 —— 把你喜欢css动画嵌入到浏览器中_css做的动画效果怎么嵌入网页_彩色之外的博客-CSDN博客
代码已上传资源
开头省略200字》……
念及此
直接上图
目录
😊 进度条
🥰 唯美登录背景
😎 蜡烛登录动画
🥵 火箭
😶🌫️ 时间翻盘
🤺 复选框动画
🤪 基础表单输入动画
😃 卡包动画
😷 流光扫过动画
🥶 酷炫标题
🤖 交互式评分
🐔 动态向上箭头
🐽 卸载对话框表情交互动画
✈️ 随机密码生成器代码
🧨 跟随设备切换主题
🚒 昼夜切换主题
❤️🩹 使用系统取色器
😃 右键动态菜单(已做边缘动画及边界外判断)
🎋 酷炫打字特效
🚆 方形抽奖
🚼 圆形抽奖
🛹 吸顶楼层导航
✈️ 困了盖坤
🛫 扣扣乐
🍰 签名回溯
🌍 翻盘抽奖
🪂 地址 🤺🤺🤺
🥰😉 谢谢观看
图例:
图片过多,不在开头一一展示了,下面图与源码会放在一起,代码设计原生JS、VUE3
😊 进度条
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>3款漂亮的html5+css3 3D百分比进度条动画特效</title>
<style>
@import url("https://fonts.googleapis.com/css?family=Nunito:400,900");
* {
box-sizing: border-box;
}
body,
html {
font-family: "Nunito", sans-serif;
background: linear-gradient(0deg, #EEEFED, #F9E3E9);
margin: 0;
padding: 0;
color: #4D5075;
font-weight: 300;
width: 100%;
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 7vh 15vw 0vh 15vw;
}
h1 {
text-align: center;
margin: 0 0 10vh 0;
font-size: 7vh;
font-weight: 500;
text-shadow: 0px -15px 70px rgba(77, 80, 117, 0.6);
}
h1 b {
font-weight: 900;
}
p {
text-align: center;
font-size: 1.3rem;
text-shadow: 10px 5px 25px rgba(77, 80, 117, 0.6);
margin-bottom: 8vh;
}
.third-bar-p {
margin-top: 7vh;
}
.perspective {
-webkit-perspective: 70vh;
perspective: 70vh;
text-align: center;
-webkit-perspective-origin: 50% 50%;
perspective-origin: 50% 50%;
position: relative;
transition: -webkit-transform 0.3s ease;
transition: transform 0.3s ease;
transition: transform 0.3s ease, -webkit-transform 0.3s ease;
}
.perspective:hover {
-webkit-transform: scale(1.04);
transform: scale(1.04);
}
.bar-input {
position: absolute;
height: 100%;
left: 0;
right: 0;
margin: auto;
opacity: 0;
}
#first-bar .bar-input {
width: 55vh;
}
#second-bar .bar-input {
width: 40vh;
}
#third-bar .bar-input {
width: 42vh;
}
.bar {
display: inline-block;
position: relative;
-webkit-transform: rotateX(55deg);
transform: rotateX(55deg);
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
}
.bar .bar-face {
display: inline-block;
width: 100%;
height: 100%;
position: absolute;
left: 0;
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
}
.bar .bar-face.front {
-webkit-transform: rotateX(-90deg);
transform: rotateX(-90deg);
}
.bar .bar-face.percentage:before {
height: 100%;
content: "";
display: block;
position: absolute;
bottom: 0;
margin: 0;
}
#first-bar .bar {
width: 55vh;
height: 7vh;
}
#first-bar .bar .bar-face {
background: rgba(255, 255, 255, 0.5);
}
#first-bar .bar .bar-face.floor {
box-shadow: 0 1.3em 1.2em -0.4em rgba(0, 0, 70, 0.25), 0 -2em 15em 0.5em #4d5075, 0 -0.75em 25em 10em rgba(255, 255, 255, 0.4);
}
#first-bar .bar .bar-face.percentage:before {
box-shadow: 0 1.6em 7em -0.3em rgba(149, 65, 105, 0.5);
}
#first-bar .bar .bar-face.roof {
-webkit-transform: translateZ(7vh);
transform: translateZ(7vh);
}
#first-bar .bar .bar-face.back {
-webkit-transform: rotateX(-90deg) translateZ(-7vh);
transform: rotateX(-90deg) translateZ(-7vh);
}
#first-bar .bar .bar-face.percentage:before {
background-color: rgba(149, 65, 105, 0.6);
}
#second-bar .bar {
width: 40vh;
height: 10vh;
}
#second-bar .bar .bar-face {
background: rgba(60, 75, 132, 0.5);
background-image: linear-gradient(90deg, rgba(134, 114, 146, 0.5), rgba(60, 75, 132, 0.1)), url("https://zephyo.github.io/22Days/code/5/graphics/stars.svg"), url("https://zephyo.github.io/22Days/code/5/graphics/stars2.svg");
background-repeat: repeat repeat;
}
#second-bar .bar .bar-face.floor {
box-shadow: 0 1.3em 1.2em -0.4em rgba(0, 0, 70, 0.25), 0 -2em 15em 0.5em #4d5075, 0 -0.75em 25em 10em rgba(255, 255, 255, 0.4);
}
#second-bar .bar .bar-face.percentage:before {
box-shadow: 0 1.6em 7em -0.3em rgba(200, 212, 250, 0.5);
}
#second-bar .bar .bar-face.roof {
-webkit-transform: translateZ(10vh);
transform: translateZ(10vh);
}
#second-bar .bar .bar-face.back {
-webkit-transform: rotateX(-90deg) translateZ(-10vh);
transform: rotateX(-90deg) translateZ(-10vh);
}
#second-bar .bar .bar-face.percentage:before {
background-image: url("https://zephyo.github.io/22Days/code/5/graphics/sky.png");
opacity: 0.9;
}
#third-bar .bar {
width: 42vh;
height: 8vh;
}
#third-bar .bar .bar-face {
background: rgba(232, 154, 173, 0.7);
}
#third-bar .bar .bar-face.floor {
box-shadow: 0 1.3em 1.2em -0.4em rgba(0, 0, 70, 0.25), 0 -2em 15em 0.5em #4d5075, 0 -0.75em 25em 10em rgba(255, 255, 255, 0.4);
}
#third-bar .bar .bar-face.percentage:before {
box-shadow: 0 1.6em 7em -0.3em rgba(236, 0, 113, 0.5);
}
#third-bar .bar .bar-face.roof {
-webkit-transform: translateZ(8vh);
transform: translateZ(8vh);
}
#third-bar .bar .bar-face.back {
-webkit-transform: rotateX(-90deg) translateZ(-8vh);
transform: rotateX(-90deg) translateZ(-8vh);
}
#third-bar .bar .bar-face.percentage:before {
background: linear-gradient(90deg, rgba(245, 239, 200, 0.5), #ec0071);
}
#third-bar .bar .indicator {
box-shadow: 0px 15px 35px rgba(236, 0, 113, 0.3);
background: #ec0071;
width: 8vh;
height: 8vh;
color: white;
-webkit-transform: translateY(9.6vh);
transform: translateY(9.6vh);
text-align: center;
font-size: 2.5vh;
font-weight: 900;
line-height: 8vh;
}
#third-bar .bar .indicator:before {
content: "";
position: absolute;
background: #ec0071;
left: 0;
right: 0;
margin: auto;
top: -6px;
width: 4vh;
height: 4vh;
z-index: -1;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
</style>
</head>
<body>
<script src="/demos/googlegg.js"></script>
<h1>Scalable <b>3D</b> Range Sliders</h1>
<main class="perspective" id="first-bar">
<section class="bar">
<div class="bar-face back percentage"></div>
<div class="bar-face floor percentage"></div>
<div class="bar-face roof percentage"></div>
<div class="bar-face front percentage"></div>
</section>
<input class="bar-input" type="range" min="0" max="101" value="64" />
</main>
<p>Simple Range</p>
<main class="perspective" id="second-bar">
<section class="bar">
<div class="bar-face back percentage"></div>
<div class="bar-face floor percentage"></div>
<div class="bar-face roof percentage"></div>
<div class="bar-face front percentage"></div>
</section>
<input class="bar-input" type="range" min="0" max="101" value="37" />
</main>
<p>Patterned Range</p>
<main class="perspective" id="third-bar">
<section class="bar">
<div class="bar-face back percentage"></div>
<div class="bar-face floor percentage"></div>
<div class="bar-face roof percentage"></div>
<div class="bar-face front percentage"></div>
<div class="indicator">89%</div>
</section>
<input class="bar-input" type="range" min="0" max="100" value="89" />
</main>
<p class="third-bar-p">Gradient Range with Indicator</p>
<script>
function initInputs() {
var allInputs = document.body.querySelectorAll(".bar-input");
for (var i = 0; i < allInputs.length; i++) {
var input = allInputs[i];
var barId = input.parentNode.id;
var styleEl = document.head.appendChild(document.createElement("style"));
if (i == allInputs.length - 1) {
//set indicator
var indicator = input.parentNode.querySelector('.bar .indicator');
setBarIndicator(barId, input, styleEl, indicator);
input.oninput = setBarIndicator.bind(this, barId, input, styleEl, indicator);
input.onchange = setBarIndicator.bind(this, barId, input, styleEl, indicator);
} else {
setBar(barId, input, styleEl);
input.oninput = setBar.bind(this, barId, input, styleEl);
input.onchange = setBar.bind(this, barId, input, styleEl);
}
}
}
function setBar(barId, input, styleEl) {
styleEl.innerHTML =
"#" + barId + " .bar-face.percentage:before {width:" + input.value + "%;}";
}
function setBarIndicator(barId, input, styleEl, indicatorEl) {
styleEl.innerHTML =
"#" + barId + " .bar-face.percentage:before {width:" + input.value + "%;}";
indicatorEl.style.marginLeft = (input.value - 10) + '%';
indicatorEl.textContent = input.value + '%';
}
initInputs();
</script>
</div>
</body>
</html>
🥰 唯美登录背景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML5 Canvas透明丝带飘动背景动画特效</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #000;
overflow: hidden;
}
.login-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div class="login-container"></div>
<script>
(function () {
let pi = Math.PI;
let pi2 = 2 * Math.PI;
this.Waves = function (holder, options) {
let Waves = this;
Waves.options = extend(options || {}, {
resize: false,
rotation: 45,
waves: 5,
width: 100,
hue: [11, 14],
amplitude: 0.5,
background: true,
preload: true,
speed: [0.004, 0.008],
debug: false,
fps: false,
});
Waves.waves = [];
Waves.holder = document.querySelector('.login-container');
Waves.canvas = document.createElement('canvas');
Waves.ctx = Waves.canvas.getContext('2d');
Waves.holder.appendChild(Waves.canvas);
Waves.hue = Waves.options.hue[0];
Waves.hueFw = true;
Waves.stats = new Stats();
Waves.resize();
Waves.init(Waves.options.preload);
if (Waves.options.resize)
window.addEventListener('resize', function () {
Waves.resize();
}, false);
};
Waves.prototype.init = function (preload) {
let Waves = this;
let options = Waves.options;
for (let i = 0; i < options.waves; i++)
Waves.waves[i] = new Wave(Waves);
if (preload) Waves.preload();
};
Waves.prototype.preload = function () {
let Waves = this;
let options = Waves.options;
for (let i = 0; i < options.waves; i++) {
Waves.updateColor();
for (let j = 0; j < options.width; j++) {
Waves.waves[i].update();
}
}
};
Waves.prototype.render = function () {
let Waves = this;
let ctx = Waves.ctx;
let options = Waves.options;
Waves.updateColor();
Waves.clear();
if (Waves.options.debug) {
ctx.beginPath();
ctx.strokeStyle = '#f00';
ctx.arc(Waves.centerX, Waves.centerY, Waves.radius, 0, pi2);
ctx.stroke();
}
if (Waves.options.background) {
Waves.background();
}
each(Waves.waves, function (wave, i) {
wave.update();
wave.draw();
});
};
Waves.prototype.animate = function () {
let Waves = this;
Waves.render();
// if (Waves.options.fps) {
// Waves.stats.log();
// Waves.ctx.font = '12px Arial';
// Waves.ctx.fillStyle = '#fff';
// Waves.ctx.fillText(Waves.stats.fps() + ' FPS', 10, 22);
// }
window.requestAnimationFrame(Waves.animate.bind(Waves));
};
Waves.prototype.clear = function () {
let Waves = this;
Waves.ctx.clearRect(0, 0, Waves.width, Waves.height);
};
Waves.prototype.background = function () {
let Waves = this;
let ctx = Waves.ctx;
let gradient = Waves.ctx.createLinearGradient(0, 0, 0, Waves.height);
gradient.addColorStop(0, '#000');
gradient.addColorStop(1, Waves.color);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, Waves.width, Waves.height);
};
Waves.prototype.resize = function () {
let Waves = this;
let width = Waves.holder.offsetWidth;
let height = Waves.holder.offsetHeight;
Waves.scale = window.devicePixelRatio || 1;
Waves.width = width * Waves.scale;
Waves.height = height * Waves.scale;
Waves.canvas.width = Waves.width;
Waves.canvas.height = Waves.height;
Waves.canvas.style.width = width + 'px';
Waves.canvas.style.height = height + 'px';
Waves.radius = Math.sqrt(Math.pow(Waves.width, 2) + Math.pow(Waves.height, 2)) / 2;
Waves.centerX = Waves.width / 2;
Waves.centerY = Waves.height / 2;
//Waves.radius /= 2; // REMOVE FOR FULLSREEN
};
Waves.prototype.updateColor = function () {
let Waves = this;
Waves.hue += (Waves.hueFw) ? 0.01 : -0.01;
if (Waves.hue > Waves.options.hue[1] && Waves.hueFw) {
Waves.hue = Waves.options.hue[1];
Waves.Waves = false;
} else if (Waves.hue < Waves.options.hue[0] && !Waves.hueFw) {
Waves.hue = Waves.options.hue[0];
Waves.Waves = true;
}
let a = Math.floor(127 * Math.sin(0.3 * Waves.hue + 0) + 128);
let b = Math.floor(127 * Math.sin(0.3 * Waves.hue + 2) + 128);
let c = Math.floor(127 * Math.sin(0.3 * Waves.hue + 4) + 128);
Waves.color = 'rgba(' + a + ',' + b + ',' + c + ', 0.1)';
};
function Wave(Waves) {
let Wave = this;
let speed = Waves.options.speed;
Wave.Waves = Waves;
Wave.Lines = [];
Wave.angle = [
rnd(pi2),
rnd(pi2),
rnd(pi2),
rnd(pi2)
];
Wave.speed = [
rnd(speed[0], speed[1]) * rnd_sign(),
rnd(speed[0], speed[1]) * rnd_sign(),
rnd(speed[0], speed[1]) * rnd_sign(),
rnd(speed[0], speed[1]) * rnd_sign(),
];
return Wave;
}
Wave.prototype.update = function () {
let Wave = this;
let Lines = Wave.Lines;
let color = Wave.Waves.color;
Lines.push(new Line(Wave, color));
if (Lines.length > Wave.Waves.options.width) {
Lines.shift();
}
};
Wave.prototype.draw = function () {
let Wave = this;
let Waves = Wave.Waves;
let ctx = Waves.ctx;
let radius = Waves.radius;
let radius3 = radius / 3;
let x = Waves.centerX;
let y = Waves.centerY;
let rotation = dtr(Waves.options.rotation);
let amplitude = Waves.options.amplitude;
let debug = Waves.options.debug;
let Lines = Wave.Lines;
each(Lines, function (line, i) {
if (debug && i > 0) return;
let angle = line.angle;
let x1 = x - radius * Math.cos(angle[0] * amplitude + rotation);
let y1 = y - radius * Math.sin(angle[0] * amplitude + rotation);
let x2 = x + radius * Math.cos(angle[3] * amplitude + rotation);
let y2 = y + radius * Math.sin(angle[3] * amplitude + rotation);
let cpx1 = x - radius3 * Math.cos(angle[1] * amplitude * 2);
let cpy1 = y - radius3 * Math.sin(angle[1] * amplitude * 2);
let cpx2 = x + radius3 * Math.cos(angle[2] * amplitude * 2);
let cpy2 = y + radius3 * Math.sin(angle[2] * amplitude * 2);
ctx.strokeStyle = (debug) ? '#fff' : line.color;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2);
ctx.stroke();
if (debug) {
ctx.strokeStyle = '#fff';
ctx.globalAlpha = 0.3;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(cpx1, cpy1);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(cpx2, cpy2);
ctx.stroke();
ctx.globalAlpha = 1;
}
});
};
function Line(Wave, color) {
let Line = this;
let angle = Wave.angle;
let speed = Wave.speed;
Line.angle = [
Math.sin(angle[0] += speed[0]),
Math.sin(angle[1] += speed[1]),
Math.sin(angle[2] += speed[2]),
Math.sin(angle[3] += speed[3])
];
Line.color = color;
}
function Stats() {
this.data = [];
}
Stats.prototype.time = function () {
return (performance || Date)
.now();
};
Stats.prototype.log = function () {
if (!this.last) {
this.last = this.time();
return 0;
}
this.new = this.time();
this.delta = this.new - this.last;
this.last = this.new;
this.data.push(this.delta);
if (this.data.length > 10)
this.data.shift();
};
Stats.prototype.fps = function () {
let fps = 0;
each(this.data, function (data, i) {
fps += data;
});
return Math.round(1000 / (fps / this.data.length));
};
function each(items, callback) {
for (let i = 0; i < items.length; i++) {
callback(items[i], i);
}
}
function extend(options, defaults) {
for (let key in options)
if (defaults.hasOwnProperty(key))
defaults[key] = options[key];
return defaults;
}
function dtr(deg) {
return deg * pi / 180;
}
function rtd(rad) {
return rad * 180 / pi;
}
function diagonal_angle(w, h) {
let a = Math.atan2(h, w) * 1.27325;
return a;
}
function rnd(a, b) {
if (arguments.length == 1)
return Math.random() * a;
return a + Math.random() * (b - a);
}
function rnd_sign() {
return (Math.random() > 0.5) ? 1 : -1;
}
})();
let waves = new Waves('.login-container ', {
fps: true,
waves: 3,
width: 200,
});
waves.animate();
</script>
</body>
</html>
😎 蜡烛登录动画
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CSS3可爱卡通蜡烛交互式动画特效</title>
<style>
body {
background: #13b280;
/* 背景 */
animation: change-background 5s infinite linear;
}
.wrapper {
position: absolute;
left: 80%;
top: 70%;
transform: scale(1.5, 1.5) translate(-50%, -50%);
}
.floor {
position: absolute;
left: 50%;
top: 50%;
width: 350px;
height: 5px;
background: #673C63;
transform: translate(-50%, -50%);
box-shadow: 0px 2px 5px #111;
z-index: 2;
}
.candles {
position: absolute;
left: 50%;
top: 50%;
width: 250px;
height: 150px;
transform: translate(-50%, -100%);
z-index: 1;
}
.candle1 {
position: absolute;
left: 50%;
top: 50%;
width: 35px;
height: 100px;
background: #fff;
border: 3px solid #673C63;
border-bottom: 0px;
border-radius: 3px;
transform-origin: center right;
transform: translate(60%, -25%);
box-shadow: -2px 0px 0px #95c6f2 inset;
/* 高蜡烛向上伸展动画 */
animation: expand-body 5s infinite linear;
}
.candle1__stick,
.candle2__stick {
position: absolute;
left: 50%;
top: 0%;
width: 3px;
height: 15px;
background: #673C63;
border-radius: 8px;
transform: translate(-50%, -100%);
}
.candle2__stick {
height: 12px;
transform-origin: bottom center;
/* 蜡烛芯 */
animation: stick-animation 5s infinite linear;
}
.candle1__eyes,
.candle2__eyes {
position: absolute;
left: 50%;
top: 0%;
width: 35px;
height: 30px;
transform: translate(-50%, 0%);
}
.candle1__eyes-one {
position: absolute;
left: 30%;
top: 20%;
width: 5px;
height: 5px;
border-radius: 100%;
background: #673C63;
transform: translate(-70%, 0%);
/* 高蜡烛眼睛 */
animation: blink-eyes 5s infinite linear;
}
.candle1__eyes-two {
position: absolute;
left: 70%;
top: 20%;
width: 5px;
height: 5px;
border-radius: 100%;
background: #673C63;
transform: translate(-70%, 0%);
/* 高蜡烛眼睛 */
animation: blink-eyes 5s infinite linear;
}
.candle1__mouth {
position: absolute;
left: 40%;
top: 20%;
width: 0px;
height: 0px;
border-radius: 20px;
background: #673C63;
transform: translate(-50%, -50%);
/* 高蜡烛嘴吹气 */
animation: uff 5s infinite linear;
}
.candle__smoke-one {
position: absolute;
left: 30%;
top: 50%;
width: 30px;
height: 3px;
background: grey;
transform: translate(-50%, -50%);
/* 气体动画 向左*/
animation: move-left 5s infinite linear;
}
.candle__smoke-two {
position: absolute;
left: 30%;
top: 40%;
width: 10px;
height: 10px;
border-radius: 10px;
background: grey;
transform: translate(-50%, -50%);
/* 气体动画 向上 */
animation: move-top 5s infinite linear;
}
.candle2 {
position: absolute;
left: 20%;
top: 65%;
width: 42px;
height: 60px;
background: #fff;
border: 3px solid #673C63;
border-bottom: 0px;
border-radius: 3px;
transform: translate(60%, -15%);
transform-origin: center right;
box-shadow: -2px 0px 0px #95c6f2 inset;
/* 矮蜡烛 涨红脸 */
animation: shake-left 5s infinite linear;
}
.candle2__eyes-one {
position: absolute;
left: 30%;
top: 50%;
width: 5px;
height: 5px;
display: inline-block;
border: 0px solid #673C63;
border-radius: 100%;
float: left;
background: #673C63;
transform: translate(-80%, 0%);
/* 矮蜡烛 眼睛 */
animation: changeto-lower 5s infinite linear;
}
.candle2__eyes-two {
position: absolute;
left: 70%;
top: 50%;
width: 5px;
height: 5px;
display: inline-block;
border: 0px solid #673C63;
border-radius: 100%;
float: left;
background: #673C63;
transform: translate(-80%, 0%);
/* 矮蜡烛眼睛 */
animation: changeto-greater 5s infinite linear;
}
.light__wave {
position: absolute;
top: 35%;
left: 35%;
width: 75px;
height: 75px;
border-radius: 100%;
z-index: 0;
transform: translate(-25%, -50%) scale(2.5, 2.5);
border: 2px solid rgba(255, 255, 255, 0.2);
/* 高蜡烛 向上伸张 */
animation: expand-light 5s infinite linear;
}
.candle2__fire {
position: absolute;
top: 50%;
left: 40%;
display: block;
width: 16px;
height: 20px;
background-color: red;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
background: #FF9800;
transform: translate(-50%, -50%);
/* 火焰 */
animation: dance-fire 5s infinite linear;
}
@keyframes blink-eyes {
0%,
35% {
opacity: 1;
transform: translate(-70%, 0%);
}
36%,
39% {
opacity: 0;
transform: translate(-70%, 0%);
}
40% {
opacity: 1;
transform: translate(-70%, 0%);
}
50%,
65% {
transform: translate(-140%, 0%);
}
66% {
transform: translate(-70%, 0%);
}
}
@keyframes expand-body {
0%,
40% {
transform: scale(1, 1) translate(60%, -25%);
}
45%,
55% {
transform: scale(1.1, 1.1) translate(60%, -28%);
}
60% {
transform: scale(0.89, 0.89) translate(60%, -25%);
}
65% {
transform: scale(1, 1) translate(60%, -25%);
}
70% {
transform: scale(0.95, 0.95) translate(60%, -25%);
}
75% {
transform: scale(1, 1) translate(60%, -25%);
}
}
@keyframes uff {
0%,
40% {
width: 0px;
height: 0px;
}
50%,
54% {
width: 15px;
height: 15px;
left: 30%;
}
59% {
width: 5px;
height: 5px;
left: 20%;
}
62% {
width: 2px;
height: 2px;
left: 20%;
}
67% {
width: 0px;
height: 0px;
left: 30%;
}
}
@keyframes change-background {
0%,
59%,
98%,
100% {
background: #13b280;
}
61%,
97% {
background: #F8AE39;
}
}
@keyframes move-left {
0%,
59%,
100% {
width: 0px;
left: 40%;
}
60% {
width: 30px;
left: 30%;
}
68% {
width: 0px;
left: 20%;
}
}
@keyframes move-top {
0%,
64%,
100% {
width: 0px;
height: 0px;
top: 0%;
}
65% {
width: 10px;
height: 10px;
top: 40%;
left: 40%;
}
80% {
width: 0px;
height: 0px;
top: 20%;
}
}
@keyframes shake-left {
0%,
40% {
left: 20%;
transform: translate(60%, -15%);
}
50%,
54% {
left: 20%;
transform: translate(60%, -15%);
}
59% {
left: 20%;
transform: translate(60%, -15%);
}
62% {
left: 18%;
transform: translate(60%, -15%);
}
65% {
left: 21%;
transform: translate(60%, -15%);
}
67% {
left: 20%;
transform: translate(60%, -15%);
}
75% {
left: 20%;
transform: scale(1.15, 0.85) translate(60%, -15%);
background: #fff;
border-color: #673C63;
}
91% {
left: 20%;
transform: scale(1.18, 0.82) translate(60%, -10%);
background: #F44336;
border-color: #F44336;
box-shadow: -2px 0px 0px #F44336 inset;
}
92% {
left: 20%;
transform: scale(0.85, 1.15) translate(60%, -15%);
}
95% {
left: 20%;
transform: scale(1.05, 0.95) translate(60%, -15%);
}
97% {
left: 20%;
transform: scale(1, 1) translate(60%, -15%);
}
}
@keyframes stick-animation {
0%,
40% {
left: 50%;
top: 0%;
transform: translate(-50%, -100%);
}
50%,
54% {
left: 50%;
top: 0%;
transform: translate(-50%, -100%);
}
59% {
left: 50%;
top: 0%;
transform: translate(-50%, -100%);
}
62% {
left: 50%;
top: 0%;
transform: rotateZ(-15deg) translate(-50%, -100%);
}
65% {
left: 50%;
top: 0%;
transform: rotateZ(15deg) translate(-50%, -100%);
}
70% {
left: 50%;
top: 0%;
transform: rotateZ(-5deg) translate(-50%, -100%);
}
72% {
left: 50%;
top: 0%;
transform: rotateZ(5deg) translate(-50%, -100%);
}
74%,
84% {
left: 50%;
top: 0%;
transform: rotateZ(0deg) translate(-50%, -100%);
}
85% {
transform: rotateZ(180deg) translate(0%, 120%);
}
92% {
left: 50%;
top: 0%;
transform: translate(-50%, -100%);
}
}
@keyframes expand-light {
10%,
29%,
59%,
89% {
transform: translate(-25%, -50%) scale(0, 0);
border: 2px solid rgba(255, 255, 255, 0);
}
90%,
20%,
50% {
transform: translate(-25%, -50%) scale(1, 1);
}
95%,
96%,
26%,
27%,
56%,
57% {
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid rgba(255, 255, 255, 0.5);
}
0%,
28%,
58%,
100% {
transform: translate(-25%, -50%) scale(2.5, 2.5);
border: 2px solid rgba(255, 255, 255, 0.2);
}
}
@keyframes dance-fire {
59%,
89% {
left: 40%;
width: 0px;
height: 0px;
}
90%,
0%,
7%,
15%,
23%,
31%,
39%,
47%,
55% {
left: 40.8%;
width: 16px;
height: 20px;
background: #FFC107;
}
94%,
3%,
11%,
19%,
27%,
35%,
43%,
51%,
58% {
left: 41.2%;
width: 16px;
height: 20px;
background: #FF9800;
}
}
@keyframes changeto-lower {
0%,
70%,
90% {
padding: 0px;
display: inline-block;
border-radius: 100%;
background: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-90%, 0%);
}
71%,
89% {
background: none;
border: solid #673C63;
border-radius: 0px;
border-width: 0 2px 2px 0;
display: inline-block;
padding: 1px;
float: left;
transform-origin: bottom left;
transform: rotate(-45deg) translate(-50%, -65%);
-webkit-transform: rotate(-45deg) translate(-50%, -65%);
}
}
@keyframes changeto-greater {
0%,
70%,
90% {
top: 50%;
padding: 0px;
display: inline-block;
border-radius: 100%;
background: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-80%, 0%);
}
71%,
89% {
top: 30%;
background: none;
border: solid #673C63;
border-radius: 0px;
border-width: 0 2px 2px 0;
display: inline-block;
padding: 1px;
float: left;
transform-origin: bottom left;
transform: rotate(135deg) translate(-80%, 20%);
-webkit-transform: rotate(135deg) translate(-80%, 20%);
}
}
</style>
</head>
<body>
<div class="wrapper">
<div class="candles">
<div class="light__wave"></div>
<div class="candle1">
<div class="candle1__body">
<div class="candle1__eyes">
<span class="candle1__eyes-one"></span>
<span class="candle1__eyes-two"></span>
</div>
<div class="candle1__mouth"></div>
</div>
<div class="candle1__stick"></div>
</div>
<div class="candle2">
<div class="candle2__body">
<div class="candle2__eyes">
<div class="candle2__eyes-one"></div>
<div class="candle2__eyes-two"></div>
</div>
</div>
<div class="candle2__stick"></div>
</div>
<div class="candle2__fire"></div>
<div class="sparkles-one"></div>
<div class="sparkles-two"></div>
<div class="candle__smoke-one">
</div>
<div class="candle__smoke-two">
</div>
</div>
<div class="floor">
</div>
</div>
</body>
</html>
🥵 火箭
源码仓库在后面,文件比较多,会影响阅读,
😶🌫️ 时间翻盘
原生js、vue版本,请移步仓库里
🤺 复选框动画
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CSS3 SVG点击复选框勾选动画特效</title>
<style>
@import url("https://fonts.googleapis.com/css?family=Exo");
html {
font-size: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #00acc1;
font-family: 'Exo', sans-serif;
}
input[type="checkbox"] {
display: none;
}
input[type="checkbox"] + .label {
position: relative;
padding-left: 1.7em;
color: #ffffff;
font-size: 3.125rem;
cursor: pointer;
}
input[type="checkbox"] + .label::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 1em;
height: 1em;
border: 3px solid #ffffff;
border-radius: 0.1em;
}
input[type="checkbox"] + .label::after {
content: '';
position: absolute;
top: 0.1em;
left: 0.1em;
width: 0.92em;
height: 0.92em;
transform: scale(0);
background-color: #ffffff;
border-radius: 0.02em;
transition: all 0.2s linear;
opacity: 0;
}
input[type="checkbox"] + .label svg {
position: absolute;
top: 0.2em;
left: 0.12em;
z-index: 1;
}
input[type="checkbox"] + .label #check-icon {
stroke: #00acc1;
stroke-dasharray: 36;
stroke-dashoffset: 36;
}
input[type="checkbox"]:checked + .label::after {
transform: scale(1);
opacity: 1;
}
input[type="checkbox"]:checked + .label #check-icon {
animation-name: check-animation;
animation-duration: 0.2s;
animation-delay: 0.4s;
animation-fill-mode: forwards;
animation-timing-function: cubic-bezier(1, 0.12, 0.96, 0.62);
}
@keyframes check-animation {
0% {
stroke-dashoffset: 36;
}
100% {
stroke-dashoffset: 0;
}
}
</style>
</head>
<body>
<input type="checkbox" id="check-me">
<label for="check-me" class="label">
Check Me!
<svg viewBox="-9 -9 43 35.191" width="43" height="35">
<path id="check-icon" d=" M 0 9 Q 8 17 8 17 Q 8 17 25 0" fill="none" stroke-width="6" stroke="rgb(0,0,0)" stroke-linecap="round" />
</svg>
</label>
</body>
</html>
🤪 基础表单输入动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>form input动画</title>
<style>
input[type='text'],
input[type='password'] {
height: 40px;
width: 200px;
background: transparent;
border: none;
border-bottom: 1px solid #999;
text-indent: 20px;
transition: 0.3;
outline: none;
}
input[type='text']:hover,
input[type='password']:hover {
border-color: #42b983;
}
input[type='text']:focus,
input[type='password']:focus {
border-bottom-color: #f1190d;
}
input[type='password']::-webkit-input-placeholder,
input[type='text']::-webkit-input-placeholder {
transition: 0.5s;
font-size: 14px;
transform-origin: top left;
}
input[type='password']:focus::-webkit-input-placeholder,
input[type='text']:focus::-webkit-input-placeholder {
transform: scale(0.8) translateY(-10px);
}
</style>
</head>
<body>
<input type="text" placeholder="请输入您的姓名" />
<input type="password" placeholder="请输入您的密码" />
</body>
</html>
😃 卡包动画
<template>
<div>
<div class="tariffCards">
<div class="economy">
<img
src="https://creditcard.ecitic.com/2019_gw/images/g-logo.png"
alt="中信银行"
height="74"
/>
<h3>中信银行</h3>
<span>zhongxin bank</span>
</div>
<div class="premiumeconomy">
<img
src="https://www.bankofchina.com/images/boc2013_logo.png"
alt="中国银行"
height="30"
/>
<h3>中国银行</h3>
<span>chinease bank</span>
</div>
<div class="business">
<img
src="http://www.jsbchina.cn/data/tosend/resource/upload/20201127/f97a668d-3cce-435c-8cb5-ca4e69b86aec.jpg"
alt="交通银行"
height="74"
/>
<h3>交通银行</h3>
<span>jiaotong bank</span>
</div>
<div class="first">
<img
src="http://www.jsbchina.cn/CN/index.html"
alt="江苏银行"
height="74"
/>
<h3>江苏银行</h3>
<span>jiangsu bank</span>
</div>
</div>
</div>
</template>
<style scoped>
.tariffCards {
position: absolute;
top: 50%;
left: 50%;
margin: -180px 0 0 -140px;
user-select: none;
transform: translate3d(0, 0, 0);
transform-style: preserve-3d;
}
.tariffCards:after {
position: absolute;
bottom: -27px;
left: 5%;
content: '';
width: 65%;
height: 10px;
border-radius: 100%;
background-image: radial-gradient(
rgba(34, 50, 84, 0.04),
rgba(34, 50, 84, 0)
);
}
.tariffCards > div {
position: relative;
width: 280px;
height: 140px;
border-radius: 12px;
color: white;
transform: rotateX(45deg) rotateY(-15deg) rotate(45deg);
transition: all 0.4s ease;
overflow: hidden;
cursor: pointer;
}
.tariffCards > div:after {
position: absolute;
top: -70px;
left: 0;
content: '';
width: 200%;
height: 200%;
background-image: linear-gradient(
60deg,
rgba(255, 255, 255, 0) 20%,
rgba(255, 255, 255, 0.1),
rgba(255, 255, 255, 0) 80%
);
transform: translateX(-100%);
}
.tariffCards > div img {
margin-top: 15px;
pointer-events: none;
}
.tariffCards > div h3 {
position: absolute;
bottom: 28px;
left: 15px;
font-size: 18px;
font-weight: 800;
}
.tariffCards > div span {
position: absolute;
font-weight: 700;
bottom: 15px;
left: 15px;
font-size: 12px;
font-weight: 600;
opacity: 0.8;
}
.tariffCards > div.economy {
margin-top: 0;
z-index: 3;
background-color: #8063e1;
background-image: linear-gradient(135deg, #bd7be8, #8063e1);
box-shadow: 20px 20px 60px rgba(34, 50, 84, 0.5), 1px 1px 0px 1px #8063e1;
}
.tariffCards > div.premiumeconomy {
margin-top: -70px;
z-index: 2;
background-color: #3f58e3;
background-image: linear-gradient(135deg, #7f94fc, #3f58e3);
box-shadow: 20px 20px 60px rgba(34, 50, 84, 0.5), 1px 1px 0px 1px #3f58e3;
}
.tariffCards > div.business {
margin-top: -70px;
z-index: 1;
background-color: #2c6fd1;
background-image: linear-gradient(135deg, #21bbfe, #2c6fd1);
box-shadow: 20px 20px 60px rgba(34, 50, 84, 0.5), 1px 1px 0px 1px #2c6fd1;
}
.tariffCards > div.first {
margin-top: -70px;
background-color: #352f64;
background-image: linear-gradient(135deg, #415197, #352f64);
box-shadow: 5px 5px 60px rgba(34, 50, 84, 0.1), 1px 1px 0px 1px #352f64;
}
.tariffCards > div:hover {
transform: rotateX(30deg) rotateY(-15deg) rotate(30deg) translate(-25px, 50px);
}
.tariffCards > div:hover:after {
transform: translateX(100%);
transition: all 1.2s ease-in-out;
}
</style>
😷 流光扫过动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.flash-container {
width: 148px;
height: 148px;
background: #333333;
margin: 0 auto;
position: relative;
overflow: hidden;
&::after {
content: '';
height: 150%;
width: 25px;
background: #fff;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
opacity: 0.6;
filter: blur(6px);
animation: move 2s infinite ease-out;
}
}
@keyframes move {
0% {
transform: translate(-200px, -200px) rotate(45deg);
}
100% {
transform: translate(200px, 200px) rotate(45deg);
}
}
</style>
</head>
<body>
<div class="flash-container">
<img src="https://img-blog.csdnimg.cn/180fbfb5f0434211b3e8640891ecea33.gif" alt />
</div>
</body>
</html>
🥶 酷炫标题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.wrap {
position: relative;
display: inline-block;
left: 50%;
top: 20px;
}
.wrap span {
position: relative;
z-index: 2;
display: inline-block;
padding: 0 15px;
height: 32px;
line-height: 32px;
background-color: #42b983;
color: #fff;
font-size: 16px;
box-shadow: 0 10px 6px -9px rgba(0, 0, 0, 0.6);
}
.wrap span::before,
.wrap span::after {
position: absolute;
bottom: -6px;
border-width: 3px 5px;
border-style: solid;
content: '';
}
.wrap span::before {
left: 0;
border-color: #42b983 #42b983 transparent transparent;
}
.wrap span::after {
right: 0;
border-color: #42b983 transparent transparent #42b983;
}
.wrap::before,
.wrap::after {
position: absolute;
top: 6px;
content: '';
border-style: solid;
border-color: #42b983;
}
.wrap::before {
left: -32px;
border-width: 16px 26px 16px 16px;
border-left-color: transparent;
}
.wrap::after {
right: -32px;
border-width: 16px 16px 16px 26px;
border-right-color: transparent;
}
</style>
</head>
<body>
<div class="wrap">
<span>距离高考还有10天</span>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
padding: 20px;
}
.wrap {
position: relative;
background-color: pink;
height: 200px;
/* width: 500px;
margin: 0 auto; */
}
.left {
width: 200px;
height: 140px;
position: absolute;
top: -8px;
left: -8px;
overflow: hidden;
}
.left::before {
position: absolute;
left: 124px;
border-radius: 8px 8px 0 0;
width: 16px;
height: 8px;
background-color: #42b983;
content: '';
}
.left::after {
position: absolute;
left: 0;
top: 124px;
border-radius: 0 8px 8px 0;
width: 8px;
height: 16px;
background-color: #42b983;
content: '';
}
.left span {
display: inline-block;
text-align: center;
width: 200px;
height: 40px;
line-height: 40px;
position: absolute;
top: 30px;
left: -50px;
z-index: 2;
overflow: hidden;
-ms-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
border: 1px dashed #fff;
box-shadow: 0 0 0 3px #42b983, 0 14px 7px -9px rgba(0, 0, 0, 0.6);
background-color: #42b983;
color: #fff;
}
.right {
width: 140px;
height: 200px;
position: absolute;
top: -8px;
right: -8px;
overflow: hidden;
}
.right::before {
position: absolute;
right: 124px;
border-radius: 8px 8px 0 0;
width: 16px;
height: 8px;
background-color: #42b983;
content: '';
}
.right::after {
position: absolute;
right: 0;
top: 124px;
border-radius: 0 8px 8px 0;
width: 8px;
height: 16px;
background-color: #42b983;
content: '';
}
.right span {
display: inline-block;
text-align: center;
width: 200px;
height: 40px;
line-height: 40px;
position: absolute;
top: 30px;
right: -50px;
z-index: 2;
overflow: hidden;
-ms-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
border: 1px dashed #fff;
box-shadow: 0 0 0 3px #42b983, 0 14px 7px -9px rgba(0, 0, 0, 0.6);
background-color: #42b983;
color: #fff;
}
</style>
</head>
<body>
<div class="wrap">
<div class="left">
<span>张</span>
</div>
<div class="right">
<span>坤</span>
</div>
</div>
</body>
</html>
🤖 交互式评分
文件较多、参考仓库
🐔 动态向上箭头
🐽 卸载对话框表情交互动画
✈️ 随机密码生成器代码
下面是功能篇章
🧨 跟随设备切换主题
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>切换主题 dark、light、system</title>
<style>
/* dark 主题 */
[data-theme="dark"] {
--text-color: white;
--background-color: black;
}
/* light 主题 */
[data-theme="light"] {
--text-color: black;
--background-color: white;
}
body {
color: var(--text-color);
background-color: var(--background-color);
}
</style>
</head>
<body>
<h1>跟随设备切换主题</h1>
<p>这是一段测试文本。</p>
<img src="https://www4.bing.com//th?id=OHR.GoliathHeron_ZH-CN2413747227_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp&w=360&h=202"
alt="">
<!-- 下拉框 -->
<select id="select-wrap" title="切换主题">
<option value="light">light</option>
<option value="dark">dark</option>
<option value="system">system</option>
</select>
<script>
const selectWrap = document.querySelector('#select-wrap');
const bodyTheme = document.querySelector('body');
window.onload = () => {
// 获取用户的选择
const theme = localStorage.getItem('theme');
// 如果用户选择了主题,就设置主题同时改变下拉框的选中项
if (theme) {
// 设置下拉框的选中项
selectWrap.value = theme;
// 动态设置 system 主题
setTheme()
}
}
// 监听下拉框的变化
selectWrap.onchange = () => {
setTheme()
localStorage.setItem('theme', selectWrap.value);
}
// 动态设置 system 主题 函数
function setTheme() {
/*
下拉框改变时,先移除 system 主题,因为dark、light主题不需要使用系统主题,
系统主题只需要在下拉框选择 system 时才需要使用,动态添加,解决系统切换主题时,项目不需要刷新
*/
removeSystemLink()
if (selectWrap.value === 'system') {
setTimeout(() => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './system.css';
// 加上名称方便找到移除
link.id = 'system';
// 在head中插入link 添加到最后
document.head.appendChild(link);
}, 0);
// 在某个元素前面插入一个元素 语法:parentElement.insertBefore(newElement, targetElement)
// parentElement是要插入新元素的父元素,newElement是要插入的新元素对象,targetElement是当前元素的前一个兄弟元素,也就是新元素要插入到这个元素之前。
// 例子:在head中插入link 添加到最前面
// document.head.insertBefore(link, document.head.firstChild);
// document.head.insertBefore(link, document.head.children[0]);
// 或者:document.head.insertBefore(link, document.head.lastChild); // 在head中插入link 添加到最后面
// 或者:document.head.insertBefore(link, document.querySelector('title')); // 在head中插入link 添加到title前面
} else {
// 动态设置 light 和 dark 主题
bodyTheme.setAttribute('data-theme', selectWrap.value);
}
}
/*
移除 system 主题 函数:
有时候切换系统主题了,项目还要刷新一下:
因为系统主题的切换需要重新加载样式表才能生效,但有时候页面中的部分元素已经渲染出来,
样式表被加载后并不会自动刷新渲染结果。在这种情况下,动态移除和添加样式表就能解决这个问题。
*/
function removeSystemLink() {
const link = document.querySelector('#system');
if (link) link.remove();
}
</script>
</body>
</html>
system.css
:root {
--text-color: black;
--background-color: white;
}
@media (prefers-color-scheme: dark) {
:root {
--text-color: white;
--background-color: black;
}
}
body {
color: var(--text-color);
background-color: var(--background-color);
}
🚒 昼夜切换主题
❤️🩹 使用系统取色器
<!DOCTYPE html>
<html>
<head>
<title>使用系统取色器</title>
<style>
.box {
width: 200px;
height: 200px;
border: 1px solid black;
}
</style>
</head>
<body>
<button>打开取色器</button>
<div class="box"></div>
<br>
<label></label>
<script>
const btn = document.querySelector('button');
const box = document.querySelector('.box');
const label = document.querySelector('label');
btn.onclick = async () => {
const dropper = new EyeDropper();
try {
const result = await dropper.open();
console.log(result);
box.style.backgroundColor = result.sRGBHex;
label.textContent = result.sRGBHex;
} catch {
console.log('用户取消了操作');
}
}
</script>
</body>
</html>
😃 右键动态菜单(已做边缘动画及边界外判断)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>右键菜单</title>
<style>
* {
margin: 0;
padding: 0;
}
#context-menu {
position: fixed;
width: 300px;
height: 400px;
background-image: linear-gradient(235deg, #f7f0ac, #acf7f0, #f0acf7);
transition: all 0.3s ease-in-out;
border-radius: 5px;
/* 使用 display: none; 会造成边界判断失败 */
visibility: hidden;
opacity: 0;
z-index: 9999;
}
.menu-item {
padding: 10px;
cursor: pointer;
}
.menu-item:hover {
background-color: #d6d6d6;
}
.rebound {
animation: rebound .5s;
}
@keyframes rebound {
0% {
transform: translate(-100%, 0);
opacity: 0;
}
40% {
transform: translate(10%, 0);
}
70% {
transform: translate(-5%, 0);
}
100% {
transform: translate(0);
opacity: 1;
}
}
</style>
</head>
<body>
<div id="context-menu">
<div class="menu-item">菜单项1</div>
<div class="menu-item">菜单项2</div>
<div class="menu-item">菜单项3</div>
</div>
<script>
const contextMenu = document.getElementById('context-menu');
function setPosition(event, menu) {
// 获取窗口宽度和高度
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
// 获取菜单宽度和高度
const menuWidth = menu.offsetWidth;
const menuHeight = menu.offsetHeight;
// 获取鼠标在页面中的位置
const x = event.clientX;
const y = event.clientY;
// 计算菜单的调整后的水平位置和垂直位置
const adjustedX = (x + menuWidth) >= screenWidth ? (x - menuWidth) : x; // 检查是否超出右侧边界
const adjustedY = (y + menuHeight) >= screenHeight ? (y - menuHeight) : y; // 检查是否超出底部边界
// 如果超出右侧边界,让菜单反弹一下
if (adjustedX !== x) {
menu.classList.add('rebound');
setTimeout(() => {
menu.classList.remove('rebound');
}, 1000);
}
// 设置菜单的位置
menu.style.left = adjustedX + 'px';
menu.style.top = adjustedY + 'px';
}
document.addEventListener('contextmenu', function (event) {
event.preventDefault(); // 阻止系统默认的右键菜单
setPosition(event, contextMenu); // 计算并设置菜单的位置
contextMenu.style.visibility = 'visible'; // 显示菜单
contextMenu.style.opacity = '1';
});
// 如果点击的地方不在菜单内,隐藏菜单
document.addEventListener('click', function (event) {
console.log(event.target);
console.log(contextMenu);
if (contextMenu !== event.target) {
contextMenu.style.visibility = 'hidden';
contextMenu.style.opacity = '0';
}
});
const menuItems = document.querySelectorAll('.menu-item');
// 为每个菜单项添加事件监听 不优化
// for (let i = 0; i < menuItems.length; i++) {
// menuItems[i].addEventListener('click', function (event) {
// console.log(`您点击了菜单项${i + 1}`);
// contextMenu.style.visibility = 'visible';
// });
// menuItems[i].addEventListener('mousedown', function (event) {
// event.stopPropagation();
// });
// }
// 使用事件委托 优化
contextMenu.addEventListener('click', function (event) {
const target = event.target;
if (target.classList.contains('menu-item')) {
// indexOf() 返回指定元素在目标队列中首次出现的索引,找不到指定元素,则会返回 -1。
// call 第一个参数是指定该方法执行时的上下文环境(写谁指向谁),第二个参数,它表示要传递给被调用的方法的参数。
const index = Array.prototype.indexOf.call(menuItems, target);
console.log(`您点击了菜单项${index + 1}`);
}
});
</script>
</body>
</html>
🎋 酷炫打字特效
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>删库跑路</title>
<style>
* {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box;
color: inherit;
}
body {
background-image: linear-gradient(120deg, #4f0088 0%, #000 100%);
height: 100vh;
}
div {
background: rgba(0, 0, 0, 0);
width: 70vw;
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
margin: 0 auto;
padding: 30px 30px 10px;
box-shadow: 0 0 150px -20px rgba(0, 0, 0, 0.5);
z-index: 3;
}
P {
font-family: "Share Tech Mono", monospace;
color: #f5f5f5;
margin: 0 0 20px;
font-size: 17px;
line-height: 1.2;
}
span {
color: #f0c674;
}
i {
color: #8abeb7;
}
div a {
text-decoration: none;
}
b {
color: #81a2be;
}
a.avatar {
position: fixed;
bottom: 15px;
right: -100px;
-webkit-animation: slide 0.5s 4.5s forwards;
animation: slide 0.5s 4.5s forwards;
display: block;
z-index: 4;
}
a.avatar img {
border-radius: 100%;
width: 44px;
border: 2px solid white;
}
@-webkit-keyframes slide {
from {
right: -100px;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
opacity: 0;
}
to {
right: 15px;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
}
@keyframes slide {
from {
right: -100px;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
opacity: 0;
}
to {
right: 15px;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
}
</style>
</head>
<body>
<div id="container">
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> rm -rf /*正在删除本地的数据 </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 删除成功 </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 文件备份自动启用中... </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 备份失败! 仅备份文件碎片... </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 文件碎片正在加载... </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 加载失败 对不起我们跑路了... </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 不要伤心 不要难过... </i>
"
</p>
<p>
>
<span> [root@VM-0-7-centos ~]# </span>
: "
<i> 或许我们还会再建个站继续骗你 ... </i>
"
</p>
</div>
<script>
// 将元素内容转换为字符串
let str = document.querySelector("#container").innerHTML.toString();
let i = 0;
let containerHtml = document.querySelector("#container");
containerHtml.innerHTML = "";
setTimeout(function () {
let se = setInterval(function () {
i++;
// str.slice(start, end) 表示从 str 字符串的 start 索引处开始,提取到 end 索引处之前(不包含 end)的子串。如果不指定 end 参数,则默认提取到字符串末尾。
containerHtml.innerHTML = str.slice(0, i) + "|";
if (i == str.length) {
clearInterval(se);
containerHtml.innerHTML = str;
}
}, 30);
}, 0);
</script>
</body>
</html>
🚆 方形抽奖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>转盘</title>
<style>
.main {
display: flex;
width: 320px;
flex-wrap: wrap;
}
.item {
width: 100px;
height: 100px;
background-color: #ddd;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #fff;
}
.item.active {
background-color: #999;
}
.item.btn {
background-color: green;
color: #fff;
}
</style>
</head>
<body>
<div class="main"></div>
<script>
window.onload = () => {
const data = {
jp: [
{ name: "代金券1" },
{ name: "代金券2", is: true },
{ name: "代金券3" },
{ name: "代金券4" },
{ name: "代金券5" },
{ name: "代金券6" },
{ name: "代金券7" },
{ name: "代金券8" },
],
};
const main = document.querySelector(".main");
main.innerHTML = data.jp
.map(
(v) =>
`<div class="item" ${v.is ? "is='true'" : ""}>${v.name}</div>`
)
.join("");
const btnEl = document.createElement("div");
btnEl.className = "item btn";
btnEl.innerHTML = "抽奖";
main.insertBefore(btnEl, main.children[4]);
let isRunning = false;
let itemIndex = 0;
const spinWheel = () => {
const nodesList = [...main.childNodes].filter(
(v) => !v.classList.contains("btn")
);
const spinOrder = [0, 1, 2, 4, 7, 6, 5, 3];
const spinItems = spinOrder.map((v) => nodesList[v]);
if (!spinItems[itemIndex]) {
itemIndex = 0;
}
const active1 = main.querySelector(".active");
active1 && active1.classList.remove("active");
spinItems[itemIndex].classList.add("active");
if (!isRunning && spinItems[itemIndex].getAttribute("is")) {
clearInterval(intervalId);
}
itemIndex++;
};
let intervalId;
btnEl.onclick = () => {
if (isRunning) return;
isRunning = true;
intervalId = setInterval(spinWheel, 100);
setTimeout(() => {
isRunning = false;
}, 3000);
};
};
</script>
</body>
</html>
🚼 圆形抽奖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>转盘</title>
<style>
.zp {
position: relative;
border-radius: 100%;
width: 300px;
overflow: hidden;
margin:0 auto;
}
.zp-panel {
background: url(./images/zp.webp) no-repeat center center;
background-size: 100%;
width: 300px;
height: 300px;
position: relative;
transition: transform 4s ease-out;
}
.item {
font-size: 12px;
position: absolute;
top: 50%;
left: 0;
right: 0;
text-align: center;
margin-top: -7px;
line-height: 1;
}
.btn {
position: absolute;
background-color: green;
width: 80px;
height: 80px;
border-radius: 100%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
.btn:after {
content: '';
position: absolute;
height: 80px;
width: 4px;
background-color: green;
left: 38px;
bottom: 60px;
}
</style>
</head>
<body>
<div class="zp">
<div class="zp-panel"></div>
<div class="btn">开始</div>
</div>
</body>
<script>
const arr = [
{name: '积分+1'},
{name: '积分+2'},
{name: '积分+3'},
{name: '积分+4'},
{name: '电动车一辆', is: true},
{name: '积分+10'}
]
const zp = document.querySelector('.zp-panel')
const btn = document.querySelector('.btn')
const d = 360 / arr.length
zp.innerHTML = arr.map((v, i) => {
return `<div class="item"
style="transform:rotate(${i*d - 90}deg) translate(75px)">${v.name}</div>`
}).join('')
btn.onclick = () => {
const zpIndex = arr.findIndex(v => v.is)
const deg = -(zpIndex * d + 720)
// 清空上次抽奖,暂停动画效果
zp.style.transition = 'inherit'
zp.style.transform = ''
setTimeout(() => {
// 重置transition,恢复动画效果
zp.style.transition = ''
setTimeout(() => {
// 触发动画
zp.style.transform = `rotate(${deg}deg)`
})
})
}
</script>
</html>
🛹 吸顶楼层导航
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>楼层滚动</title>
</head>
<style>
* {
padding: 0;
margin: 0;
}
body,
html {
height: auto;
width: 100%;
background-image: linear-gradient(to right, #f7f0ac, #acf7f0, #f0acf7);
}
.nav-wrap {
position: fixed;
z-index: 99;
top: 50%;
left: 120px;
transform: translateY(-50%);
width: 100px;
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
.stick {
position: sticky;
top: 10px;
height: 15px;
padding: 5px;
line-height: 15px;
background-color: #5f9ea0;
color: #ffff;
font-weight: 900;
}
.nav {
width: 100px;
flex: 1;
margin-bottom: 10px;
background: url(https://img1.baidu.com/it/u=105002249,3897918256&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=281)
no-repeat center center/cover;
display: flex;
align-items: center;
justify-content: center;
font-size: 25px;
}
.main {
text-align: center;
line-height: 900px;
font-size: 30px;
color: #ffff;
width: 800px;
height: 900px;
/* background-color: hsla(0, 0%, 100%, 0.2); */
background: rgba(255, 255, 255, 0.4);
position: relative;
left: 50%;
transform: translateX(-50%);
}
.nav-active {
color: red;
background: url("https://img1.baidu.com/it/u=833204098,2930185410&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=745")
no-repeat center center/cover;
}
</style>
<body>
<div class="nav-wrap">
<div class="nav">A</div>
<div class="nav">B</div>
<div class="nav">C</div>
<div class="nav">D</div>
</div>
<div class="main">A</div>
<div class="main">
<div class="stick">B</div>
</div>
<div class="main">
<div class="stick">C</div>
</div>
<div class="main">
<div class="stick">D</div>
</div>
</body>
<script>
const navAll = document.querySelector(".nav-wrap");
const mainAll = document.querySelectorAll(".main");
function reset() {
for (let i = 0; i < navAll.children.length; i++) {
navAll.children[i].classList.remove("nav-active");
}
}
// 进入页面时,第一个导航栏高亮
navAll.children[0].classList.add("nav-active");
navAll.addEventListener("click", (event) => {
if (event.target.classList.contains("nav")) {
const index = Array.from(navAll.children).indexOf(event.target);
console.log(index);
window.scrollTo({
top: Math.floor(mainAll[index].offsetTop),
behavior: "smooth",
});
}
});
window.addEventListener("scroll", () => {
const scrollTop = document.scrollingElement.scrollTop;
let left = 0;
let right = navAll.children.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const currentMain = mainAll[mid];
if (
currentMain.offsetTop <= scrollTop &&
currentMain.offsetTop + currentMain.offsetHeight > scrollTop
) {
reset();
navAll.children[mid].classList.add("nav-active");
break;
} else if (currentMain.offsetTop > scrollTop) {
right = mid - 1;
} else {
left = mid + 1;
}
}
});
</script>
</html>
✈️ 困了盖坤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>羊了个羊</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.main {
position: relative;
}
.item {
position: absolute;
background: no-repeat center center #ffffff;
border: 1px solid #ddd;
background-size: 100%;
color: transparent;
display: flex;
justify-content: center;
align-items: center;
z-index: 0;
transition: left 0.3s, top 0.3s, transform 0.3s;
}
.item:after {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
transition: background-color 0.2s;
}
.disabled:after {
background-color: rgba(0, 0, 0, 0.7);
}
.move-list {
border: 1px solid #ddd;
background-color: #ddd;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="main"></div>
<div class="move-list"></div>
</body>
<script>
// 基础数据
const simpleData = [
{ name: "虎", color: "#ff1100", bg: "./xxl-img/1.png" },
{ name: "兔", color: "#ff8800", bg: "./xxl-img/2.png" },
{ name: "牛", color: "green", bg: "./xxl-img/3.png" },
{ name: "羊", color: "blue", bg: "./xxl-img/4.png" },
{ name: "蛇", color: "#779922", bg: "./xxl-img/5.png" },
{ name: "鼠", color: "#335577", bg: "./xxl-img/6.png" },
];
// 卡片大小
const size = 40;
// 矩阵行
const rows = 10;
// 矩阵列
const cols = 10;
// 每组为3个
const oneGoupCount = 3;
// 每个卡片有x组
const group = 6;
// 总共6层,即6个表格
const layerCount = 6;
// 表格html
const cellHtml = [];
// 将生成所有的十二生肖
const renderData = Array.from(new Array(oneGoupCount * group))
.map((v) => {
return simpleData.map((v) => ({ ...v }));
})
.flat()
.sort((v) => Math.random() - 0.5);
// 第一步:绘制出表格矩阵
for (let ly = layerCount - 1; ly >= 0; ly--) {
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
let pyStep = (ly + 1) % 2 === 0 ? size / 2 : 0;
let item = Math.random() > 0.7 && renderData.pop();
item &&
cellHtml.push(`<div class="item" onclick="move(this)" id="m${ly}-${i}-${j}"
style="width:${size}px;height:${size}px;left:${size * j + pyStep}px;top:${size * i + pyStep
}px;background-image:url(${item.bg || ""})">${item.name || ""}</div>`);
}
}
}
const main = document.querySelector(".main");
const moveList = document.querySelector(".move-list");
main.innerHTML = cellHtml.reverse().join("");
main.style.height = `${size * rows + size * 2}px`;
main.style.width = `${size * cols}px`;
moveList.style.height = `${size}px`;
moveList.style.width = `${size * 6}px`;
// 第二步:计算被遮住的底牌需标暗色
const checkDisabled = (items) => {
(items || main.querySelectorAll(".item")).forEach((v, i) => {
const arr = v.id
.substring(1)
.split("-")
.map((v) => Number(v));
const isPy = (arr[0] + 1) % 2 === 0;
for (let i = arr[0] + 1; i <= layerCount - 1; i++) {
const isPyB = (i + 1) % 2 === 0;
if (isPy === isPyB) {
const el = main.querySelector(`#m${i}-${arr[1]}-${arr[2]}`);
if (el) {
v.classList.add("disabled");
break;
}
} else if (isPy && !isPyB) {
if (
![
`${i}-${arr[1]}-${arr[2]}`,
`${i}-${arr[1]}-${arr[2] + 1}`,
`${i}-${arr[1] + 1}-${arr[2]}`,
`${i}-${arr[1] + 1}-${arr[2] + 1}`,
].every((k) => {
return !main.querySelector("#m" + k);
})
) {
v.classList.add("disabled");
break;
} else {
v.classList.remove("disabled");
}
} else if (!isPy && isPyB) {
if (
![
`${i}-${arr[1]}-${arr[2]}`,
`${i}-${arr[1]}-${arr[2] - 1}`,
`${i}-${arr[1] - 1}-${arr[2]}`,
`${i}-${arr[1] - 1}-${arr[2] - 1}`,
].every((k) => {
return !main.querySelector("#m" + k);
})
) {
v.classList.add("disabled");
break;
} else {
v.classList.remove("disabled");
}
}
}
});
};
// 第三步:点击卡片进行消除计算
let canMove = true;
// 点击棋子,动画移动
const move = (me) => {
let left = moveList.offsetLeft;
let top = moveList.offsetTop;
if (!canMove || me.className.indexOf("disabled") >= 0) {
return;
}
canMove = false;
if (moveList.children.length > 0) {
let el = moveList.children[moveList.children.length - 1];
left = el.offsetLeft + size;
top = el.offsetTop;
}
me.style.top = `${top}px`;
me.style.left = `${left}px`;
me.transitionNamesCount = 0;
me.ontransitionend = (e) => {
me.transitionNamesCount++;
if (me.transitionNamesCount === 2) {
moveEnd(me);
canMove = true;
}
};
};
// 动画结束的相关计算
const moveEnd = (me) => {
me.ontransitionend = null;
me.setAttribute("onclick", "");
moveList.appendChild(me);
const findResult = [...moveList.children].filter((v) => v.innerHTML === me.innerHTML);
if (findResult.length === 3) {
findResult.forEach((v) => {
v.ontransitionend = (e) => {
moveList.removeChild(v);
[...moveList.children].forEach((v, i) => {
v.style.left = `${i * size + moveList.offsetLeft}px`;
});
};
setTimeout(() => (v.style.transform = "scale(0)"));
});
}
if (moveList.children.length === 6) {
return alert("池子已满,游戏结束");
} else if (main.children.length === 0) {
return alert("恭喜通关");
}
checkDisabled();
};
checkDisabled();
</script>
</html>
🛫 扣扣乐
<canvas id="cvs" width="605" height="338"></canvas>
<style>
canvas {
background: url(./images/zj.png) no-repeat center center;
background-size: 100%;
}
</style>
<script>
const cvs = document.getElementById('cvs')
const ctx = cvs.getContext('2d')
const url = './images/zj-bg.jpeg'
const img = new Image()
img.src = url
img.onload = () => {
ctx.drawImage(img, 0, 0, cvs.width, cvs.height)
}
let isClearing = false
cvs.addEventListener('mousedown', e => {
isClearing = true
})
cvs.addEventListener('mousemove', e => {
if (isClearing) {
const clearSize = 60
ctx.clearRect(e.pageX - clearSize, e.pageY - clearSize,
clearSize, clearSize)
}
})
document.addEventListener('mouseup', e => {
isClearing = false
})
</script>
🍰 签名回溯
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>HTML5 绘图</title>
<style>
.canvas {
border: 1px solid #ccc;
cursor: crosshair;
}
</style>
</head>
<body>
<input type="button" value="重放" id="btn1" />
<input type="button" value="清空" id="btn2" />
<input type="button" value="保存" id="btn3" />
<br />
<canvas id="cvs" class="canvas"></canvas>
</body>
</html>
<script>
class Draw {
constructor(selector, width, height) {
this.element = document.querySelector(selector);
this.canvas = this.element.getContext("2d");
this.element.width = width;
this.element.height = height;
this.timeId = null;
this.animateArr = [];
}
init() {
const { canvas: c, element: el } = this;
let isDrag = false;
c.lineWidth = 2;
c.lineJoin = "round";
c.shadowColor = "#000";
c.shadowBlur = 4;
el.onmousedown = () => {
isDrag = true;
c.beginPath();
};
el.onmousemove = (e) => {
if (isDrag) {
const x = e.pageX - el.offsetLeft;
const y = e.pageY - el.offsetTop;
c.lineTo(x, y);
c.stroke();
this.animateArr.push([x, y]);
}
};
el.onmouseup = () => {
isDrag = false;
this.animateArr.push(-1);
};
el.onmouseout = () => {
el.onmouseup();
};
}
animateGo() {
const { canvas: c } = this;
this.clear();
c.beginPath();
const loop = (animate, i) => {
if (i < animate.length - 1) {
const arr = animate[i];
if (arr === -1) {
c.beginPath();
} else {
c.lineTo(arr[0], arr[1]);
c.stroke();
}
i++;
this.timeId = setTimeout(() => {
loop(animate, i);
}, 10);
}
};
loop(this.animateArr, 0);
}
clear() {
const { canvas: c } = this;
const { width, height } = this.element;
c.clearRect(0, 0, width, height);
}
}
const g = new Draw("#cvs", 600, 500);
g.init();
document.getElementById("btn1").onclick = () => {
g.animateGo();
};
document.getElementById("btn2").onclick = () => {
g.clear();
g.animateArr = [];
};
document.getElementById("btn3").onclick = () => {
const canvas = document.querySelector("#cvs");
const dataURL = canvas.toDataURL();
// 将dataURL发送到服务器进行保存操作
console.log(dataURL);
};
</script>
🌍 翻盘抽奖
画面 挺多、不一一展示了,快来一键三连抱走吧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>9宫格翻牌抽奖</title>
<style>
.title {
text-align: center;
}
.box {
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 330px;
margin:0 auto;
}
.item {
position: relative;
margin: 5px;
width: 100px;
height: 100px;
}
.style1, .style2 {
position: absolute;
left:0;
top: 0;
bottom: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
text-align: center;
transition: transform .4s
}
.style1 {
background-color: #ff8800;
color: #fff;
}
.style2 {
color: #ff8800;
border:1px solid #ff8800;
padding: 0 10px;
transform: scaleX(0);
background-color: #ffe6c9;
}
.hide {
transform: scaleX(0);
}
.show {
transform: scaleX(1);
}
.show-result {
transform: scaleX(1);
opacity: 0.5;
}
</style>
</head>
<style>
</style>
<body>
<div class="title">可抽奖<label id="count">0</label>次</div>
<div class="box"></div>
</body>
<script>
// 奖品列表
const list = [
{id: 1, name: '1元优惠券', is: true},
{id: 2, name: '10元优惠券', is: true},
{id: 3, name: '谢谢惠顾', is: true},
{id: 4, name: '豪华电动车'},
{id: 5, name: '1w购物券'},
{id: 6, name: '5w购物券'},
{id: 7, name: '豪华轿车'},
{id: 8, name: '房子一套'},
{id: 9, name: '顶配笔记本'}
].sort(v => Math.random() - 0.5)
// 中奖项
const isArr = list.filter(v => v.is)
// 非中奖项
const noArr = list.filter(v => !v.is)
// 抽奖次数
let count = 3
const box = document.querySelector('.box')
const countEL = document.querySelector('#count')
countEL.innerHTML = count
box.innerHTML = list.map(v => {
return `<div class="item" onclick="fp(this)">
<div class="style1">抽奖</div>
<div class="style2"></div>
</div>`
}).join('')
// 翻牌抽奖
const fp = (me) => {
if (count === 0) {
return
}
me.querySelector('.style1').classList.add('hide')
// 抽奖动画
setTimeout(() => {
// 从非中奖项中取最后一个,并从数组中移除该项
const item = isArr.pop()
const style2 = me.querySelector('.style2')
style2.innerHTML = item.name
style2.classList.add('show')
}, 400)
count--
countEL.innerHTML = count
// 当抽奖次数为0的时候,就把剩余的奖项全部展示出来
if (count === 0) {
setTimeout(() => {
box.querySelectorAll('.style1').forEach(v => {
v.classList.add('hide')
if (v.nextElementSibling.className.indexOf('show') === -1) {
const item = noArr.pop()
v.nextElementSibling.innerHTML = item.name
}
})
setTimeout(() => {
box.querySelectorAll('.style2').forEach(v => {
if (v.className.indexOf('show') === -1) {
v.classList.add('show-result')
}
})
}, 400)
}, 1000)
}
}
</script>
</html>
🪂 地址 🤺🤺🤺
🥰😉 谢谢观看
_______________________________ 期待再见 _______________________________