Skip to content

Instantly share code, notes, and snippets.

@bczhc
Last active December 25, 2025 14:47
Show Gist options
  • Select an option

  • Save bczhc/d46209da4030e23dc0f9bb28a5540f60 to your computer and use it in GitHub Desktop.

Select an option

Save bczhc/d46209da4030e23dc0f9bb28a5540f60 to your computer and use it in GitHub Desktop.
Gemini thinking 写的李萨如曲线全面进化版 #cg
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>利萨茹艺术生成器 - https://github.com/bczhc/Lissajous-curves/blob/master/a.htm 进化版</title>
<style>
:root {
--bg-color: #0a0a0c;
--panel-bg: rgba(255, 255, 255, 0.05);
--accent-color: #00f2ff;
}
body {
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: #fff;
font-family: 'Segoe UI', system-ui, sans-serif;
overflow: hidden;
display: flex;
}
/* 画布占据全屏 */
canvas {
display: block;
flex-grow: 1;
cursor: crosshair;
}
/* 控制面板 */
#controls {
position: absolute;
left: 20px;
top: 20px;
width: 280px;
padding: 20px;
background: var(--panel-bg);
backdrop-filter: blur(10px);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
user-select: none;
}
.control-group { margin-bottom: 15px; }
label { display: block; font-size: 12px; color: #888; margin-bottom: 5px; text-transform: uppercase; letter-spacing: 1px; }
input[type="range"] {
width: 100%;
accent-color: var(--accent-color);
cursor: pointer;
}
.value-display {
float: right;
color: var(--accent-color);
font-family: monospace;
}
.btn-group { display: flex; gap: 10px; margin-top: 20px; }
button {
flex: 1;
padding: 8px;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
color: white;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
button:hover { background: var(--accent-color); color: #000; }
.info {
position: absolute;
bottom: 20px;
right: 20px;
color: rgba(255,255,255,0.3);
font-size: 12px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="controls">
<h2 style="margin: 0 0 20px 0; font-weight: 300;">Lissajous <span style="color: var(--accent-color);">Art</span></h2>
<div class="control-group">
<label>X 频率 (Freq X) <span class="value-display" id="val-fx">1.0</span></label>
<input type="range" id="freqX" min="0" max="10" step="0.01" value="2">
</div>
<div class="control-group">
<label>Y 频率 (Freq Y) <span class="value-display" id="val-fy">1.0</span></label>
<input type="range" id="freqY" min="0" max="10" step="0.01" value="3">
</div>
<div class="control-group">
<label>绘制速度 <span class="value-display" id="val-speed">0.02</span></label>
<input type="range" id="speed" min="0.001" max="0.1" step="0.001" value="0.02">
</div>
<div class="control-group">
<label>相位偏移 <span class="value-display" id="val-phase">0.0</span></label>
<input type="range" id="phase" min="0" max="6.28" step="0.01" value="0">
</div>
<div class="control-group">
<label>尾迹长度 <span class="value-display" id="val-trail">0.05</span></label>
<input type="range" id="trail" min="0.01" max="0.5" step="0.01" value="0.05">
</div>
<div class="btn-group">
<button id="clearBtn">重置画布</button>
<button id="saveBtn">保存图片</button>
</div>
</div>
<div class="info">点击画布或调整滑块探索几何之美</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 状态管理
const state = {
time: 0,
freqX: 2,
freqY: 3,
speed: 0.02,
phase: 0,
trail: 0.05,
hue: 0
};
// 初始化尺寸
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// 调整大小时清空背景
ctx.fillStyle = '#0a0a0c';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
window.addEventListener('resize', resize);
resize();
// 绑定 UI
const inputs = ['freqX', 'freqY', 'speed', 'phase', 'trail'];
inputs.forEach(id => {
const el = document.getElementById(id);
el.oninput = (e) => {
state[id] = parseFloat(e.target.value);
document.getElementById(`val-${id}`).innerText = e.target.value;
};
});
document.getElementById('clearBtn').onclick = () => {
ctx.fillStyle = '#0a0a0c';
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
document.getElementById('saveBtn').onclick = () => {
const link = document.createElement('a');
link.download = 'art.png';
link.href = canvas.toDataURL();
link.click();
};
// 核心绘制逻辑
function draw() {
// 实现拖尾效果:每帧画一层半透明矩形覆盖,而不是完全清空
ctx.fillStyle = `rgba(10, 10, 12, ${state.trail})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) * 0.8;
// 计算当前点和下一个点,连成线更平滑
const steps = 5; // 每帧多画几个步骤,让曲线更连贯
ctx.lineWidth = 2;
ctx.lineCap = 'round';
for(let s = 0; s < steps; s++) {
const t1 = state.time;
state.time += state.speed / steps;
const t2 = state.time;
const x1 = centerX + Math.sin(t1 * state.freqX + state.phase) * radius;
const y1 = centerY + Math.sin(t1 * state.freqY) * radius;
const x2 = centerX + Math.sin(t2 * state.freqX + state.phase) * radius;
const y2 = centerY + Math.sin(t2 * state.freqY) * radius;
// 颜色随时间变化
state.hue = (state.hue + 0.1) % 360;
ctx.strokeStyle = `hsl(${state.hue}, 80%, 60%)`;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
requestAnimationFrame(draw);
}
// 启动动画
draw();
// 交互:点击画布随机化
canvas.onclick = () => {
state.freqX = (Math.random() * 5 + 1).toFixed(2);
state.freqY = (Math.random() * 5 + 1).toFixed(2);
document.getElementById('freqX').value = state.freqX;
document.getElementById('freqY').value = state.freqY;
document.getElementById('val-freqX').innerText = state.freqX;
document.getElementById('val-freqY').innerText = state.freqY;
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment