前端:UI 交互式特效 —— Css、Js

news2024/11/19 14:36:13

😷😊🤺🤺🤺前期回顾f8e3cc1a0f694ac2b665ca2ad14c49d7.png

打造极简风格动效 —— 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>

🪂 地址 🤺🤺🤺 

  

 🥰😉  谢谢观看

7730e2bd39d64179909767e1967da702.jpeg

 _______________________________  期待再见  _______________________________ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/756086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

研0进阶式学习—-数据挖掘概念与技术

目录 【 写在前面】什么是数据挖掘为何进行模式评估如何进行模式评估数据挖掘的发展趋势 【 写在前面】 本科期间&#xff0c;数据挖掘算法学过一些&#xff0c;甚至本人的毕业设计也是围绕此展开的&#xff0c;但是显然学得太皮毛&#xff0c;今天偶然读到《数据挖掘•概念与…

基于tauri+vue3+pinia2客户端管理系统程序|tauri+vite4后台系统

TauriAdmin一款跨端通用后台系统模板解决方案 基于 tauri rust webview2 整合 vite4 搭建桌面端 vue3 管理后台模板TauriVue3Admin。支持多窗口切换管理、vue-i18n多语言、动态路由权限、常用业务功能模块及动态路由缓存等功能。 使用技术 编码工具&#xff1a;Vscode框架技术…

【chap4-链表】用Python3刷《代码随想录》

通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域data&#xff0c;另一个是指针域next&#xff08;存放指向下一个节点的指针&#xff09;&#xff0c;最后一个节点的指针域指向null&#xff08;空指针&#xff09; 链接的入口点称为…

耳夹式骨传导耳机有哪些比较好用?这三个款式不容错过!

骨传导耳机由于不入耳&#xff0c;不用担心耳道健康问题&#xff0c;越来越受到广大网友的喜欢&#xff0c;而传统的入耳式耳机&#xff0c;则因为长时间佩戴会耳朵痛&#xff0c;容易掉落等问题逐渐的被网友抛弃&#xff0c;那么在骨传导耳机市场种类这么多的情况下&#xff0…

Apache Kudu 在**医疗科技的生产实践

目录 说明 医疗场景下数据特点 KUDU 的介绍 kudu 架构 kudu 文件组织形式 kudu的生产实践 技术选型 整体的架构 项目遇到的问题 参考资料 说明 本文主要介绍APACHE KUDU 在**医疗科技数据实时分析场景下的实践&#xff0c;内容包括&#xff1a; 医疗场景下数据特点 …

mysql什么情况下行锁(表锁)(锁的概念)

1&#xff1a;数据表aa的设计结构 2&#xff1a; 使用navicat编写手动控制事务 3&#xff1a;先选择开启事务和执行更新操作&#xff0c;where b1&#xff08;表锁&#xff09;b不是索引&#xff0c;不提交事务&#xff0c;&#xff08;如果where b1&#xff0c;b是索引就行锁&…

本地Nginx部署React前端项目浅尝

目录 nginx [下载](http://nginx.org/en/download.html)nginx命令react打包文件放置nginx 配置 运行效果nginx踩坑根目录配置 nginx 下载 根据上面的版本找到适合自己的 nginx版本&#xff0c;我目前是环境是 windows&#xff0c;所以下载 稳定版本。 nginx命令 在下载的ngin…

数学建模-判断数据是否服从正态分布

大样本用qq图 >1000 皮尔逊相关系数需要正态性检验&#xff0c;利用上面三种方法其中一种 斯皮尔曼相关系数不用正态性检验

Claude2轻松解决代码Bug的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

GPT-4最新细节曝光:从架构、基础设施、训练数据集、成本、视觉到MoE

OpenAI保持GPT-4架构封闭&#xff0c;不是因为对人类的某种存在风险&#xff0c;而是因为他们所构建的内容是可复制的。实际上&#xff0c;我们预计Google、Meta、Anthropic、Inflection、Character、Tencent、ByteDance、Baidu等公司在短期内将拥有与GPT-4一样甚至更强大的模型…

T100简易的查询作业功能开发

一、自定义作业维护 首先打开作业【azzi310】,进行作业的新增。 这是一个空白的作业界面,我们需要填写的地方也就那么几个。 查询单id:注册一个查询单 【四个字母三个数字】比如这里我们cxmq101.查询单名称:给这个查询起一个名字最大查询笔数:可以自己定义报表是否自己录…

数据库,数据仓库,数据湖

数据仓库四层分层 ODS——原始数据层&#xff1a;存放原始数据 ODS层即操作数据存储&#xff0c;是最接近数据源中数据的一层&#xff0c;数据源中的数据&#xff0c;经过抽取、洗净、传输&#xff0c;也就说传说中的ETL之后&#xff0c;装入本层&#xff1b;一般来说ODS层的数…

JavaScript初识

ECMAScript和JavaScript到底是什么关系&#xff1f; 简单来说&#xff0c;ECMAScript是JavaScript语言的国际标准&#xff0c;JavaScript是ECMAScript的实现。 一、第一个程序&#xff1a;hello word 二、JavaScript的几种常见写法&#xff1a; 1、将JavaScript写在标签上 2、…

应用上下文能否获取-spring13

我们能否通过web层通过spring容器去获得serive&#xff0c;然后serive内部Dao再去注入 这句话的意思是去加载xml配置文件&#xff0c;去加载spring容器&#xff0c;每次都要创建&#xff0c;太麻烦了&#xff0c;影响性能 最好的方法&#xff1a;应用上下文或者spring容器创建一…

DCL与延迟初始化(单例)

前言 在Java多线程程序中&#xff0c;有时候需要采用延迟初始化来降低初始化类和创建对象的开销。 第一种&#xff08;存在问题&#xff09; public class UnsafeLazyInitialization {private static Instance instance;public static Instance getInstance() {if (instance …

零基础如何自学成为网络安全工程师

前言 一份网络攻防渗透测试的学习路线&#xff0c;不藏私了&#xff01; &#x1f449; 【一帮助安全学习一】&#x1f448;这里自取256G网络安全自学资料 1、学习编程语言(phpmysqljshtml) 原因&#xff1a; phpmysql可以帮助你快速的理解B/S架构是怎样运行的&#xff0c…

【AI底层逻辑】——篇章5(上):机器学习算法之回归分类

目录 引入 一、何为机器学习 1、定规则和学规则 2、算法的定义 二、机器学习算法 1、常见学习方法 2、回归 3、分类 续下篇... 往期精彩&#xff1a; 引入 计算机发明初&#xff0c;专家通过将专业知识和经验梳理成规则输入计算机程序&#xff0c;但是这样跟不上知识…

IT技术培训班:搭乘学习快车的抉择

引言&#xff1a; 在IT技术学习的道路上&#xff0c;我们常常会被推荐各种五花八门的技术培训班。它们通过各种宣传手段向我们展示着美好的未来和无限的机会。然而&#xff0c;我们又应该如何看待这些培训班呢&#xff1f;在培训班里学技术真的有用吗&#xff1f;本文将从不同角…

【Java进阶之路】HashMap源码分析(JDK1.8)

概述 JDK 1.8 对 HashMap 进行了比较大的优化&#xff0c;底层实现由之前的 “数组链表” 改为 “数组链表红黑树”&#xff0c;本文就 HashMap 的几个常用的重要方法和 JDK 1.8 之前的死循环问题展开学习讨论。 JDK 1.8 的 HashMap 的数据结构如下图所示&#xff0c;当链表节…

Docker 替代方案:适用于 SaaS 应用程序的 10 种 Docker 替代方案

Docker技术已经在基础设施管理领域引起了革命性的变化&#xff0c;以至于Docker现在已经成为容器的代名词。重要的是要理解&#xff0c;所有的Docker都是容器&#xff0c;但并非所有的容器都是Docker。虽然Docker是最常用的容器技术&#xff0c;但还有其他几种替代Docker的选择…