<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML5游动的线条动画特效</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #121212;
overflow: hidden;
font-family: 'Arial', sans-serif;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.controls {
position: absolute;
bottom: 20px;
left: 20px;
z-index: 100;
color: white;
}
.credit {
position: absolute;
bottom: 20px;
right: 20px;
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
z-index: 100;
}
.credit a {
color: #4CAF50;
text-decoration: none;
transition: color 0.3s;
}
.credit a:hover {
color: #FFD700;
}
button {
background: rgba(76, 175, 80, 0.7);
color: white;
border: none;
padding: 8px 15px;
margin: 5px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background: rgba(76, 175, 80, 0.9);
}
label {
display: block;
margin-top: 10px;
color: white;
}
input[type="range"] {
width: 200px;
margin-top: 5px;
}
</style>
</head>
<body>
<canvas id="lineCanvas"></canvas>
<div class="controls">
<button id="resetBtn">重置动画</button>
<button id="changeColorBtn">改变颜色</button>
<label for="lineCount">线条数量: <span id="lineCountValue">50</span></label>
<input type="range" id="lineCount" min="10" max="100" value="50">
<label for="speedControl">游动速度: <span id="speedValue">50</span></label>
<input type="range" id="speedControl" min="10" max="100" value="50">
</div>
<div class="credit">
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('lineCanvas');
const ctx = canvas.getContext('2d');
// 设置画布大小为窗口大小
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// 线条数组
let lines = [];
let lineCount = 50;
let speed = 50;
let colorHue = 200;
let animationId;
// 线条类
class Line {
constructor() {
this.reset();
this.history = [];
this.maxHistory = 20;
}
reset() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = Math.random() * 4 - 2;
this.vy = Math.random() * 4 - 2;
this.color = `hsl(${colorHue + Math.random() * 60}, 100%, 50%)`;
this.width = Math.random() * 2 + 1;
this.life = 100 + Math.random() * 100;
this.maxLife = this.life;
}
update() {
// 保存当前位置到历史记录
this.history.push({ x: this.x, y: this.y });
if (this.history.length > this.maxHistory) {
this.history.shift();
}
// 移动
this.x += this.vx * (speed / 50);
this.y += this.vy * (speed / 50);
// 随机改变方向
if (Math.random() < 0.02) {
this.vx += Math.random() * 0.4 - 0.2;
this.vy += Math.random() * 0.4 - 0.2;
}
// 限制速度
const maxSpeed = 2 + speed / 50;
const speedVal = Math.sqrt(this.vx * this.vx + this.vy * this.vy);
if (speedVal > maxSpeed) {
this.vx = (this.vx / speedVal) * maxSpeed;
this.vy = (this.vy / speedVal) * maxSpeed;
}
// 边界检查
if (this.x < 0 || this.x > canvas.width) {
this.vx *= -1;
}
if (this.y < 0 || this.y > canvas.height) {
this.vy *= -1;
}
// 生命周期减少
this.life--;
// 如果生命周期结束,重置线条
if (this.life <= 0) {
this.reset();
}
}
draw() {
if (this.history.length < 2) return;
ctx.beginPath();
// 绘制历史轨迹
for (let i = 1; i < this.history.length; i++) {
const alpha = i / this.history.length;
const prev = this.history[i - 1];
const curr = this.history[i];
ctx.moveTo(prev.x, prev.y);
ctx.lineTo(curr.x, curr.y);
// 线条颜色和透明度渐变
ctx.strokeStyle = this.color.replace(')', `, ${alpha})`).replace('hsl', 'hsla');
ctx.lineWidth = this.width * alpha;
ctx.stroke();
}
// 绘制当前点
ctx.beginPath();
ctx.arc(this.x, this.y, this.width, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
}
}
// 初始化线条
function initLines() {
lines = [];
for (let i = 0; i < lineCount; i++) {
lines.push(new Line());
}
}
// 动画循环
function animate() {
// 清除画布
ctx.fillStyle = 'rgba(18, 18, 18, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 更新和绘制线条
lines.forEach(line => {
line.update();
line.draw();
});
animationId = requestAnimationFrame(animate);
}
// 初始化并开始动画
initLines();
animate();
// 窗口大小调整时重置画布
window.addEventListener('resize', function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initLines();
});
// 控制按钮事件
document.getElementById('resetBtn').addEventListener('click', function() {
cancelAnimationFrame(animationId);
initLines();
animate();
});
document.getElementById('changeColorBtn').addEventListener('click', function() {
colorHue = Math.random() * 360;
lines.forEach(line => {
line.color = `hsl(${colorHue + Math.random() * 60}, 100%, 50%)`;
});
});
document.getElementById('lineCount').addEventListener('input', function() {
lineCount = parseInt(this.value);
document.getElementById('lineCountValue').textContent = lineCount;
initLines();
});
document.getElementById('speedControl').addEventListener('input', function() {
speed = parseInt(this.value);
document.getElementById('speedValue').textContent = speed;
});
});
</script>
</body>
</html>