<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas发光夜景动画场景特效</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #000;
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="nightSceneCanvas"></canvas>
<div class="controls">
<button id="resetBtn">重置场景</button>
<button id="changeColorBtn">改变灯光颜色</button>
<label for="lightCount">灯光数量: <span id="lightCountValue">50</span></label>
<input type="range" id="lightCount" min="10" max="150" 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('nightSceneCanvas');
const ctx = canvas.getContext('2d');
// 设置画布大小为窗口大小
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// 场景元素
let lights = [];
let stars = [];
let buildings = [];
let lightCount = 50;
let speed = 50;
let colorHue = 200;
let animationId;
// 灯光类
class Light {
constructor() {
this.reset();
}
reset() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height * 0.7 + canvas.height * 0.3;
this.size = Math.random() * 10 + 5;
this.color = `hsl(${colorHue + Math.random() * 60}, 100%, 50%)`;
this.alpha = Math.random() * 0.5 + 0.5;
this.speed = Math.random() * 0.5 + 0.5;
this.flickerSpeed = Math.random() * 0.05 + 0.05;
this.flickerAmount = Math.random() * 0.3 + 0.7;
this.time = Math.random() * 100;
}
update() {
this.time += this.flickerSpeed * (speed / 50);
this.alpha = (Math.sin(this.time) * 0.1 + 0.9) * this.flickerAmount;
// 轻微移动
this.x += (Math.random() - 0.5) * 0.2 * (speed / 50);
this.y += (Math.random() - 0.5) * 0.2 * (speed / 50);
// 边界检查
if (this.x < 0) this.x = canvas.width;
if (this.x > canvas.width) this.x = 0;
if (this.y < canvas.height * 0.3) this.y = canvas.height * 0.3;
if (this.y > canvas.height) this.y = canvas.height * 0.3;
}
draw() {
// 绘制光晕
const gradient = ctx.createRadialGradient(
this.x, this.y, 0,
this.x, this.y, this.size * 3
);
gradient.addColorStop(0, this.color.replace(')', `, ${this.alpha})`).replace('hsl', 'hsla'));
gradient.addColorStop(1, this.color.replace(')', `, 0)`).replace('hsl', 'hsla'));
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * 3, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
// 绘制中心亮点
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * 0.5, 0, Math.PI * 2);
ctx.fillStyle = this.color.replace(')', `, ${this.alpha})`).replace('hsl', 'hsla');
ctx.fill();
}
}
// 星星类
class Star {
constructor() {
this.reset();
}
reset() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height * 0.3;
this.size = Math.random() * 2 + 1;
this.alpha = Math.random() * 0.8 + 0.2;
this.speed = Math.random() * 0.2 + 0.1;
this.flickerSpeed = Math.random() * 0.02 + 0.01;
this.time = Math.random() * 100;
}
update() {
this.time += this.flickerSpeed * (speed / 50);
this.alpha = (Math.sin(this.time) * 0.1 + 0.9) * 0.8;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
ctx.fill();
}
}
// 建筑类
class Building {
constructor() {
this.reset();
}
reset() {
this.x = Math.random() * canvas.width;
this.width = Math.random() * 100 + 50;
this.height = Math.random() * 200 + 100;
this.color = `hsl(0, 0%, ${Math.random() * 10 + 5}%)`;
this.windows = [];
// 创建窗户
const windowCols = Math.floor(this.width / 15);
const windowRows = Math.floor(this.height / 20);
for (let i = 0; i < windowCols; i++) {
for (let j = 0; j < windowRows; j++) {
if (Math.random() > 0.3) { // 70%的窗户会亮灯
this.windows.push({
x: this.x + i * 15 + 5,
y: canvas.height - this.height + j * 20 + 5,
size: 5,
on: Math.random() > 0.5,
flicker: Math.random() > 0.7
});
}
}
}
}
update() {
// 更新窗户状态
this.windows.forEach(window => {
if (window.flicker && Math.random() < 0.05 * (speed / 50)) {
window.on = !window.on;
}
});
}
draw() {
// 绘制建筑轮廓
ctx.fillStyle = this.color;
ctx.fillRect(this.x, canvas.height - this.height, this.width, this.height);
// 绘制窗户
this.windows.forEach(window => {
if (window.on) {
ctx.fillStyle = `hsla(${colorHue + Math.random() * 30}, 100%, 50%, ${Math.random() * 0.5 + 0.5})`;
ctx.fillRect(window.x, window.y, window.size, window.size);
} else {
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
ctx.fillRect(window.x, window.y, window.size, window.size);
}
});
}
}
// 初始化场景
function initScene() {
// 创建灯光
lights = [];
for (let i = 0; i < lightCount; i++) {
lights.push(new Light());
}
// 创建星星
stars = [];
for (let i = 0; i < 200; i++) {
stars.push(new Star());
}
// 创建建筑
buildings = [];
for (let i = 0; i < 10; i++) {
buildings.push(new Building());
}
}
// 动画循环
function animate() {
// 清除画布
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制渐变夜空背景
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0, '#000814');
gradient.addColorStop(1, '#001d3d');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 更新和绘制星星
stars.forEach(star => {
star.update();
star.draw();
});
// 更新和绘制建筑
buildings.forEach(building => {
building.update();
building.draw();
});
// 更新和绘制灯光
lights.forEach(light => {
light.update();
light.draw();
});
animationId = requestAnimationFrame(animate);
}
// 初始化并开始动画
initScene();
animate();
// 窗口大小调整时重置画布
window.addEventListener('resize', function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initScene();
});
// 控制按钮事件
document.getElementById('resetBtn').addEventListener('click', function() {
cancelAnimationFrame(animationId);
initScene();
animate();
});
document.getElementById('changeColorBtn').addEventListener('click', function() {
colorHue = Math.random() * 360;
lights.forEach(light => {
light.color = `hsl(${colorHue + Math.random() * 60}, 100%, 50%)`;
});
});
document.getElementById('lightCount').addEventListener('input', function() {
lightCount = parseInt(this.value);
document.getElementById('lightCountValue').textContent = lightCount;
initScene();
});
document.getElementById('speedControl').addEventListener('input', function() {
speed = parseInt(this.value);
document.getElementById('speedValue').textContent = speed;
});
});
</script>
</body>
</html>