Last active
December 25, 2025 14:47
-
-
Save bczhc/d46209da4030e23dc0f9bb28a5540f60 to your computer and use it in GitHub Desktop.
Gemini thinking 写的李萨如曲线全面进化版 #cg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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