<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
body {
width: 100%;
height: 100vh;
background-image: linear-gradient(-225deg, #2C73D2 0%, #36156b 29%, #B0A8B9 100%);
}
canvas {
position: fixed;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas></canvas>
</body>
<script>
const STAR_COLOR = '#fff';
const STAR_SIZE = 3;
const STAR_MIN_SCALE = 0.2;
const OVERFLOW_THRESHOLD = 50;
const STAR_COUNT = (window.innerWidth + window.innerHeight) / 8;
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
let scale = 1;
let width;
let height;
let stars = [];
let pointerX;
let pointerY;
let velocity = {
x: 0,
y: 0,
tx: 0,
ty: 0,
z: 0.0009
};
let touchInput = false;
generate();
resize();
step();
window.onresize = resize;
canvas.onmousemove = onMouseMove;
canvas.ontouchmove = onTouchMove;
canvas.ontouchend = onMouseLeave;
document.onmouseleave = onMouseLeave;
function generate() {
for (let i = 0; i < STAR_COUNT; i++) {
stars.push({
x: 0,
y: 0,
z: STAR_MIN_SCALE + Math.random() * (1 - STAR_MIN_SCALE),
});
}
}
function placeStar(star) {
star.x = Math.random() * width;
star.y = Math.random() * height;
}
function recycleStar(star) {
let direction = 'z';
let vx = Math.abs(velocity.x);
let vy = Math.abs(velocity.y);
if (vx > 1 || vy > 1) {
let axis;
if (vx > vy) {
axis = Math.random() < vx / (vx + vy) ? 'h' : 'v';
} else {
axis = Math.random() < vy / (vx + vy) ? 'v' : 'h';
}
if (axis === 'h') {
direction = velocity.x > 0 ? 'l' : 'r';
} else {
direction = velocity.y > 0 ? 't' : 'b';
}
}
star.z = STAR_MIN_SCALE + Math.random() * (1 - STAR_MIN_SCALE);
if (direction === 'z') {
star.z = 0.1;
star.x = Math.random() * width;
star.y = Math.random() * height;
} else if (direction === 'l') {
star.x = -OVERFLOW_THRESHOLD;
star.y = height * Math.random();
} else if (direction === 'r') {
star.x = width + OVERFLOW_THRESHOLD;
star.y = height * Math.random();
} else if (direction === 't') {
star.x = width * Math.random();
star.y = -OVERFLOW_THRESHOLD;
} else if (direction === 'b') {
star.x = width * Math.random();
star.y = height + OVERFLOW_THRESHOLD;
}
}
function resize() {
scale = window.devicePixelRatio || 1;
width = window.innerWidth * scale;
height = window.innerHeight * scale;
canvas.width = width;
canvas.height = height;
stars.forEach(placeStar);
}
function step() {
context.clearRect(0, 0, width, height);
update();
render();
requestAnimationFrame(step);
}
function update() {
velocity.tx *= 0.96;
velocity.ty *= 0.96;
velocity.x += (velocity.tx - velocity.x) * 0.8;
velocity.y += (velocity.ty - velocity.y) * 0.8;
stars.forEach((star) => {
star.x += velocity.x * star.z;
star.y += velocity.y * star.z;
star.x += (star.x - width / 2) * velocity.z * star.z;
star.y += (star.y - height / 2) * velocity.z * star.z;
star.z += velocity.z;
if (
star.x < -OVERFLOW_THRESHOLD ||
star.x > width + OVERFLOW_THRESHOLD ||
star.y < -OVERFLOW_THRESHOLD ||
star.y > height + OVERFLOW_THRESHOLD
) {
recycleStar(star);
}
});
}
function render() {
stars.forEach((star) => {
context.beginPath();
context.lineCap = 'round';
context.lineWidth = STAR_SIZE * star.z * scale;
context.globalAlpha = 0.5 + 0.5 * Math.random();
context.strokeStyle = STAR_COLOR;
context.beginPath();
context.moveTo(star.x, star.y);
let tailX = velocity.x * 2;
let tailY = velocity.y * 2;
if (Math.abs(tailX) < 0.1) tailX = 0.5;
if (Math.abs(tailY) < 0.1) tailY = 0.5;
context.lineTo(star.x + tailX, star.y + tailY);
context.stroke();
});
}
function movePointer(x, y) {
if (typeof pointerX === 'number' && typeof pointerY === 'number') {
let ox = x - pointerX;
let oy = y - pointerY;
velocity.tx = velocity.tx + (ox / 8) * scale * (touchInput ? 1 : -1);
velocity.ty = velocity.ty + (oy / 8) * scale * (touchInput ? 1 : -1);
}
pointerX = x;
pointerY = y;
}
function onMouseMove(event) {
touchInput = false;
movePointer(event.clientX, event.clientY);
}
function onTouchMove(event) {
touchInput = true;
movePointer(event.touches[0].clientX, event.touches[0].clientY, true);
event.preventDefault();
}
function onMouseLeave() {
pointerX = null;
pointerY = null;
}
</script>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256