Canvas实例篇:同心圆星轨动画

Canvas实例篇:同心圆星轨动画

在 Web 开发中,Canvas 元素为我们提供了强大的图形渲染能力。本文将通过一个「同心圆星轨动画」的实战案例,带大家深入了解 Canvas 在动画效果开发中的应用,同时掌握如何通过数学逻辑与图形渲染实现浪漫的星空视觉效果。

效果预览

同心圆星轨

代码实现

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>同心圆星轨</title>
		<style>
			body {
				margin: 0;
				overflow: hidden;
			}

			canvas {
				display: block;
			}
		</style>
	</head>
	<body>
		<canvas id="starCanvas"></canvas>
		<canvas id="trailCanvas"></canvas>

		<script>
			const starCanvas = document.getElementById('starCanvas');
			const starCtx = starCanvas.getContext('2d');
			const trailCanvas = document.getElementById('trailCanvas');
			const trailCtx = trailCanvas.getContext('2d');
			let stars = [];
			let center = {
				x: 0,
				y: 0
			};
			let rotationSpeed = 1;
			const STAR_COUNT = 200;

			// 设置trailCanvas的样式
			trailCanvas.style.position = 'absolute';
			trailCanvas.style.top = '0';
			trailCanvas.style.left = '0';

			// 初始化画布
			function resizeCanvas() {
				starCanvas.width = window.innerWidth;
				starCanvas.height = window.innerHeight;
				trailCanvas.width = window.innerWidth;
				trailCanvas.height = window.innerHeight;
				center.x = starCanvas.width * 0.5;
				center.y = starCanvas.height * 0.5;
			}
			window.addEventListener('resize', resizeCanvas);
			resizeCanvas();

			// 北极星位置
			function getPolarisPosition() {
				return {
					x: center.x,
					y: center.y
				};
			}

			// 星星颜色预设
			const starColors = [
				'#ffffff', // 白色
				'#a8c9ff', // 蓝色
				'#fffacd', // 黄色
				'#ffb6c1', // 粉色
				'#d3d3d3' // 灰色
			];

			// 创建星星
			function createStars() {
				stars = [];
				const polaris = getPolarisPosition();

				for (let i = 0; i < STAR_COUNT; i++) {
					const angle = Math.random() * Math.PI * 2;
					const distance = Math.random() * Math.min(starCanvas.width, starCanvas.height) * 0.45 + 20;

					stars.push({
						x: polaris.x + Math.cos(angle) * distance,
						y: polaris.y + Math.sin(angle) * distance,
						size: Math.random() * 1.5 + 0.5,
						color: starColors[Math.floor(Math.random() * starColors.length)],
						angle: angle,
						distance: distance,
						trailColor: `rgba(255, 255, 255, ${Math.random() * 0.1 + 0.05})`,
						blinkPhase: Math.random() * Math.PI * 2,
						blinkSpeed: Math.random() * 0.02 + 0.01,
						isPolaris: i === 0
					});
				}
			}

			// 绘制星空背景
			function drawBackground() {
				const gradient = starCtx.createRadialGradient(
					center.x, center.y, 0,
					center.x, center.y, Math.max(starCanvas.width, starCanvas.height)
				);

				gradient.addColorStop(0, '#050a1a');
				gradient.addColorStop(0.5, '#101833');
				gradient.addColorStop(1, '#1a244d');

				starCtx.fillStyle = gradient;
				starCtx.fillRect(0, 0, starCanvas.width, starCanvas.height);
			}

			// 更新星星位置
			function updateStars() {
				const polaris = getPolarisPosition();

				stars.forEach(star => {
					if (!star.isPolaris) {
						star.blinkPhase += star.blinkSpeed;
						const blinkFactor = Math.sin(star.blinkPhase) * 0.3 + 0.7;

						star.angle += 0.001 * rotationSpeed;
						star.x = polaris.x + Math.cos(star.angle) * star.distance;
						star.y = polaris.y + Math.sin(star.angle) * star.distance;

						star.currentBrightness = blinkFactor;
					}
				});
			}

			// 绘制星轨
			function drawTrails() {
				const polaris = getPolarisPosition();
				trailCtx.globalCompositeOperation = 'lighter';

				stars.forEach(star => {
					if (!star.isPolaris) {
						const startAngle = star.angle - 0.003 * rotationSpeed;
						const endAngle = star.angle;

						trailCtx.beginPath();
						trailCtx.arc(
							polaris.x, polaris.y, star.distance, startAngle, endAngle, false
						);
						trailCtx.strokeStyle = star.trailColor.replace('0.05', '0.2');
						trailCtx.lineWidth = 1.5;
						trailCtx.stroke();
						trailCtx.closePath();
					}
				});

				trailCtx.globalCompositeOperation = 'source-over';
			}

			// 绘制星星
			function drawStars() {
				stars.forEach(star => {
					// 绘制星星本体
					starCtx.beginPath();
					starCtx.arc(
						star.x,
						star.y,
						star.isPolaris ? star.size : star.size * (star.currentBrightness || 1),
						0,
						Math.PI * 2
					);
					starCtx.fillStyle = star.color;
					starCtx.fill();
					starCtx.closePath();

					// 添加星星光晕
					starCtx.beginPath();
					starCtx.arc(
						star.x,
						star.y,
						star.isPolaris ? 15 : star.size * 3 * (star.currentBrightness || 1),
						0,
						Math.PI * 2
					);
					starCtx.fillStyle = `${star.color}20`;
					starCtx.fill();
					starCtx.closePath();
				});
			}

			// 动画循环
			function animate() {

				updateStars();

				// 只在starCanvas上绘制背景和星星
				starCtx.clearRect(0, 0, starCanvas.width, starCanvas.height);
				drawBackground();
				drawStars();

				// 在trailCanvas上绘制星轨
				drawTrails();

				requestAnimationFrame(animate);
			}

			// 初始化
			function init() {
				drawBackground();
				createStars();
				animate();
			}

			// 启动动画
			init();
		</script>
	</body>
</html>

实现思路

  1. 使用两个 Canvas 元素,分别负责星星本体和星轨的渲染,避免混合绘制带来的性能问题。
  2. 北极星逻辑:标记第一个星星为北极星isPolaris: true,使其不参与旋转和闪烁,固定在一个位置。
  3. 通过Math.cos(angle)Math.sin(angle)计算星星在圆形轨道上的位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值