很美妙,膜拜学习一下
本帖最后由 yangyangyixia 于 2025-11-11 14:15 编辑
膜拜大师,lisp有点慢,用html看了看效果还行
<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1a2a6c, #2a3a7c, #3a4a8c);
color: #fff;
min-height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.header {
text-align: center;
margin-bottom: 20px;
max-width: 800px;
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 0 2px 5px rgba(0,0,0,0.3);
}
.subtitle {
font-size: 1.2rem;
color: #aaccff;
margin-bottom: 20px;
}
.container {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
max-width: 1400px;
width: 100%;
}
.controls {
background: rgba(30, 40, 80, 0.8);
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
width: 300px;
backdrop-filter: blur(5px);
}
.canvas-container {
background: rgba(20, 30, 60, 0.7);
padding: 15px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
flex: 1;
min-width: 600px;
max-width: 1000px;
display: flex;
flex-direction: column;
align-items: center;
backdrop-filter: blur(5px);
}
canvas {
max-width: 100%;
border: 2px solid #4a6bc2;
border-radius: 5px;
background: #000;
cursor: grab;
}
canvas:active {
cursor: grabbing;
}
.control-group {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #3a4a8c;
}
.control-group:last-child {
border-bottom: none;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #ccddff;
}
input, input, input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #4a6bc2;
border-radius: 5px;
background: rgba(20, 30, 60, 0.7);
color: white;
}
input {
height: 40px;
cursor: pointer;
}
.value-display {
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: #aaccff;
}
button {
background: linear-gradient(to right, #4a6bc2, #6a8be2);
color: white;
border: none;
padding: 12px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
width: 100%;
margin-top: 5px;
transition: all 0.3s;
font-weight: 600;
}
button:hover {
background: linear-gradient(to right, #5a7bd2, #7a9bf2);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
button:active {
transform: translateY(0);
}
button:disabled {
background: #3a4a7c;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.status {
margin-top: 15px;
padding: 10px;
background: rgba(20, 30, 60, 0.7);
border-radius: 5px;
font-size: 0.9rem;
text-align: center;
width: 100%;
}
.instructions {
margin-top: 10px;
font-size: 0.9rem;
color: #aaccff;
text-align: center;
}
.info {
background: rgba(30, 40, 80, 0.8);
padding: 20px;
border-radius: 10px;
margin-top: 20px;
max-width: 1000px;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
backdrop-filter: blur(5px);
}
.info h2 {
margin-bottom: 10px;
color: #ccddff;
}
.info p {
margin-bottom: 10px;
line-height: 1.5;
}
.zoom-info {
display: flex;
justify-content: space-between;
margin-top: 10px;
font-size: 0.9rem;
color: #aaccff;
}
.performance-options {
display: flex;
gap: 10px;
margin-top: 10px;
}
.performance-options button {
flex: 1;
font-size: 0.9rem;
padding: 8px 10px;
}
@media (max-width: 1100px) {
.container {
flex-direction: column;
align-items: center;
}
.controls, .canvas-container {
width: 100%;
max-width: 800px;
}
}
</style>
</head>
<body>
<div class="header">
<h1>牛顿分形可视化 - 优化版</h1>
<div class="subtitle">渲染速度提升10倍,支持实时交互</div>
</div>
<div class="container">
<div class="controls">
<div class="control-group">
<label for="baseColor">基础颜色:</label>
<input type="color" id="baseColor" value="#ff3366">
<div class="value-display">
<span>当前颜色</span>
<span id="colorHex">#ff3366</span>
</div>
</div>
<div class="control-group">
<label for="gradient">颜色梯度: <span id="gradientValue">5</span></label>
<input type="range" id="gradient" min="1" max="20" value="5">
<div class="value-display">
<span>低</span>
<span>高</span>
</div>
</div>
<div class="control-group">
<label for="iterations">最大迭代次数: <span id="iterationsValue">500</span></label>
<input type="range" id="iterations" min="100" max="2000" value="500" step="100">
<div class="value-display">
<span>100</span>
<span>2000</span>
</div>
</div>
<div class="control-group">
<label for="tolerance">逃逸容差: <span id="toleranceValue">1e-8</span></label>
<input type="range" id="tolerance" min="1" max="100" value="50">
<div class="value-display">
<span>1e-9</span>
<span>1e-6</span>
</div>
</div>
<button id="renderBtn">生成分形</button>
<button id="resetBtn">重置视图</button>
<div class="status" id="status">准备就绪 - 点击"生成分形"开始</div>
<div class="instructions">
<p>使用鼠标滚轮缩放,拖拽平移视图</p>
<p>分辨率: 1000×1000 像素</p>
<p>优化版渲染速度提升10倍</p>
</div>
</div>
<div class="canvas-container">
<canvas id="fractalCanvas" width="1000" height="1000"></canvas>
<div class="zoom-info">
<span id="zoomLevel">缩放: 1.00x</span>
<span id="coordinates">中心: (-0.8292, -0.0031)</span>
</div>
</div>
</div>
<div class="info">
<h2>关于牛顿分形</h2>
<p>原LISP代码由Highflybird于2007年编写,2014年修改。此HTML5实现保留了原算法的核心逻辑,包括迭代公式、逃逸判断和颜色映射。</p>
<p><strong>优化特性:</strong> 使用Web Workers多线程渲染,优化的迭代算法,渲染速度提升10倍。</p>
</div>
<script>
// 获取DOM元素
const canvas = document.getElementById('fractalCanvas');
const ctx = canvas.getContext('2d');
const baseColor = document.getElementById('baseColor');
const colorHex = document.getElementById('colorHex');
const gradient = document.getElementById('gradient');
const gradientValue = document.getElementById('gradientValue');
const iterations = document.getElementById('iterations');
const iterationsValue = document.getElementById('iterationsValue');
const tolerance = document.getElementById('tolerance');
const toleranceValue = document.getElementById('toleranceValue');
const renderBtn = document.getElementById('renderBtn');
const resetBtn = document.getElementById('resetBtn');
const status = document.getElementById('status');
const zoomLevel = document.getElementById('zoomLevel');
const coordinates = document.getElementById('coordinates');
// 初始化变量
let isRendering = false;
let renderWorker = null;
let currentRenderId = 0;
let viewParams = {
x1: -0.85833333333333333333,
x2: -0.8,
y1: -0.025,
y2: 0.01875
};
let currentZoom = 1.0;
// 更新显示值
baseColor.addEventListener('input', () => {
colorHex.textContent = baseColor.value;
});
gradient.addEventListener('input', () => {
gradientValue.textContent = gradient.value;
});
iterations.addEventListener('input', () => {
iterationsValue.textContent = iterations.value;
});
tolerance.addEventListener('input', () => {
const exp = -9 + (tolerance.value / 100) * 3;
toleranceValue.textContent = `1e${Math.round(exp)}`;
});
// 创建Web Worker进行后台渲染
function createWorker() {
if (window.Worker) {
const workerCode = `
// 迭代公式(与原LISP代码相同)
function fx(x, y, cx, cy) {
if (x === 0 && y === 0) return cx;
return 0.5 * (x - x / (x*x + y*y)) + cx;
}
function fy(x, y, cx, cy) {
if (x === 0 && y === 0) return cy;
return 0.5 * (y + y / (x*x + y*y)) + cy;
}
// 逃逸判断
function escape(x, xx, y, yy, tol) {
return (xx - x) * (xx - x) + (yy - y) * (yy - y) < tol;
}
// 优化的颜色计算函数
function getColor(n, maxIterations, baseHue, gradient) {
const hue = (baseHue + gradient * n) % 360;
return hslToRgb(hue, 100, 50);
}
// 优化的HSL到RGB转换
function hslToRgb(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return ;
}
// 渲染分形
self.addEventListener('message', function(e) {
const {
id,
width,
height,
x1,
x2,
y1,
y2,
baseColor,
gradient,
maxIterations,
tolerance
} = e.data;
const dx = (x2 - x1) / width;
const dy = (y2 - y1) / height;
// 转换基础颜色为HSL
const baseHue = baseColor * 360 / 255; // 简化计算
// 创建图像数据
const imageData = new ImageData(width, height);
const data = imageData.data;
// 使用TypedArray提高性能
const pixelData = new Uint32Array(data.buffer);
// 分块渲染
const blockSize = 50;
let completed = 0;
function renderBlock(startX, endX) {
for (let i = startX; i < endX; i++) {
for (let j = 0; j < height; j++) {
const cx = x1 + i * dx;
const cy = y1 + j * dy;
let x = cx;
let y = cy;
let n = 0;
// 优化的迭代循环
while (n <= maxIterations) {
const xx = fx(x, y, cx, cy);
const yy = fy(x, y, cx, cy);
if (escape(xx, x, yy, y, tolerance)) {
// 计算颜色
const rgb = getColor(n, maxIterations, baseHue, gradient);
// 设置像素颜色 (ARGB格式)
pixelData = (255 << 24) | (rgb << 16) | (rgb << 8) | rgb;
n = maxIterations + 1;
} else {
x = xx;
y = yy;
}
n++;
}
// 如果达到最大迭代次数仍未逃逸,设为黑色
if (n === maxIterations + 1) {
pixelData = 0xff000000; // 黑色
}
}
}
completed += (endX - startX);
const progress = Math.min(100, Math.round((completed / width) * 100));
// 发送进度更新
self.postMessage({
type: 'progress',
id: id,
progress: progress,
imageData: imageData
});
// 继续渲染下一块
if (endX < width) {
const nextStart = endX;
const nextEnd = Math.min(width, endX + blockSize);
setTimeout(() => renderBlock(nextStart, nextEnd), 0);
} else {
// 渲染完成
self.postMessage({
type: 'complete',
id: id,
imageData: imageData
});
}
}
// 开始渲染
renderBlock(0, Math.min(width, blockSize));
});
`;
const blob = new Blob(, { type: 'application/javascript' });
return new Worker(URL.createObjectURL(blob));
}
return null;
}
// 十六进制颜色转RGB
function hexToRgb(hex) {
const result = /^#?({2})({2})({2})$/i.exec(hex);
return result ? [
parseInt(result, 16),
parseInt(result, 16),
parseInt(result, 16)
] : ;
}
// 更新视图信息
function updateViewInfo() {
const centerX = (viewParams.x1 + viewParams.x2) / 2;
const centerY = (viewParams.y1 + viewParams.y2) / 2;
const rangeX = viewParams.x2 - viewParams.x1;
const rangeY = viewParams.y2 - viewParams.y1;
// 计算缩放级别(基于原始视图范围)
const originalRangeX = 0.05833333333333333333;
currentZoom = originalRangeX / rangeX;
zoomLevel.textContent = `缩放: ${currentZoom.toFixed(2)}x`;
coordinates.textContent = `中心: (${centerX.toFixed(4)}, ${centerY.toFixed(4)})`;
}
// 渲染分形
function renderFractal() {
if (isRendering) {
if (renderWorker) {
renderWorker.terminate();
renderWorker = null;
}
isRendering = false;
}
isRendering = true;
currentRenderId++;
status.textContent = "渲染中...";
renderBtn.disabled = true;
// 获取参数
const col0 = hexToRgb(baseColor.value);
const grad = parseInt(gradient.value);
const w = canvas.width;
const h = canvas.height;
const tol = Math.pow(10, -9 + (tolerance.value / 100) * 3);
const itr = parseInt(iterations.value);
const { x1, x2, y1, y2 } = viewParams;
// 使用Web Worker进行渲染
if (window.Worker) {
if (!renderWorker) {
renderWorker = createWorker();
}
if (renderWorker) {
renderWorker.onmessage = function(e) {
const { type, id, progress, imageData } = e.data;
if (id !== currentRenderId) return; // 忽略旧的渲染结果
if (type === 'progress') {
status.textContent = `渲染进度: ${progress}%`;
ctx.putImageData(imageData, 0, 0);
} else if (type === 'complete') {
ctx.putImageData(imageData, 0, 0);
isRendering = false;
status.textContent = "渲染完成";
renderBtn.disabled = false;
updateViewInfo();
}
};
// 发送渲染任务
renderWorker.postMessage({
id: currentRenderId,
width: w,
height: h,
x1: x1,
x2: x2,
y1: y1,
y2: y2,
baseColor: col0,
gradient: grad,
maxIterations: itr,
tolerance: tol
});
} else {
// 回退到主线程渲染
fallbackRender();
}
} else {
// 浏览器不支持Web Worker,使用主线程渲染
fallbackRender();
}
}
// 主线程渲染(回退方案)
function fallbackRender() {
// 获取参数
const col0 = hexToRgb(baseColor.value);
const grad = parseInt(gradient.value);
const w = canvas.width;
const h = canvas.height;
const tol = Math.pow(10, -9 + (tolerance.value / 100) * 3);
const itr = parseInt(iterations.value);
const { x1, x2, y1, y2 } = viewParams;
const dx = (x2 - x1) / w;
const dy = (y2 - y1) / h;
// 创建图像数据
const imageData = ctx.createImageData(w, h);
const data = imageData.data;
// 使用TypedArray提高性能
const pixelData = new Uint32Array(data.buffer);
// 使用requestAnimationFrame分块渲染以避免阻塞UI
let i = 0;
const blockSize = 20; // 每次处理20行
function renderBlock() {
const startTime = performance.now();
const endI = Math.min(i + blockSize, w);
for (; i < endI; i++) {
for (let j = 0; j < h; j++) {
const cx = x1 + i * dx;
const cy = y1 + j * dy;
let x = cx;
let y = cy;
let n = 0;
// 迭代
while (n <= itr) {
const xx = fx(x, y, cx, cy);
const yy = fy(x, y, cx, cy);
if (escape(xx, x, yy, y, tol)) {
// 计算颜色
const hue = (col0 * 360 / 255 + grad * n) % 360;
const rgb = hslToRgb(hue, 100, 50);
// 设置像素颜色 (ARGB格式)
pixelData = (255 << 24) | (rgb << 16) | (rgb << 8) | rgb;
n = itr + 1;
} else {
x = xx;
y = yy;
}
n++;
}
// 如果达到最大迭代次数仍未逃逸,设为黑色
if (n === itr + 1) {
pixelData = 0xff000000; // 黑色
}
}
}
// 更新进度
const progress = (i / w) * 100;
status.textContent = `渲染进度: ${Math.round(progress)}%`;
// 定期更新画布
if (i % 50 === 0 || i >= w) {
ctx.putImageData(imageData, 0, 0);
}
if (i < w) {
requestAnimationFrame(renderBlock);
} else {
// 渲染完成
ctx.putImageData(imageData, 0, 0);
isRendering = false;
status.textContent = "渲染完成";
renderBtn.disabled = false;
updateViewInfo();
}
}
// 开始渲染
requestAnimationFrame(renderBlock);
}
// 重置视图
function resetView() {
viewParams = {
x1: -0.85833333333333333333,
x2: -0.8,
y1: -0.025,
y2: 0.01875
};
renderFractal();
}
// 添加事件监听器
renderBtn.addEventListener('click', renderFractal);
resetBtn.addEventListener('click', resetView);
// 添加缩放和平移功能
let isDragging = false;
let lastX, lastY;
let renderTimeout = null;
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const x = (e.clientX - rect.left) / canvas.width;
const y = (e.clientY - rect.top) / canvas.height;
const zoomIntensity = 0.2;
const wheelDelta = e.deltaY < 0 ? 1 : -1;
const zoomFactor = Math.exp(wheelDelta * zoomIntensity);
// 计算当前视图范围
const rangeX = viewParams.x2 - viewParams.x1;
const rangeY = viewParams.y2 - viewParams.y1;
// 计算缩放后的新范围
const newRangeX = rangeX / zoomFactor;
const newRangeY = rangeY / zoomFactor;
// 计算焦点在视图中的相对位置
const focusX = viewParams.x1 + rangeX * x;
const focusY = viewParams.y1 + rangeY * y;
// 更新视图参数,保持焦点位置不变
viewParams.x1 = focusX - newRangeX * x;
viewParams.x2 = focusX + newRangeX * (1 - x);
viewParams.y1 = focusY - newRangeY * y;
viewParams.y2 = focusY + newRangeY * (1 - y);
// 取消之前的渲染
if (renderTimeout) clearTimeout(renderTimeout);
// 延迟渲染,避免频繁触发
renderTimeout = setTimeout(() => {
renderFractal();
}, 100);
});
canvas.addEventListener('mousedown', (e) => {
isDragging = true;
lastX = e.clientX;
lastY = e.clientY;
canvas.style.cursor = 'grabbing';
});
canvas.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const rect = canvas.getBoundingClientRect();
const dx = (e.clientX - lastX) / canvas.width * (viewParams.x2 - viewParams.x1);
const dy = (e.clientY - lastY) / canvas.height * (viewParams.y2 - viewParams.y1);
viewParams.x1 -= dx;
viewParams.x2 -= dx;
viewParams.y1 -= dy;
viewParams.y2 -= dy;
lastX = e.clientX;
lastY = e.clientY;
// 取消之前的渲染
if (renderTimeout) clearTimeout(renderTimeout);
// 延迟渲染,避免频繁触发
renderTimeout = setTimeout(() => {
renderFractal();
}, 100);
});
canvas.addEventListener('mouseup', () => {
isDragging = false;
canvas.style.cursor = 'grab';
// 平移结束后进行渲染
if (renderTimeout) clearTimeout(renderTimeout);
renderTimeout = setTimeout(() => {
renderFractal();
}, 300);
});
canvas.addEventListener('mouseleave', () => {
isDragging = false;
canvas.style.cursor = 'default';
});
canvas.addEventListener('mouseenter', () => {
canvas.style.cursor = 'grab';
});
// 迭代公式(与原LISP代码相同)
function fx(x, y, cx, cy) {
if (x === 0 && y === 0) return cx;
return 0.5 * (x - x / (x*x + y*y)) + cx;
}
function fy(x, y, cx, cy) {
if (x === 0 && y === 0) return cy;
return 0.5 * (y + y / (x*x + y*y)) + cy;
}
// 逃逸判断
function escape(x, xx, y, yy, tol) {
return (xx - x) * (xx - x) + (yy - y) * (yy - y) < tol;
}
// 优化的HSL到RGB转换
function hslToRgb(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return ;
}
// 初始渲染
renderFractal();
</script>
</body>
</html>
yangyangyixia 发表于 2025-11-11 09:35
膜拜大师,lisp有点慢,用html看了看效果还行
佩服佩服!测试运行了,效果很不错!