流星滑落效果

111 阅读4分钟

案例一

screenshots.gif

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>星空轨道动画</title>
    <style>
        #startrack { 
          width: 100vw; 
          height: 100vh; 
          display: block; 
        }
    </style>
</head>
<body>
    <canvas id="startrack"></canvas>
    <script>
        class StarTrack {
            constructor() {
                // 初始化画布和上下文
                this.canvas = document.getElementById('startrack');
                this.ctx = this.canvas.getContext('2d');
                this.cacheCanvas = document.createElement("canvas");
                this.cacheCtx = this.cacheCanvas.getContext("2d");
                
                // 动画相关属性
                this.stars = [];
                this.drawTimes = 0;
                this.rotationSpeed = 0.025;
                
                // 初始化
                this.initCanvas();
                this.createStars(20000);
                this.setupEventListeners();
                this.startAnimation();
            }
            
            // 初始化画布尺寸
            initCanvas() {
                this.cw = this.canvas.offsetWidth;
                this.ch = this.canvas.offsetHeight;
                this.canvas.width = this.cw;
                this.canvas.height = this.ch;
                
                // 设置缓存画布
                this.longside = Math.max(this.cw, this.ch);
                this.cacheCanvas.width = 2.6 * this.longside;
                this.cacheCanvas.height = 2.6 * this.longside;
                
                // 设置初始样式和旋转中心(左上角)
                this.ctx.fillStyle = "rgba(21, 21, 21, 1)";
                this.ctx.fillRect(0, 0, this.cw, this.ch);
                this.ctx.lineCap = "round";
                this.ctx.translate(this.cw, 0); // 旋转中心设为左上角
            }
            
            // 随机数生成
            rand(min, max) {
                return min + Math.round(Math.random() * (max - min));
            }
            
            // 创建随机颜色
            randomColor() {
                return `rgba(
                    ${this.rand(120, 255)},
                    ${this.rand(120, 255)},
                    ${this.rand(120, 255)},
                    ${this.rand(30, 100) / 100}
                )`;
            }
            
            // 创建单个星星
            createStar() {
                return {
                    x: this.rand(-this.cacheCanvas.width, this.cacheCanvas.width),
                    y: this.rand(-this.cacheCanvas.height, this.cacheCanvas.height),
                    size: 1,
                    color: this.randomColor()
                };
            }
            
            // 创建多个星星
            createStars(count) {
                for (let i = 0; i < count; i++) {
                    this.stars.push(this.createStar());
                }
                this.drawStarsToCache();
            }
            
            // 绘制星星到缓存画布
            drawStarsToCache() {
                this.stars.forEach(star => {
                    this.cacheCtx.beginPath();
                    this.cacheCtx.arc(star.x, star.y, star.size, 0, 2 * Math.PI);
                    this.cacheCtx.fillStyle = star.color;
                    this.cacheCtx.fill();
                    this.cacheCtx.closePath();
                });
            }
            
            // 从缓存绘制到主画布
            drawFromCache() {
                this.ctx.drawImage(
                    this.cacheCanvas, 
                    -this.cacheCanvas.width / 2, 
                    -this.cacheCanvas.height / 2
                );
            }
            
            // 旋转画布
            rotateCanvas(angle) {
                this.ctx.rotate(angle * Math.PI / 180);
            }
            
            // 动画循环
            loop() {
                this.drawFromCache();
                
                // 逐渐添加轨迹效果
                if (this.drawTimes > 150 && this.drawTimes % 8 === 0) {
                    this.ctx.fillStyle = "rgba(0, 0, 0, 0.04)";
                    this.ctx.fillRect(
                        -3 * this.longside, 
                        -3 * this.longside, 
                        6 * this.longside, 
                        6 * this.longside
                    );
                }
                
                this.rotateCanvas(this.rotationSpeed);
                this.drawTimes++;
            }
            
            // 开始动画
            startAnimation() {
                const animate = () => {
                    requestAnimationFrame(animate);
                    this.loop();
                };
                animate();
            }
            
            // 改变旋转速度
            changeRotationSpeed() {
                this.rotationSpeed = this.rand(1, 100) * 0.001;
            }
            
            // 设置事件监听器
            setupEventListeners() {
                // 窗口大小改变时重新初始化
                window.addEventListener('resize', () => this.initCanvas());
                // 可以添加点击事件来改变旋转速度
                // this.canvas.addEventListener('click', () => this.changeRotationSpeed());
            }
        }
        
        // 启动动画
        window.onload = () => new StarTrack();
    </script>
</body>
</html>
    

案例二 -类幻灯片打字机效果

screenshots.gif

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多行文本擦除效果</title>
    <style>
        body{
            background-color: #000;
            color: #fff; 
        }
        .container {
            width: 80%;
            margin: 1em auto;
            position: relative;
            padding: 10px;
            line-height: 25px;
            letter-spacing: 2px;
            text-indent: 2em;
        }
        .eraser{
            position: absolute;
            inset: 0;
            /* left: 0;
            top: 0; */
        }
        .text{
            background: linear-gradient(to right, #0000 var(--p), #000 calc(var(--p) + 100px));
            color: transparent;
            animation: erase 10s forwards;
        }
        /* 自定义属性 */
        @property --p {
            syntax: '<percentage>';
            initial-value: 0%;
            inherits: false;
        }
        /* 动画只对属性有效,对变量无效 */
        @keyframes erase {
            to{
                --p: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <p class="eraser">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Incidunt, veniam culpa. Beatae repellat eos repudiandae ipsum
            eum nesciunt neque. Fugiat quaerat beatae rerum fugit earum eos laudantium eveniet ullam veritatis.
            Pariatur, repudiandae repellat. Dolorum vero magni accusantium fuga corporis. Nihil natus minima pariatur voluptatum,
            consectetur unde, quo vel non vero ducimus consequuntur et culpa perferendis? Vitae eius ipsa facere excepturi.
            Quia, blanditiis quis? Omnis illo modi magni alias temporibus nesciunt animi ipsum ullam delectus explicabo, quia,
            consequuntur error, molestiae quas laborum assumenda non voluptatem soluta quisquam officia reiciendis vitae qui!
            Voluptatibus, minima eius debitis maxime earum, voluptates voluptate optio distinctio mollitia commodi assumenda
            pariatur. Veritatis eveniet nulla ducimus ea laborum ut nihil soluta, distinctio necessitatibus in, molestias, placeat
            consequuntur aut.
            Minima voluptatem odit eligendi architecto nam molestias voluptates repellendus quidem explicabo hic sed placeat
            accusamus harum quam, exercitationem mollitia error inventore ullam cupiditate fuga, rerum nesciunt. Voluptatibus
            dignissimos atque explicabo?
        </p>
        <p class="eraser">
            <span class="text">
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Incidunt, veniam culpa. Beatae repellat eos repudiandae ipsum
                eum nesciunt neque. Fugiat quaerat beatae rerum fugit earum eos laudantium eveniet ullam veritatis.
                Pariatur, repudiandae repellat. Dolorum vero magni accusantium fuga corporis. Nihil natus minima pariatur voluptatum,
                consectetur unde, quo vel non vero ducimus consequuntur et culpa perferendis? Vitae eius ipsa facere excepturi.
                Quia, blanditiis quis? Omnis illo modi magni alias temporibus nesciunt animi ipsum ullam delectus explicabo, quia,
                consequuntur error, molestiae quas laborum assumenda non voluptatem soluta quisquam officia reiciendis vitae qui!
                Voluptatibus, minima eius debitis maxime earum, voluptates voluptate optio distinctio mollitia commodi assumenda
                pariatur. Veritatis eveniet nulla ducimus ea laborum ut nihil soluta, distinctio necessitatibus in, molestias, placeat
                consequuntur aut.
                Minima voluptatem odit eligendi architecto nam molestias voluptates repellendus quidem explicabo hic sed placeat
                accusamus harum quam, exercitationem mollitia error inventore ullam cupiditate fuga, rerum nesciunt. Voluptatibus
                dignissimos atque explicabo?
            </span>
        </p>
    </div>
    
</body>
</html>

案例三、css衣服换色

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    <style>
        body{
            /*overflow: hidden;*/
        }
        .container{
            width: 300px;
            height: 500px;
            position: relative;
            margin: 200px auto;
        }
        .model{
            display: block;
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
        .skirt{
            position: absolute;
            inset: 0;
            background: #f40;
            mask: url("./cloth.png") no-repeat 50% 50% / cover;
            mix-blend-mode: multiply; //正片叠底
        }
    </style>
</head>
<body>
    <div class="container">
        <img src="./model.jpeg" alt="" class="model">
        <div class="skirt"></div>
    </div>
</body>
</html>

image.png

<template>
  <el-checkbox-group v-model="selectedAccounts" @change="handleSelectionChange">
        <div class="card" v-for="item in list" :key="item.id">
          <div>{{ item.name }}</div>
          <div>{{ item.id }}</div>
          <el-checkbox 
          class="hidden-checkbox" 
          :label="item.id" :disabled="isDisabled(item.id)"></el-checkbox>
        </div>
  </el-checkbox-group>
</template>
export default{
  data(){
      return {
        selectedAccounts: [],
        list:[
            { id: '3123200023203232', name: '张三' },
            { id: '3123200023203233', name: '李四' },
            { id: '3123200023203234', name: '王五' },
            { id: '3123200023203235', name: '赵六' },
            { id: '3123200023203236', name: '钱七' },
            { id: '3123200023203237', name: '孙八' },
            { id: '3123200023203238', name: '周九' },
        ],
      }
  }
  methods: {
        handleSelectionChange(selected){
            if (selected.length > 5) {
                // 如果超过5个,自动截断到前5个
                this.selectedAccounts = selected.slice(0, 5);
            } 
            console.log(this.selectedAccounts);
        },
        // 判断是否禁用某个选项
        isDisabled(id) {
        // 如果已选满5个且当前选项未被选中,则禁用
            return this.selectedAccounts.length >= 5 && !this.selectedAccounts.includes(id);
        },
    }
}
<style lang="less" scoped>
    .card {
        border: 1px solid #ebeef5;
        border-radius: 4px;
        padding: 12px;
        margin-bottom: 10px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        background-color: #fff;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    }
    .card:hover {
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
    }
    .card div:first-child {
        font-weight: bold;
        font-size: 14px;
    }

    .card div:nth-child(2) {
        color: #909399;
        font-size: 13px;
    }
    .hidden-checkbox /deep/ .el-checkbox__label{
        display: none;
    }
</style>

css属性

style{
 background-blend-mode: multiply; //混合
 filter: drop-shadow(2px, 2px, 4px rgba(0, 0, 0, 0.16)); //像素点阴影 box-shadow: 盒子阴影
}