JSDM

HTML

97
 
1
<!DOCTYPE html>
You don't need a DOCTYPE on CodePen. Just put here what you would normally put in the <body>.
2
<html>
3
<head>
4
    <title>渐显白色流星群</title>
5
    <style>
6
        body { margin: 0; overflow: hidden; background: #000; }
7
        canvas { display: block; }
8
    </style>
9
</head>
10
<body>
11
    <canvas id="canvas"></canvas>
12
    <script>
13
        const canvas = document.getElementById('canvas');
14
        const ctx = canvas.getContext('2d');
15
        canvas.width = window.innerWidth;
16
        canvas.height = window.innerHeight;
17
18
        class Meteor {
19
            constructor() {
20
                this.reset(true);
21
            }
22
23
            reset() {
24
                this.x = canvas.width/2;
25
                this.y = canvas.height/2;
26
                this.angle = Math.random() * Math.PI * 2;
27
                this.speed = Math.random() * 1.2 + 0.3;      // 更慢的速度范围
28
                this.size = Math.random() * 2 + 1;
29
                this.trail = [];
30
                this.trailLength = Math.floor(Math.random() * 25 + 15); // 更长尾迹
31
                this.maxDistance = Math.max(canvas.width, canvas.height) / 2; // 动态最大距离
32
            }
33
34
            update() {
35
                // 记录轨迹点
36
                this.trail.push({ x: this.x, y: this.y })
37
                if(this.trail.length > this.trailLength) this.trail.shift()
38
39
                // 计算新位置
40
                this.x += Math.cos(this.angle) * this.speed;
41
                this.y += Math.sin(this.angle) * this.speed;
42
43
                // 基于距离计算透明度
44
                const dx = this.x - canvas.width/2;
45
                const dy = this.y - canvas.height/2;
46
                const distance = Math.sqrt(dx*dx + dy*dy);
47
                this.alpha = Math.min(distance / this.maxDistance, 1); // 距离渐显
48
49
                // 重置条件(仅位置判断)
50
                if(this.x < 0 || this.x > canvas.width || 
51
                   this.y < 0 || this.y > canvas.height) {
52
                    this.reset();
53
                }
54
            }
55
56
            draw() {
57
                // 绘制尾迹
58
                this.trail.forEach((pos, index) => {
59
                    const ratio = index/this.trailLength;
60
                    ctx.beginPath();
61
                    ctx.arc(pos.x, pos.y, this.size * ratio, 0, Math.PI*2);
62
                    ctx.fillStyle = `rgba(255, 255, 255, ${ratio * 0.6 * this.alpha})`; // 复合透明度
63
                    ctx.fill();
64
                });
65
66
                // 绘制头部
67
                ctx.beginPath();
68
                ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
69
                ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
70
                ctx.fill();
71
            }
72
        }
73
74
        const meteors = Array(150).fill().map(() => new Meteor());
75
76
        function animate() {
77
            ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';  // 更淡的尾迹残留
78
            ctx.fillRect(0, 0, canvas.width, canvas.height);
79
80
            meteors.forEach(meteor => {
81
                meteor.update();
82
                meteor.draw();
83
            });
84
85
            requestAnimationFrame(animate);
86
        }
87
88
        window.addEventListener('resize', () => {
89
            canvas.width = window.innerWidth;
90
            canvas.height = window.innerHeight;
91
            meteors.forEach(meteor => meteor.reset());
92
        });
93
94
        animate();
95
    </script>
96
</body>
97
</html>
!

CSS

xxxxxxxxxx
1
 
1
? ?
? ?
必须是有效的URL
+ 添加另一个资源

JS

1
 
1
必须是有效的URL
+ 添加另一个资源
Close

文件管理 点击文件查看URL

图片

  1. 暂无文件

CSS

  1. 暂无文件

JavaScript

  1. 暂无文件

其他

  1. 暂无文件
拖动文件到上面的区域或者:
加载中 ..................