<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<title>❄️ 雪花生成器</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* === 固定设计尺寸:1080×1920 === */
body {
width: 100vw;
height: 100vh;
overflow: hidden;
position: fixed;
top: 0; left: 0;
background: #f0f0f0;
color: #333;
font-family: 'Microsoft YaHei', sans-serif;
}
/* 主容器:绝对定位 + 居中缩放 */
#app-container {
position: absolute;
top: 50%;
left: 50%;
transform-origin: center center;
width: 1080px;
height: 1920px;
}
/* === 标题区域 === */
.title-container {
background: linear-gradient(135deg, #004080, #0066cc);
color: #fff;
text-align: center;
padding: 25px 15px;
border-radius: 12px;
margin-bottom: 40px; /* 新增:标题与画布之间增加间距 */
}
h1 {
font-size: 3em;
font-weight: 500;
letter-spacing: 2px;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
/* === 雪花画布容器:严格正圆 === */
#canvas-wrapper {
width: 100%;
max-width: 800px;
aspect-ratio: 1 / 1; /* 关键:强制正圆 */
margin: 0 auto 50px auto; /* 修改:原只有水平居中,现在下方加 50px 空隙 */
background: transparent;
position: relative;
}
#canvas-container {
width: 100%;
height: 100%;
background: #000;
border-radius: 50%;
box-shadow:
inset 0 0 30px rgba(0, 0, 0, 0.8),
0 0 25px rgba(0, 0, 0, 0.3);
}
canvas {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: contain;
}
/* === 控制面板:固定紧凑高度,不拉伸 === */
.controls {
background: #fff;
border-radius: 16px;
padding: 20px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
color: #444;
margin-bottom: 20px; /* 可选:给底部留点呼吸空间 */
}
.slider-group {
margin-bottom: 18px;
}
label {
display: block;
font-size: 1.1em;
color: #0066cc;
margin-bottom: 6px;
font-weight: 500;
}
input[type="range"] {
width: 100%;
height: 8px;
background: rgba(0, 100, 200, 0.1);
border-radius: 4px;
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #0066cc;
cursor: pointer;
box-shadow: 0 0 12px rgba(0, 100, 200, 0.6);
}
.value-display {
text-align: right;
font-family: 'Courier New', monospace;
color: #0066cc;
font-size: 1.1em;
margin-top: 4px;
font-weight: bold;
}
/* === 双按钮容器 === */
.button-group {
display: flex;
gap: 10px;
margin-top: 10px;
}
.button-group button {
flex: 1;
padding: 16px;
font-size: 1.2em;
font-weight: 600;
letter-spacing: 2px;
color: #fff;
border: none;
border-radius: 12px;
cursor: pointer;
box-shadow: 0 6px 20px rgba(0, 100, 200, 0.5);
transition: all 0.3s ease;
}
#generate-btn {
background: #0066cc;
}
#back-home-btn {
background: #555;
}
#generate-btn:hover {
background: #0055aa;
transform: translateY(-2px);
}
#back-home-btn:hover {
background: #444;
transform: translateY(-2px);
}
</style>
</head>
<body>
<!-- 主容器:用于缩放 -->
<div id="app-container">
<!-- 标题 -->
<div class="title-container">
<h1>雪花生成器</h1>
</div>
<!-- 画布容器:严格正圆 -->
<div id="canvas-wrapper">
<div id="canvas-container">
<canvas id="snowflake-canvas"></canvas>
</div>
</div>
<!-- 控制面板 -->
<div class="controls">
<!-- 滑块组 -->
<div class="slider-group">
<label>递归深度(0~4级)</label>
<input type="range" id="depth" min="0" max="4" value="3" step="1"/>
<div class="value-display"><span id="depth-value">3</span></div>
</div>
<div class="slider-group">
<label>分支强度(0.0~1.0)</label>
<input type="range" id="bend" min="0" max="1.0" value="0.3" step="0.01"/>
<div class="value-display"><span id="bend-value">0.30</span></div>
</div>
<div class="slider-group">
<label>对称臂数(6/8/10/12)</label>
<input type="range" id="arms" min="6" max="12" value="6" step="2"/>
<div class="value-display"><span id="arms-value">6</span></div>
</div>
<div class="slider-group">
<label>整体旋转角度(0~360°)</label>
<input type="range" id="rotation" min="0" max="360" value="0" step="1"/>
<div class="value-display"><span id="rotation-value">0°</span></div>
</div>
<div class="slider-group">
<label>自然扰动(0.0~0.5)</label>
<input type="range" id="jitter" min="0" max="0.5" value="0.1" step="0.01"/>
<div class="value-display"><span id="jitter-value">0.10</span></div>
</div>
<!-- 双按钮 -->
<div class="button-group">
<button id="generate-btn">重新生成</button>
<button id="back-home-btn">返回首页</button>
</div>
</div>
</div>
<!-- 脚本 -->
<script>
const canvas = document.getElementById('snowflake-canvas');
const ctx = canvas.getContext('2d');
const depthSlider = document.getElementById('depth');
const bendSlider = document.getElementById('bend');
const armsSlider = document.getElementById('arms');
const rotationSlider = document.getElementById('rotation');
const jitterSlider = document.getElementById('jitter');
const depthValue = document.getElementById('depth-value');
const bendValue = document.getElementById('bend-value');
const armsValue = document.getElementById('arms-value');
const rotationValue = document.getElementById('rotation-value');
const jitterValue = document.getElementById('jitter-value');
const generateBtn = document.getElementById('generate-btn');
const backHomeBtn = document.getElementById('back-home-btn');
function updateValues() {
depthValue.textContent = depthSlider.value;
bendValue.textContent = parseFloat(bendSlider.value).toFixed(2);
armsValue.textContent = armsSlider.value;
rotationValue.textContent = rotationSlider.value + '°';
jitterValue.textContent = parseFloat(jitterSlider.value).toFixed(2);
drawSnowflake();
}
// 绑定滑块事件
[depthSlider, bendSlider, armsSlider, rotationSlider, jitterSlider]
.forEach(s => s.addEventListener('input', updateValues));
function drawSnowflake() {
const container = canvas.parentElement;
const rect = container.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
const size = Math.floor(rect.width * dpr);
canvas.width = size;
canvas.height = size;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.imageSmoothingEnabled = true;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = 2 * dpr;
ctx.strokeStyle = '#fff';
const cx = canvas.width / 2;
const cy = canvas.height / 2;
const radius = canvas.width * 0.42;
const depth = +depthSlider.value;
const bend = +bendSlider.value;
const numArms = +armsSlider.value;
const rotation = (+rotationSlider.value) * Math.PI / 180;
const jitter = +jitterSlider.value;
ctx.beginPath();
for (let i = 0; i < numArms; i++) {
const angle = rotation + i * (2 * Math.PI / numArms);
drawArm(ctx, cx, cy, radius, angle, depth, bend, jitter);
}
ctx.stroke();
}
function drawArm(ctx, x, y, length, angle, depth, bend, jitter) {
if (depth < 0) return;
const rand = () => (Math.random() - 0.5) * jitter;
const len = length / 3;
const x1 = x + len * Math.cos(angle);
const y1 = y + len * Math.sin(angle);
const dx = len * Math.cos(angle + Math.PI / 2);
const dy = len * Math.sin(angle + Math.PI / 2);
const x2 = x1 + dx * bend + dx * rand();
const y2 = y1 + dy * bend + dy * rand();
const x3 = x + 2 * len * Math.cos(angle);
const y3 = y + 2 * len * Math.sin(angle);
const x4 = x + length * Math.cos(angle);
const y4 = y + length * Math.sin(angle);
if (depth === 0) {
ctx.moveTo(x, y);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.lineTo(x4, y4);
} else {
const subBend = bend * (1 + rand() * 0.5);
const subJitter = jitter * 0.7;
drawArm(ctx, x, y, len, angle, depth - 1, subBend, subJitter);
drawArm(ctx, x1, y1, len, angle + Math.PI / 3, depth - 1, subBend * 0.7, subJitter);
drawArm(ctx, x2, y2, len, angle, depth - 1, subBend, subJitter);
drawArm(ctx, x3, y3, len, angle - Math.PI / 3, depth - 1, subBend * 0.7, subJitter);
}
}
// “重新生成”按钮
generateBtn.addEventListener('click', drawSnowflake);
// “返回首页”按钮
backHomeBtn.addEventListener('click', () => {
window.location.href = './index.html';
});
// 初始化
window.addEventListener('load', () => {
updateValues();
drawSnowflake();
});
// =============================
// ✅ 统一缩放逻辑(与首页、蚁群一致)
// =============================
function setupScaling() {
const container = document.getElementById('app-container');
function resize() {
const w = window.innerWidth;
const h = window.innerHeight;
const scaleX = w / 1080;
const scaleY = h / 1920;
const scale = Math.min(scaleX, scaleY);
container.style.transform = `translate(-50%, -50%) scale(${scale})`;
}
resize();
window.addEventListener('resize', resize);
setTimeout(resize, 100); // 防止移动端首次加载错位
setTimeout(resize, 500);
}
window.addEventListener('DOMContentLoaded', setupScaling);
</script>
</body>
</html>
你可以给这个程序也加一个通用响应式系统吗
最新发布