Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgt4vee6ravpm37untitledHTML
```html
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Fraktal Mandelbrota</title>
<style>
html, body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
}
canvas {
display: block;
width: 100%;
height: 100%;
cursor: crosshair;
}
#info {
position: fixed;
top: 10px;
left: 10px;
color: white;
font: 14px monospace;
background: rgba(0, 0, 0, .65);
padding: 8px 12px;
border-radius: 5px;
user-select: none;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="info">
Scroll = zoom<br>
Lewy przycisk myszy = przesuwanie<br>
R = reset
</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width;
let height;
let centerX = -0.5;
let centerY = 0;
let scale = 3.0;
const MAX_ITERATIONS = 500;
// ---------------------------------------------------------
// Canvas
// ---------------------------------------------------------
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
draw();
}
window.addEventListener('resize', resize);
// ---------------------------------------------------------
// Konwersja piksel -> liczba zespolona
// ---------------------------------------------------------
function pixelToComplex(px, py) {
const aspectRatio = width / height;
const x =
centerX +
(px / width - 0.5) *
scale *
aspectRatio;
const y =
centerY +
(py / height - 0.5) *
scale;
return { x, y };
}
// ---------------------------------------------------------
// Konwersja liczby zespolonej -> kolor
// ---------------------------------------------------------
function getColor(iterations, zx, zy) {
if (iterations === MAX_ITERATIONS) {
return [0, 0, 0];
}
/*
* Smooth coloring.
*
* Dzięki temu zamiast prostych pasów kolorów
* otrzymujemy płynne przejścia.
*/
const magnitude = Math.sqrt(zx * zx + zy * zy);
const smooth =
iterations +
1 -
Math.log(Math.log(magnitude)) / Math.log(2);
const t = smooth / MAX_ITERATIONS;
const r = Math.floor(9 * (1 - t) * t * t * t * 255);
const g = Math.floor(15 * (1 - t) * (1 - t) * t * t * 255);
const b = Math.floor(8.5 * (1 - t) * (1 - t) * (1 - t) * t * 255);
return [r, g, b];
}
// ---------------------------------------------------------
// Mandelbrot
// ---------------------------------------------------------
function draw() {
const image = ctx.createImageData(width, height);
const data = image.data;
const aspectRatio = width / height;
let index = 0;
for (let py = 0; py < height; py++) {
const cy =
centerY +
(py / height - 0.5) *
scale;
for (let px = 0; px < width; px++) {
const cx =
centerX +
(px / width - 0.5) *
scale *
aspectRatio;
let zx = 0;
let zy = 0;
let iterations = 0;
while (
zx * zx + zy * zy <= 4 &&
iterations < MAX_ITERATIONS
) {
/*
* (zx + i*zy)^2 + (cx + i*cy)
*/
const zx2 =
zx * zx -
zy * zy +
cx;
const zy2 =
2 * zx * zy +
cy;
zx = zx2;
zy = zy2;
iterations++;
}
const [r, g, b] =
getColor(iterations, zx, zy);
data[index++] = r;
data[index++] = g;
data[index++] = b;
data[index++] = 255;
}
}
ctx.putImageData(image, 0, 0);
}
// ---------------------------------------------------------
// Zoom
// ---------------------------------------------------------
canvas.addEventListener('wheel', event => {
event.preventDefault();
const rect = canvas.getBoundingClientRect();
const mouseX =
event.clientX - rect.left;
const mouseY =
event.clientY - rect.top;
/*
* Zapamiętujemy punkt pod kursorem
* przed zmianą skali.
*/
const before =
pixelToComplex(mouseX, mouseY);
const zoomFactor =
event.deltaY < 0
? 0.8
: 1.25;
scale *= zoomFactor;
/*
* Po zmianie skali przesuwamy środek
* tak, aby punkt znajdujący się
* pod kursorem pozostał pod kursorem.
*/
const after =
pixelToComplex(mouseX, mouseY);
centerX += before.x - after.x;
centerY += before.y - after.y;
draw();
}, { passive: false });
// ---------------------------------------------------------
// Panowanie
// ---------------------------------------------------------
let dragging = false;
let lastMouseX = 0;
let lastMouseY = 0;
canvas.addEventListener('mousedown', event => {
dragging = true;
lastMouseX = event.clientX;
lastMouseY = event.clientY;
});
window.addEventListener('mouseup', () => {
dragging = false;
});
window.addEventListener('mousemove', event => {
if (!dragging) {
return;
}
const dx =
event.clientX - lastMouseX;
const dy =
event.clientY - lastMouseY;
const aspectRatio =
width / height;
centerX -=
dx / width *
scale *
aspectRatio;
centerY -=
dy / height *
scale;
lastMouseX = event.clientX;
lastMouseY = event.clientY;
draw();
});
// ---------------------------------------------------------
// Reset
// ---------------------------------------------------------
window.addEventListener('keydown', event => {
if (event.key.toLowerCase() === 'r') {
centerX = -0.5;
centerY = 0;
scale = 3;
draw();
}
});
// ---------------------------------------------------------
// Start
// ---------------------------------------------------------
resize();
</script>
</body>
</html>
```
6,452 chars · codepastes.com