Files
root a0dda98b83 i18n: build-time ru/en localization + EN homepage & cookie-policy
- build-pages.mjs: per-locale page/partial overrides (*.en.html), i18n token
  resolution with ru fallback, <html lang> + assetPrefix per locale
- UI dictionaries src/i18n/{ru,en}.json; lang-switch partial
- headers/footer/partials converted to {{i18n:…}} tokens
- root-absolute static asset refs so /en/ subpath loads css/js/vendor
- EN content: homepage (index.en.html + partials) and cookie-policy.en.html
  (cookie-policy body translated via apinex.bond /v1/tools/translator)
2026-09-15 22:27:51 +02:00

82 lines
2.8 KiB
HTML

<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>bake paper texture</title></head>
<body style="margin:0">
<div id="host" style="width:512px;height:512px"></div>
<script type="module">
import { ShaderMount, paperTextureFragmentShader, getShaderNoiseTexture } from "/_lab/paper-bundle.js";
// Параметры из пресета Figma (по умолчанию)
const P = window.__PARAMS || {
u_colorBack: [1, 1, 1, 1],
u_colorFront: [0, 0, 0, 1],
u_contrast: 0.3,
u_roughness: 0.4,
u_fiber: 0.3,
u_fiberSize: 0.2,
u_crumples: 0.3,
u_crumpleSize: 0.35,
u_folds: 0.65,
u_foldCount: 5,
u_drops: 0.2,
u_fade: 0,
u_seed: 5.8,
u_scale: 0.6,
};
try {
const host = document.getElementById("host");
const noise = getShaderNoiseTexture();
await noise.decode();
const mount = new ShaderMount(
host,
paperTextureFragmentShader,
{ ...P, u_noiseTexture: noise },
// preserveDrawingBuffer обязателен: без него буфер очищается
// после композитинга и readPixels вернёт пустоту
{ antialias: false, preserveDrawingBuffer: true },
0,
0,
1,
);
await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
const canvas = host.querySelector("canvas");
const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
const w = canvas.width, h = canvas.height;
const px = new Uint8Array(w * h * 4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, px);
let min = 255, max = 0, sum = 0, n = 0;
for (let i = 0; i < px.length; i += 4) {
const v = px[i];
if (v < min) min = v;
if (v > max) max = v;
sum += v; n++;
}
const avg = sum / n;
let vs = 0;
for (let i = 0; i < px.length; i += 4) vs += (px[i] - avg) ** 2;
// readPixels читает снизу вверх — переворачиваем
const out = document.createElement("canvas");
out.width = w; out.height = h;
const ctx = out.getContext("2d");
const img = ctx.createImageData(w, h);
for (let y = 0; y < h; y++) {
img.data.set(px.subarray((h - 1 - y) * w * 4, (h - y) * w * 4), y * w * 4);
}
ctx.putImageData(img, 0, 0);
window.__STATS = { w, h, min, max, avg: +avg.toFixed(2), sd: +Math.sqrt(vs / n).toFixed(2) };
window.__URL = out.toDataURL("image/png");
window.__DONE = true;
} catch (e) {
window.__ERR = e.message + " | " + e.stack;
}
</script>
</body>
</html>