html
复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Canvas 内部圆形波浪</title>
<style>
canvas {
border: 5px solid black;
}
</style>
</head>
<body>
<canvas id="waveCanvas" width="400" height="400"></canvas>
<label for="heightPercentageInput">Wave Height:</label>
<input
type="range"
id="heightPercentageInput"
min="0"
max="1"
step="0.01"
value="0.5"
/>
<label for="speedInput">Wave Speed:</label>
<input
type="range"
id="speedInput"
min="0.1"
max="5"
step="0.1"
value="1"
/>
<label for="opacity1Input">Wave 1 Opacity:</label>
<input
type="range"
id="opacity1Input"
min="0"
max="1"
step="0.01"
value="0.5"
/>
<label for="opacity2Input">Wave 2 Opacity:</label>
<input
type="range"
id="opacity2Input"
min="0"
max="1"
step="0.01"
value="0.2"
/>
<script>
const canvas = document.getElementById("waveCanvas");
const ctx = canvas.getContext("2d");
let time = 0;
function drawWave() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除之前的画面
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 100; // 圆的半径
// 创建径向渐变
const gradient = ctx.createRadialGradient(
centerX,
centerY,
radius * 0.9,
centerX,
centerY,
radius
);
gradient.addColorStop(0, "rgba(0, 255, 200, 0.1)");
gradient.addColorStop(1, "rgba(0, 255, 200, 0)");
ctx.save();
ctx.scale(0.9, 0.9);
ctx.translate(20, 20);
// 设置渐变填充样式并绘制圆
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.fillStyle = gradient; // 使用渐变填充颜色
ctx.fill(); // 填充圆
ctx.clip(); // Clip to the circle
// 获取输入值
const heightPercentageInput = document.getElementById(
"heightPercentageInput"
);
const speedInput = document.getElementById("speedInput");
const opacity1Input = document.getElementById("opacity1Input");
const opacity2Input = document.getElementById("opacity2Input");
const waveHeight = radius * heightPercentageInput.value; // Control wave height
const waveSpeed = speedInput.value; // Control wave speed
const opacity1 = opacity1Input.value; // Control wave 1 opacity
const opacity2 = opacity2Input.value; // Control wave 2 opacity
// 画第一条波浪
ctx.beginPath();
ctx.moveTo(0, centerY + radius);
for (let i = 0; i <= canvas.width; i++) {
const y =
Math.sin(i * 0.05 + time * waveSpeed) * 10 +
waveHeight * 0.6 +
centerY;
ctx.lineTo(i, Math.min(y, centerY + radius)); // 确保在圆内
}
ctx.lineTo(canvas.width, centerY + radius);
ctx.lineTo(0, centerY + radius);
ctx.fillStyle = `rgba(0, 255, 200, ${opacity1})`;
ctx.fill();
// 画第二条波浪
ctx.beginPath();
ctx.moveTo(0, centerY + radius);
for (let i = 0; i <= canvas.width; i++) {
const y =
Math.sin(i * 0.04 + time * (waveSpeed * 1.5) + Math.PI / 2) * 10 +
waveHeight * 0.55 +
centerY;
ctx.lineTo(i, Math.min(y, centerY + radius)); // 确保在圆内
}
ctx.lineTo(canvas.width, centerY + radius);
ctx.lineTo(0, centerY + radius);
ctx.fillStyle = `rgba(0, 255, 200, ${opacity2})`; // Make it a bit more opaque
ctx.fill();
ctx.restore();
// 绘制带有轨迹的旋转弧线
const angle = (time / 5) % (Math.PI * 2); // 根据时间计算当前角度
const trailLength = Math.PI * 0.75; // 弧线的长度
const segments = 333; // 轨迹的段数
// 绘制轨迹
for (let i = 0; i < segments; i++) {
const alpha = 1 - i / segments; // 渐变透明度
const startAngle = angle - (i * trailLength) / segments; // 每段的起始角度
const endAngle = startAngle + trailLength / segments; // 每段的结束角度
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle); // 绘制每段弧线
ctx.strokeStyle = `rgba(59, 144, 125, ${alpha})`; // 设置弧线颜色,带透明度
ctx.lineWidth = 4 - (3 * i) / segments; // 渐变线宽
ctx.stroke(); // 描绘弧线
}
// 设定一个小的偏移角度
const offsetAngle = 2.31; // 调整这个值可以改变"往前"的距离
// 绘制位于弧线末尾的点(移前)
const dotX =
centerX + radius * Math.cos(angle + trailLength - offsetAngle); // 计算点的X坐标
const dotY =
centerY + radius * Math.sin(angle + trailLength - offsetAngle); // 计算点的Y坐标
ctx.beginPath();
ctx.arc(dotX, dotY, 4, 0, Math.PI * 2); // 绘制点
ctx.fillStyle = "#3b907d"; // 点的填充颜色
ctx.fill(); // 填充点
// 增加时间以实现动画
time += 0.1;
// 递归调用以实现动画效果
requestAnimationFrame(drawWave);
}
drawWave(); // 开始绘制
</script>
</body>
</html>