@zinejs/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cssRenderer-OzRRoEa4.js +127 -0
- package/dist/cssRenderer-OzRRoEa4.js.map +1 -0
- package/dist/engine/spread.d.ts +10 -5
- package/dist/geometry/curls/fold.d.ts +9 -0
- package/dist/geometry/curls/index.d.ts +5 -0
- package/dist/geometry/curls/mesh.d.ts +20 -0
- package/dist/geometry/curls/peel.d.ts +9 -0
- package/dist/geometry/curls/roll.d.ts +8 -0
- package/dist/geometry/curls/simple.d.ts +15 -0
- package/dist/geometry/curls/types.d.ts +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +451 -312
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +40 -59
- package/dist/index.umd.cjs.map +1 -1
- package/dist/renderer/types.d.ts +22 -0
- package/dist/renderer/webglRenderer.d.ts +6 -5
- package/dist/source/compose.d.ts +16 -0
- package/dist/source/types.d.ts +6 -0
- package/dist/types-Bz64qLvE.js +11 -0
- package/dist/types-Bz64qLvE.js.map +1 -0
- package/dist/webglRenderer-m-lr412M.js +493 -0
- package/dist/webglRenderer-m-lr412M.js.map +1 -0
- package/dist/zine.d.ts +12 -3
- package/options.schema.json +26 -6
- package/package.json +1 -1
- package/dist/cssRenderer-BACNXO8M.js +0 -68
- package/dist/cssRenderer-BACNXO8M.js.map +0 -1
- package/dist/flipProgressToPose-DZWSuKoE.js +0 -13
- package/dist/flipProgressToPose-DZWSuKoE.js.map +0 -1
- package/dist/webglRenderer-D-1UyC-B.js +0 -306
- package/dist/webglRenderer-D-1UyC-B.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webglRenderer-m-lr412M.js","names":["#container","#canvas","#onContextLost","#onContextRestored","#onVisibilityChange","#gl","#buildGlResources","#clearRestoreTimer","#texCache","#blankTex","#quadVao","#curlVao","#flat","#curl","#cacheBytes","#content","#flip","#fatalHandler","#fill","#trackAspect","#render","#pageAspect","#flipT","#curlType","#anchor","#fromShiftUnit","#toShiftUnit","#view","#bookBox","#contentBox","#contextLost","#restoreTimer","#fatal","#fatalFired","#flatU","#curlU","#makeQuadVao","#buildCurlMesh","#makeBlankTexture","#idxCount","#posBuf","#normBuf","#mesh","#resize","#fitBook","#drawFlip","#drawStatic","#bookW","#bookH","#shiftX","#useFlat","#drawQuad","#bindFace","#dpr","#textureFor","#evictTextures"],"sources":["../src/geometry/curls/mesh.ts","../src/geometry/curls/roll.ts","../src/geometry/curls/simple.ts","../src/geometry/curls/fold.ts","../src/geometry/curls/peel.ts","../src/geometry/curls/index.ts","../src/renderer/gl/context.ts","../src/renderer/gl/program.ts","../src/renderer/gl/texture.ts","../src/renderer/webglRenderer.ts"],"sourcesContent":["/**\n * Shared grid mesh + normal utilities for the page-curl models.\n *\n * A curl model fills `positions`/`normals` in place each frame; UVs are static grid\n * coordinates (no texture swimming). All models bend the same (cols+1)×(rows+1) grid.\n */\n\nexport interface PageMesh {\n readonly cols: number;\n readonly rows: number;\n /** ((cols+1)*(rows+1))*2 — static grid UVs (u across width, v down height). */\n readonly uvs: Float32Array;\n /** *3 — deformed positions, updated by the curl model. */\n readonly positions: Float32Array;\n /** *3 — per-vertex normals, updated by the curl model. */\n readonly normals: Float32Array;\n}\n\n/** Allocate a (cols+1)×(rows+1) grid mesh with static UVs (positions/normals filled by a curl). */\nexport function createPageMesh(cols: number, rows: number): PageMesh {\n const nx = cols + 1;\n const ny = rows + 1;\n const count = nx * ny;\n const uvs = new Float32Array(count * 2);\n for (let j = 0; j < ny; j++) {\n for (let i = 0; i < nx; i++) {\n const k = j * nx + i;\n uvs[k * 2] = i / cols;\n uvs[k * 2 + 1] = j / rows;\n }\n }\n return {\n cols,\n rows,\n uvs,\n positions: new Float32Array(count * 3),\n normals: new Float32Array(count * 3),\n };\n}\n\n/** Per-vertex normals from central differences of the deformed grid (front = +z when flat). */\nexport function computeNormals(mesh: PageMesh): void {\n const nx = mesh.cols + 1;\n const ny = mesh.rows + 1;\n const pos = mesh.positions;\n const nrm = mesh.normals;\n\n for (let j = 0; j < ny; j++) {\n for (let i = 0; i < nx; i++) {\n const iPrev = i > 0 ? i - 1 : i;\n const iNext = i < nx - 1 ? i + 1 : i;\n const jPrev = j > 0 ? j - 1 : j;\n const jNext = j < ny - 1 ? j + 1 : j;\n\n const a = (j * nx + iNext) * 3;\n const b = (j * nx + iPrev) * 3;\n const c = (jNext * nx + i) * 3;\n const e = (jPrev * nx + i) * 3;\n\n const tuX = pos[a]! - pos[b]!;\n const tuY = pos[a + 1]! - pos[b + 1]!;\n const tuZ = pos[a + 2]! - pos[b + 2]!;\n const tvX = pos[c]! - pos[e]!;\n const tvY = pos[c + 1]! - pos[e + 1]!;\n const tvZ = pos[c + 2]! - pos[e + 2]!;\n\n let nx0 = tuY * tvZ - tuZ * tvY;\n let ny0 = tuZ * tvX - tuX * tvZ;\n let nz0 = tuX * tvY - tuY * tvX;\n const l = Math.hypot(nx0, ny0, nz0) || 1;\n nx0 /= l;\n ny0 /= l;\n nz0 /= l;\n\n const k = (j * nx + i) * 3;\n nrm[k] = nx0;\n nrm[k + 1] = ny0;\n nrm[k + 2] = nz0;\n }\n }\n}\n","import { computeNormals, type PageMesh } from './mesh';\n\n/**\n * Roll-and-flop. The leaf wraps a cylinder whose wrap angle grows 0→π over the first\n * half while its radius (= W/theta) shrinks, so the sheet rolls up off the surface in\n * place; over the second half it unwraps π→0 while rigidly rotating around the spine\n * 0→-π down onto the far side. Arc length across the width is exactly preserved.\n */\nexport function deformRoll(mesh: PageMesh, W: number, H: number, t: number): void {\n const nx = mesh.cols + 1;\n const ny = mesh.rows + 1;\n const pos = mesh.positions;\n\n const theta = Math.max(1e-6, t < 0.5 ? t * 2 * Math.PI : (1 - (t - 0.5) * 2) * Math.PI);\n const radius = W / theta;\n const rot = t < 0.5 ? 0 : -(t - 0.5) * 2 * Math.PI;\n const cosR = Math.cos(rot);\n const sinR = Math.sin(rot);\n\n for (let i = 0; i < nx; i++) {\n const rad = (i / mesh.cols) * theta;\n const cx = Math.sin(rad) * radius;\n const cz = (1 - Math.cos(rad)) * radius;\n const X = cx * cosR + cz * sinR;\n const Z = -cx * sinR + cz * cosR;\n for (let j = 0; j < ny; j++) {\n const k = (j * nx + i) * 3;\n pos[k] = X;\n pos[k + 1] = (j / mesh.rows) * H;\n pos[k + 2] = Z;\n }\n }\n\n computeNormals(mesh);\n}\n","import { computeNormals, type PageMesh } from './mesh';\n\n/**\n * Flat page turn — the plainest flip, matching the CSS renderer. The leaf stays a rigid\n * flat sheet and rotates around the spine (angle 0→π), going edge-on at the midpoint. No\n * cylinder bend; GPU-rendered here with the shared perspective so it reads like the CSS fold.\n */\nexport function deformSimple(mesh: PageMesh, W: number, H: number, t: number): void {\n const nx = mesh.cols + 1;\n const ny = mesh.rows + 1;\n const pos = mesh.positions;\n\n const angle = t * Math.PI;\n const cosA = Math.cos(angle);\n const sinA = Math.sin(angle);\n\n for (let i = 0; i < nx; i++) {\n const x0 = (i / mesh.cols) * W; // distance from the spine\n const X = x0 * cosA;\n const Z = x0 * sinA;\n for (let j = 0; j < ny; j++) {\n const k = (j * nx + i) * 3;\n pos[k] = X;\n pos[k + 1] = (j / mesh.rows) * H;\n pos[k + 2] = Z;\n }\n }\n\n computeNormals(mesh);\n}\n\n/**\n * Bent page flip: the single-page (fill) form for spine curls whose spread shape assumes a\n * centered spine or a neighbour (roll). The leaf rotates about its spine edge (0→π) like the\n * flat turn, but the sheet also curves into a shallow arc that peaks mid-flip and flattens at\n * both ends, so the paper visibly bends as it turns instead of staying rigid. Reduces to the\n * flat turn when the bend is zero; arc length across the width is preserved.\n */\nexport function deformBentFlip(mesh: PageMesh, W: number, H: number, t: number): void {\n const nx = mesh.cols + 1;\n const ny = mesh.rows + 1;\n const pos = mesh.positions;\n\n const phi = t * Math.PI; // spine rotation, 0 → π\n // Curl across the width, 0 at the ends. Deep enough that the sheet's facing varies across\n // its width, so the shared fold shading + glossy highlight ride across it as it turns.\n const bend = Math.max(1e-3, 1.6 * Math.sin(Math.PI * t));\n const R = W / bend; // arc radius: arc length W subtends `bend` radians\n const sinPhi = Math.sin(phi);\n const cosPhi = Math.cos(phi);\n\n for (let i = 0; i < nx; i++) {\n const x0 = (i / mesh.cols) * W;\n const a = phi + bend * (x0 / W); // tangent angle at this column (spine → free edge)\n const X = R * (Math.sin(a) - sinPhi);\n const Z = R * (cosPhi - Math.cos(a));\n for (let j = 0; j < ny; j++) {\n const k = (j * nx + i) * 3;\n pos[k] = X;\n pos[k + 1] = (j / mesh.rows) * H;\n pos[k + 2] = Z;\n }\n }\n\n computeNormals(mesh);\n}\n","import { computeNormals, type PageMesh } from './mesh';\nimport type { CurlAnchor } from './types';\n\nconst CREASE = 0.04; // crease radius as a fraction of width — small = a hard, flat fold\nconst DIAG = 0.6; // how far the dragged corner pulls toward the vertical centre mid-turn\n\n/**\n * Flat origami fold. The tapped corner (`anchor.y`) is dragged across the spine; the\n * sheet folds along the perpendicular bisector of corner→pointer, reflecting the corner\n * region flat over the rest with a tight rounded crease. Off-centre taps give a diagonal\n * fold; the fold sweeps to the spine and the page lands (near) flat on the far side.\n */\nexport function deformFold(mesh: PageMesh, W: number, H: number, t: number, anchor: CurlAnchor): void {\n const nx = mesh.cols + 1;\n const ny = mesh.rows + 1;\n const pos = mesh.positions;\n const r = Math.max(1e-4, CREASE * W);\n\n const cy = anchor.y * H;\n const px = W - 2 * W * t; // corner sweeps free edge -> across the spine\n const py = cy + (0.5 * H - cy) * DIAG * Math.sin(Math.PI * t);\n const dx = W - px; // corner - pointer\n const dy = cy - py;\n const len = Math.hypot(dx, dy);\n\n if (len < 1e-4) {\n for (let j = 0; j < ny; j++) {\n for (let i = 0; i < nx; i++) {\n const k = (j * nx + i) * 3;\n pos[k] = (i / mesh.cols) * W;\n pos[k + 1] = (j / mesh.rows) * H;\n pos[k + 2] = 0;\n }\n }\n computeNormals(mesh);\n return;\n }\n\n const nX = dx / len; // toward the corner (fold \"past\" direction)\n const nY = dy / len;\n const fX = -nY; // along the fold line (preserved)\n const fY = nX;\n const midX = (W + px) / 2;\n const midY = (cy + py) / 2;\n\n for (let j = 0; j < ny; j++) {\n const y0 = (j / mesh.rows) * H;\n for (let i = 0; i < nx; i++) {\n const x0 = (i / mesh.cols) * W;\n const relX = x0 - midX;\n const relY = y0 - midY;\n const d = relX * nX + relY * nY; // signed distance past the fold (+ = corner side)\n const e = relX * fX + relY * fY; // position along the fold line\n const k = (j * nx + i) * 3;\n\n if (d <= 0) {\n pos[k] = x0;\n pos[k + 1] = y0;\n pos[k + 2] = 0;\n continue;\n }\n\n const theta = Math.min(d / r, Math.PI); // wrap the crease, then lie flat\n const extra = Math.max(0, d - r * Math.PI);\n const nComp = r * Math.sin(theta) - extra;\n pos[k] = midX + fX * e + nX * nComp;\n pos[k + 1] = midY + fY * e + nY * nComp;\n pos[k + 2] = r * (1 - Math.cos(theta));\n }\n }\n\n computeNormals(mesh);\n}\n","import { computeNormals, type PageMesh } from './mesh';\nimport type { CurlAnchor } from './types';\n\nconst LEAD = 0.7; // how far the anchored corner runs ahead of the far edge (diagonal peel)\n\n/**\n * Diagonal corner peel. Each row runs the roll-and-flop, but rows near the tapped corner\n * (`anchor.y`) lead and rows far behind lag, so the sheet lifts from that corner and\n * sweeps across on a diagonal — a smooth 3D peel. Every row still reaches t=1 together,\n * so the page lands flat.\n */\nexport function deformPeel(mesh: PageMesh, W: number, H: number, t: number, anchor: CurlAnchor): void {\n const nx = mesh.cols + 1;\n const ny = mesh.rows + 1;\n const pos = mesh.positions;\n\n for (let j = 0; j < ny; j++) {\n const v = j / mesh.rows;\n const d = Math.abs(v - anchor.y); // 0 at the tapped corner, up to 1 at the far edge\n const tRow = Math.min(1, Math.max(0, t * (1 + LEAD) - LEAD * d));\n\n const theta = Math.max(1e-6, tRow < 0.5 ? tRow * 2 * Math.PI : (1 - (tRow - 0.5) * 2) * Math.PI);\n const radius = W / theta;\n const rot = tRow < 0.5 ? 0 : -(tRow - 0.5) * 2 * Math.PI;\n const cosR = Math.cos(rot);\n const sinR = Math.sin(rot);\n const y = v * H;\n\n for (let i = 0; i < nx; i++) {\n const rad = (i / mesh.cols) * theta;\n const cx = Math.sin(rad) * radius;\n const cz = (1 - Math.cos(rad)) * radius;\n const k = (j * nx + i) * 3;\n pos[k] = cx * cosR + cz * sinR;\n pos[k + 1] = y;\n pos[k + 2] = -cx * sinR + cz * cosR;\n }\n }\n\n computeNormals(mesh);\n}\n","/**\n * Page-curl registry. Each model turns a 0→1 flip progress into a bent page mesh; the\n * WebGL2 renderer picks one by the `curl` option. `anchored` models fold from a corner\n * given by `anchor.y` (0 = top, 1 = bottom — where the reader tapped); the others roll\n * the whole free edge and ignore it.\n *\n * Importing this module pulls in the deform math, so only the WebGL2 renderer does.\n * The engine references the lightweight `./types` (union + validation list) instead.\n */\nimport { deformRoll } from './roll';\nimport { deformSimple, deformBentFlip } from './simple';\nimport { deformFold } from './fold';\nimport { deformPeel } from './peel';\nimport type { CurlModel, CurlType } from './types';\n\nexport { createPageMesh, computeNormals, type PageMesh } from './mesh';\nexport { CURL_TYPES, DEFAULT_CURL } from './types';\nexport type { CurlAnchor, CurlDeform, CurlModel, CurlType } from './types';\n\nexport const CURLS: Record<CurlType, CurlModel> = {\n roll: { deform: deformRoll, deformFill: deformBentFlip, anchored: false },\n simple: { deform: deformSimple, anchored: false },\n fold: { deform: deformFold, anchored: true },\n peel: { deform: deformPeel, anchored: true },\n};\n","/** Create a WebGL2 context, or throw so the caller can fall back to CSS (§8.4). */\nexport function createGlContext(canvas: HTMLCanvasElement): WebGL2RenderingContext {\n const gl = canvas.getContext('webgl2', {\n alpha: true,\n premultipliedAlpha: true,\n antialias: true,\n depth: false,\n stencil: false,\n // We render on demand (not every frame), so the buffer must persist between\n // composites — otherwise the settled page flickers once the flip animation\n // stops and nothing is redrawing each frame.\n preserveDrawingBuffer: true,\n });\n if (gl === null) {\n throw new Error('WebglRenderer: could not create a WebGL2 context.');\n }\n return gl;\n}\n","/** Compile + link a shader program; throws with the driver log on failure. */\nexport function createProgram(\n gl: WebGL2RenderingContext,\n vertexSrc: string,\n fragmentSrc: string,\n): WebGLProgram {\n const program = gl.createProgram();\n if (program === null) throw new Error('WebglRenderer: could not create a program.');\n\n const vs = compileShader(gl, gl.VERTEX_SHADER, vertexSrc);\n const fs = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSrc);\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n\n // Shaders can be detached/deleted once linked.\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(program);\n gl.deleteProgram(program);\n throw new Error(`WebglRenderer: program link failed: ${log ?? 'unknown error'}`);\n }\n return program;\n}\n\nfunction compileShader(gl: WebGL2RenderingContext, type: number, src: string): WebGLShader {\n const shader = gl.createShader(type);\n if (shader === null) throw new Error('WebglRenderer: could not create a shader.');\n gl.shaderSource(shader, src);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(shader);\n gl.deleteShader(shader);\n throw new Error(`WebglRenderer: shader compile failed: ${log ?? 'unknown error'}`);\n }\n return shader;\n}\n","/** Create a page texture with clamp + linear filtering (no mipmaps — §8.3). */\nexport function createTexture(gl: WebGL2RenderingContext): WebGLTexture {\n const tex = gl.createTexture();\n if (tex === null) throw new Error('WebglRenderer: could not create a texture.');\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n return tex;\n}\n\n/** Upload a decoded page raster (ImageBitmap or canvas) into `tex`. */\nexport function uploadTexture(\n gl: WebGL2RenderingContext,\n tex: WebGLTexture,\n source: TexImageSource,\n): void {\n gl.bindTexture(gl.TEXTURE_2D, tex);\n // No Y flip: the vertex shader maps aUnit.y=0 to the page top and samples the\n // source's top row there. UNPACK_FLIP_Y is honored for <canvas> sources but\n // IGNORED for ImageBitmap, so flipping would invert PDF (canvas) pages while\n // leaving image (ImageBitmap) pages upright. Off keeps both consistent.\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n}\n","import {\n CURLS,\n createPageMesh,\n DEFAULT_CURL,\n type CurlAnchor,\n type CurlType,\n type PageMesh,\n} from '../geometry/curls';\nimport type { Spread } from '../engine/spread';\nimport type {\n FlipDirection,\n LayoutMetrics,\n Renderer,\n RenderOptions,\n SpreadContent,\n} from './types';\nimport { createGlContext } from './gl/context';\nimport { createProgram } from './gl/program';\nimport { createTexture, uploadTexture } from './gl/texture';\n\n// Flat program: a page rect (device px, top-left origin) with zoom/pan and a soft\n// permanent gutter shadow toward the spine so the turning leaf's shadow lands into it.\nconst FLAT_VERT = `#version 300 es\nin vec2 aUnit;\nuniform vec2 uViewport;\nuniform vec4 uRect;\nuniform vec3 uView;\nout vec2 vUv;\nvoid main() {\n vec2 px = uRect.xy + aUnit * uRect.zw;\n px = px * uView.x + uView.yz;\n vec2 clip = px / uViewport * 2.0 - 1.0;\n gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);\n vUv = aUnit;\n}`;\n\nconst FLAT_FRAG = `#version 300 es\nprecision mediump float;\nin vec2 vUv;\nuniform sampler2D uTex;\nuniform float uGutterSide; // +1 = spine at right edge, -1 = at left edge, 0 = none\nout vec4 outColor;\nvoid main() {\n vec4 c = texture(uTex, vUv);\n if (uGutterSide != 0.0) {\n float d = uGutterSide > 0.0 ? (1.0 - vUv.x) : vUv.x;\n c.rgb *= mix(0.72, 1.0, smoothstep(0.0, 0.10, d));\n }\n outColor = c;\n}`;\n\n// Curl program: a thin pass-through for the CPU-deformed page mesh (positions and\n// normals come from the selected curl model). It places the leaf, applies a top-safe depth\n// perspective, and lights from the true surface normal.\nconst CURL_VERT = `#version 300 es\nin vec3 aPos; // leaf-local device px: x = bent offset from spine, y 0..H, z depth\nin vec3 aNormal;\nin vec2 aUv;\nuniform vec2 uViewport;\nuniform vec3 uView; // scale, tx, ty\nuniform float uOriginX; // spine x in device px\nuniform float uDir; // +1 leaf to the right of spine, -1 to the left\nuniform float uLeafW; // leaf width in device px (perspective scale)\nout vec2 vUv;\nout float vFacing;\nout float vU;\nvoid main() {\n float bookX = uOriginX + uDir * aPos.x;\n float bookY = aPos.y;\n float Z = aPos.z;\n float D = uLeafW * 3.0;\n float persp = D / max(D - Z, 1.0);\n float cx = uViewport.x * 0.5;\n float cy = uViewport.y * 0.5;\n float px = cx + (bookX - cx) * persp;\n float py = cy + (bookY - cy) * min(persp, 1.0); // Y only shrinks (top-safe)\n vec2 p = vec2(px, py) * uView.x + uView.yz;\n vec2 clip = p / uViewport * 2.0 - 1.0;\n gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);\n vUv = aUv;\n vFacing = aNormal.z;\n vU = aUv.x;\n}`;\n\nconst CURL_FRAG = `#version 300 es\nprecision mediump float;\nin vec2 vUv;\nin float vFacing;\nin float vU;\nuniform sampler2D uFront;\nuniform sampler2D uBack;\nuniform highp float uDir;\nuniform float uGloss; // 0 disables the specular highlight (e.g. the flat 'simple' curl)\nout vec4 outColor;\nvoid main() {\n // The turn mesh carries its own facing in the normal; the front points at the viewer\n // (normal.z > 0) until the leaf passes vertical, for both flip directions. u->x still\n // flips with uDir so a left-hand leaf reads spine-inward.\n bool showFront = vFacing > 0.0;\n float fx = uDir > 0.0 ? vUv.x : 1.0 - vUv.x;\n vec4 c = showFront ? texture(uFront, vec2(fx, vUv.y)) : texture(uBack, vec2(1.0 - fx, vUv.y));\n\n // Fold shading: the sheet darkens sharply where it curves away from the viewer, so the\n // rolled edge reads as a deep crease with a soft highlight riding the ridge.\n float diff = clamp(abs(vFacing), 0.0, 1.0);\n float lit = mix(0.42, 1.0, diff);\n lit *= mix(0.7, 1.0, smoothstep(0.0, 0.12, vU)); // spine crease matches the gutter shadow\n\n // Glossy specular: brightest where the surface tilts ~30 deg from facing the viewer,\n // so a thin glossy highlight rides across the sheet as it rolls.\n float spec = pow(max(cos(acos(diff) - 0.52), 0.0), 220.0) * 0.22 * uGloss;\n\n c.rgb = clamp(c.rgb * lit + spec, 0.0, 1.0);\n outColor = c;\n}`;\n\nconst GRID_COLS = 28;\nconst GRID_ROWS = 36;\nconst RESTORE_TIMEOUT_MS = 4000;\nconst MAX_TEXTURE_CACHE_BYTES = 256 * 1024 * 1024;\n\ninterface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\ninterface FlipState {\n underLeft: TexImageSource | null;\n underRight: TexImageSource | null;\n underFull: TexImageSource | null; // fill mode\n front: TexImageSource | null;\n back: TexImageSource | null;\n dir: number; // +1 forward (leaf right of spine), -1 backward (left)\n fill: boolean;\n}\n\n/** How far to shift a spread to centre a lone page, in units of a quarter container width:\n * -1 = only a right page (cover), +1 = only a left page (back), 0 = a full spread. */\nfunction shiftUnit(content: SpreadContent): number {\n const lone = (content.left === null) !== (content.right === null);\n return lone ? (content.right !== null ? -1 : 1) : 0;\n}\n\n/**\n * GPU renderer (WebGL2, hand-written). The turning leaf is a dense grid deformed on\n * the CPU by the selected curl model (see geometry/curls) and\n * uploaded each frame, lit from real normals; the static pages are flat quads with a\n * gutter shadow. Restores\n * GL state on context loss; signals onFatal (→ CSS fallback) when unrecoverable.\n */\nexport class WebglRenderer implements Renderer {\n #container: HTMLElement | null = null;\n #canvas: HTMLCanvasElement | null = null;\n #gl: WebGL2RenderingContext | null = null;\n\n #flat: WebGLProgram | null = null;\n #curl: WebGLProgram | null = null;\n #quadVao: WebGLVertexArrayObject | null = null;\n #curlVao: WebGLVertexArrayObject | null = null;\n #posBuf: WebGLBuffer | null = null;\n #normBuf: WebGLBuffer | null = null;\n #idxCount = 0;\n #mesh: PageMesh = createPageMesh(GRID_COLS, GRID_ROWS);\n\n #texCache = new Map<TexImageSource, { tex: WebGLTexture; bytes: number }>();\n #cacheBytes = 0;\n #blankTex: WebGLTexture | null = null;\n\n #flatU: Record<string, WebGLUniformLocation | null> = {};\n #curlU: Record<string, WebGLUniformLocation | null> = {};\n\n #view = { scale: 1, tx: 0, ty: 0 };\n #dpr = 1;\n #content: SpreadContent | null = null;\n #fill = false;\n #flip: FlipState | null = null;\n #flipT = 0;\n #curlType: CurlType = DEFAULT_CURL;\n #anchor: CurlAnchor = { y: 0.5 };\n // A lone page (cover / book front-back) sits on one half; these center it by shifting\n // the view a quarter-width, interpolated across a flip so the open/close doesn't jump.\n #shiftX = 0;\n #fromShiftUnit = 0;\n #toShiftUnit = 0;\n // Book aspect fit: the 2-page spread is letterboxed to the pages' aspect, centered\n // in the container, so pages fill their halves without stretching. 0 = aspect unknown.\n #pageAspect = 0;\n #bookW = 0;\n #bookH = 0;\n\n #contextLost = false;\n #fatalFired = false;\n #restoreTimer: ReturnType<typeof setTimeout> | null = null;\n #fatalHandler: (() => void) | null = null;\n\n mount(container: HTMLElement): Promise<void> {\n this.#container = container;\n const doc = container.ownerDocument;\n const canvas = doc.createElement('canvas');\n canvas.style.cssText = 'display:block;width:100%;height:100%;';\n container.append(canvas);\n this.#canvas = canvas;\n\n canvas.addEventListener('webglcontextlost', this.#onContextLost as EventListener);\n canvas.addEventListener('webglcontextrestored', this.#onContextRestored as EventListener);\n doc.addEventListener('visibilitychange', this.#onVisibilityChange);\n\n this.#gl = createGlContext(canvas); // throws if unavailable -> engine falls back (§8.4)\n this.#buildGlResources();\n return Promise.resolve();\n }\n\n destroy(): void {\n this.#clearRestoreTimer();\n const canvas = this.#canvas;\n if (canvas !== null) {\n canvas.removeEventListener('webglcontextlost', this.#onContextLost as EventListener);\n canvas.removeEventListener('webglcontextrestored', this.#onContextRestored as EventListener);\n canvas.ownerDocument.removeEventListener('visibilitychange', this.#onVisibilityChange);\n }\n const gl = this.#gl;\n if (gl !== null) {\n for (const cached of this.#texCache.values()) gl.deleteTexture(cached.tex);\n if (this.#blankTex !== null) gl.deleteTexture(this.#blankTex);\n if (this.#quadVao !== null) gl.deleteVertexArray(this.#quadVao);\n if (this.#curlVao !== null) gl.deleteVertexArray(this.#curlVao);\n if (this.#flat !== null) gl.deleteProgram(this.#flat);\n if (this.#curl !== null) gl.deleteProgram(this.#curl);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n }\n canvas?.remove();\n this.#texCache.clear();\n this.#cacheBytes = 0;\n this.#blankTex = null;\n this.#container = null;\n this.#canvas = null;\n this.#gl = null;\n this.#content = null;\n this.#flip = null;\n }\n\n onFatal(handler: () => void): void {\n this.#fatalHandler = handler;\n }\n\n renderSpread(_spread: Spread, content: SpreadContent, options?: RenderOptions): void {\n this.#content = content;\n this.#fill = options?.fill ?? false;\n this.#flip = null;\n this.#trackAspect(content);\n this.#render();\n }\n\n /** Remember the page aspect (width/height) so the book can be letterboxed to it. */\n #trackAspect(content: SpreadContent): void {\n const p = content.left ?? content.right;\n if (p && p.height) this.#pageAspect = p.width / p.height;\n }\n\n beginFlip(\n from: SpreadContent,\n to: SpreadContent,\n direction: FlipDirection,\n options?: RenderOptions,\n ): void {\n const fill = options?.fill ?? false;\n this.#flipT = 0;\n if (options?.curl) this.#curlType = options.curl;\n if (options?.anchor) this.#anchor = options.anchor;\n this.#fromShiftUnit = fill ? 0 : shiftUnit(from);\n this.#toShiftUnit = fill ? 0 : shiftUnit(to);\n this.#trackAspect(to);\n if (fill) {\n this.#flip = {\n underLeft: null,\n underRight: null,\n underFull: to.right ?? to.left,\n front: from.right ?? from.left,\n // A lone page has no facing \"next\" leaf on its back; show the same page mirrored\n // (the shader flips the back UV) so it reads as the sheet's own reverse side.\n back: from.right ?? from.left,\n dir: direction === 'forward' ? 1 : -1,\n fill: true,\n };\n } else if (direction === 'forward') {\n this.#flip = {\n underLeft: from.left,\n underRight: to.right,\n underFull: null,\n front: from.right,\n back: to.left,\n dir: 1,\n fill: false,\n };\n } else {\n this.#flip = {\n underLeft: to.left,\n underRight: from.right,\n underFull: null,\n front: from.left,\n back: to.right,\n dir: -1,\n fill: false,\n };\n }\n this.#render();\n }\n\n setFlipProgress(t: number, _direction: FlipDirection): void {\n this.#flipT = t;\n this.#render();\n }\n\n setViewTransform(scale: number, x: number, y: number): void {\n this.#view = { scale, tx: x, ty: y };\n this.#render();\n }\n\n measure(): LayoutMetrics {\n const el = this.#container;\n const w = el?.clientWidth ?? 0;\n const h = el?.clientHeight ?? 0;\n const known = this.#pageAspect > 0;\n const book = known ? this.#bookBox() : undefined;\n const content = known ? this.#contentBox() : undefined;\n return { containerWidth: w, containerHeight: h, pageWidth: w / 2, pageHeight: h, book, content };\n }\n\n /** The fitted book rect in container CSS px (letterbox aware) — a stable 2-page area. */\n #bookBox(): { x: number; y: number; width: number; height: number } {\n const el = this.#container;\n const cw = el?.clientWidth ?? 0;\n const ch = el?.clientHeight ?? 0;\n if (this.#pageAspect <= 0 || cw <= 0 || ch <= 0) return { x: 0, y: 0, width: cw, height: ch };\n const ba = (this.#fill ? 1 : 2) * this.#pageAspect;\n let bw = cw;\n let bh = ch;\n if (ba > cw / ch) bh = cw / ba;\n else bw = ch * ba;\n return { x: (cw - bw) / 2, y: (ch - bh) / 2, width: bw, height: bh };\n }\n\n /** Where the current spread is actually painted: the book, or its centered half for a lone\n * page (mirrors the draw-time #shiftX centering). This is what the engine hit-tests against. */\n #contentBox(): { x: number; y: number; width: number; height: number } {\n const b = this.#bookBox();\n const lone = !this.#fill && this.#content !== null && shiftUnit(this.#content) !== 0;\n return lone ? { x: b.x + b.width / 4, y: b.y, width: b.width / 2, height: b.height } : b;\n }\n\n // --- context loss / restore ---------------------------------------------------\n\n #onContextLost = (event: Event): void => {\n event.preventDefault();\n this.#contextLost = true;\n if (this.#restoreTimer === null) {\n this.#restoreTimer = setTimeout(() => this.#fatal(), RESTORE_TIMEOUT_MS);\n }\n };\n\n #onContextRestored = (): void => {\n this.#clearRestoreTimer();\n this.#contextLost = false;\n this.#buildGlResources();\n this.#render();\n };\n\n #onVisibilityChange = (): void => {\n if (this.#canvas?.ownerDocument.visibilityState === 'visible') this.#render();\n };\n\n #fatal(): void {\n if (this.#fatalFired) return;\n this.#fatalFired = true;\n this.#fatalHandler?.();\n }\n\n #clearRestoreTimer(): void {\n if (this.#restoreTimer !== null) {\n clearTimeout(this.#restoreTimer);\n this.#restoreTimer = null;\n }\n }\n\n #buildGlResources(): void {\n const gl = this.#gl;\n if (gl === null) return;\n\n this.#flat = createProgram(gl, FLAT_VERT, FLAT_FRAG);\n this.#curl = createProgram(gl, CURL_VERT, CURL_FRAG);\n for (const name of ['uViewport', 'uRect', 'uView', 'uTex', 'uGutterSide']) {\n this.#flatU[name] = gl.getUniformLocation(this.#flat, name);\n }\n for (const name of ['uViewport', 'uView', 'uOriginX', 'uDir', 'uLeafW', 'uFront', 'uBack', 'uGloss']) {\n this.#curlU[name] = gl.getUniformLocation(this.#curl, name);\n }\n\n this.#quadVao = this.#makeQuadVao(gl, this.#flat);\n this.#buildCurlMesh(gl, this.#curl);\n\n this.#texCache.clear();\n this.#cacheBytes = 0;\n this.#blankTex = this.#makeBlankTexture(gl);\n gl.useProgram(this.#flat);\n gl.uniform1i(this.#flatU.uTex ?? null, 0);\n gl.useProgram(this.#curl);\n gl.uniform1i(this.#curlU.uFront ?? null, 0);\n gl.uniform1i(this.#curlU.uBack ?? null, 1);\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); // premultiplied alpha\n }\n\n #makeQuadVao(gl: WebGL2RenderingContext, program: WebGLProgram): WebGLVertexArrayObject {\n const vao = gl.createVertexArray();\n const buffer = gl.createBuffer();\n if (vao === null || buffer === null) throw new Error('WebglRenderer: could not create buffers.');\n gl.bindVertexArray(vao);\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), gl.STATIC_DRAW);\n const aUnit = gl.getAttribLocation(program, 'aUnit');\n gl.enableVertexAttribArray(aUnit);\n gl.vertexAttribPointer(aUnit, 2, gl.FLOAT, false, 0, 0);\n gl.bindVertexArray(null);\n return vao;\n }\n\n #buildCurlMesh(gl: WebGL2RenderingContext, program: WebGLProgram): void {\n const nx = GRID_COLS + 1;\n const indices: number[] = [];\n for (let j = 0; j < GRID_ROWS; j++) {\n for (let i = 0; i < GRID_COLS; i++) {\n const a = j * nx + i;\n const b = a + 1;\n const c = a + nx;\n const d = c + 1;\n indices.push(a, c, b, b, c, d);\n }\n }\n this.#idxCount = indices.length;\n\n const vao = gl.createVertexArray();\n this.#posBuf = gl.createBuffer();\n this.#normBuf = gl.createBuffer();\n const uvBuf = gl.createBuffer();\n const idxBuf = gl.createBuffer();\n if (vao === null || this.#posBuf === null || this.#normBuf === null || uvBuf === null || idxBuf === null) {\n throw new Error('WebglRenderer: could not create mesh buffers.');\n }\n gl.bindVertexArray(vao);\n\n gl.bindBuffer(gl.ARRAY_BUFFER, this.#posBuf);\n gl.bufferData(gl.ARRAY_BUFFER, this.#mesh.positions, gl.DYNAMIC_DRAW);\n const aPos = gl.getAttribLocation(program, 'aPos');\n gl.enableVertexAttribArray(aPos);\n gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0);\n\n gl.bindBuffer(gl.ARRAY_BUFFER, this.#normBuf);\n gl.bufferData(gl.ARRAY_BUFFER, this.#mesh.normals, gl.DYNAMIC_DRAW);\n const aNormal = gl.getAttribLocation(program, 'aNormal');\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n\n gl.bindBuffer(gl.ARRAY_BUFFER, uvBuf);\n gl.bufferData(gl.ARRAY_BUFFER, this.#mesh.uvs, gl.STATIC_DRAW);\n const aUv = gl.getAttribLocation(program, 'aUv');\n gl.enableVertexAttribArray(aUv);\n gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 0, 0);\n\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuf);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);\n\n gl.bindVertexArray(null);\n this.#curlVao = vao;\n }\n\n // --- drawing ------------------------------------------------------------------\n\n #render(): void {\n const gl = this.#gl;\n const canvas = this.#canvas;\n if (gl === null || canvas === null || this.#contextLost || gl.isContextLost()) return;\n this.#resize();\n gl.clear(gl.COLOR_BUFFER_BIT);\n this.#fitBook();\n if (this.#flip !== null) this.#drawFlip();\n else if (this.#content !== null) this.#drawStatic();\n }\n\n /** Letterbox the 2-page book to the page aspect, centered; restricts the GL viewport\n * so pages fill their halves without stretching (the surround stays cleared). */\n #fitBook(): void {\n const gl = this.#gl!;\n const canvas = this.#canvas!;\n const cw = canvas.width;\n const ch = canvas.height;\n if (this.#pageAspect > 0) {\n const bookAspect = (this.#fill ? 1 : 2) * this.#pageAspect;\n if (bookAspect > cw / ch) {\n this.#bookW = cw;\n this.#bookH = Math.round(cw / bookAspect);\n } else {\n this.#bookH = ch;\n this.#bookW = Math.round(ch * bookAspect);\n }\n } else {\n this.#bookW = cw;\n this.#bookH = ch;\n }\n const bx = Math.round((cw - this.#bookW) / 2);\n const by = Math.round((ch - this.#bookH) / 2); // centered, so GL's bottom offset == by\n gl.viewport(bx, by, this.#bookW, this.#bookH);\n }\n\n #drawStatic(): void {\n const w = this.#bookW;\n const h = this.#bookH;\n const content = this.#content!;\n this.#shiftX = this.#fill ? 0 : shiftUnit(content) * (w / 4);\n this.#useFlat();\n if (this.#fill) {\n this.#drawQuad({ x: 0, y: 0, w, h }, content.right ?? content.left);\n } else {\n // A lone page (one side null) gets no gutter shadow — it's a standalone, centered page.\n this.#drawQuad({ x: 0, y: 0, w: w / 2, h }, content.left, content.right ? 1 : 0);\n this.#drawQuad({ x: w / 2, y: 0, w: w / 2, h }, content.right, content.left ? -1 : 0);\n }\n }\n\n #drawFlip(): void {\n const gl = this.#gl!;\n const w = this.#bookW;\n const h = this.#bookH;\n const flip = this.#flip!;\n\n // Interpolate the lone-page centering across the flip so an opening cover (or a\n // closing back page) slides between centered and spread instead of jumping.\n this.#shiftX = flip.fill\n ? 0\n : (this.#fromShiftUnit + (this.#toShiftUnit - this.#fromShiftUnit) * this.#flipT) * (w / 4);\n\n // Static pages underneath (with gutter shadow).\n this.#useFlat();\n if (flip.fill) {\n this.#drawQuad({ x: 0, y: 0, w, h }, flip.underFull);\n } else {\n this.#drawQuad({ x: 0, y: 0, w: w / 2, h }, flip.underLeft, 1);\n this.#drawQuad({ x: w / 2, y: 0, w: w / 2, h }, flip.underRight, -1);\n }\n\n if (flip.front === null && flip.back === null) return;\n\n // Deform the leaf mesh with the selected curl model on the CPU, upload it, then draw.\n const leafW = flip.fill ? w : w / 2;\n const originX = flip.fill ? (flip.dir > 0 ? 0 : w) : w / 2;\n // Some curls (roll) assume a centered spine or a neighbouring page; in single-page mode\n // they use a fill-specific variant that reads on a lone page instead of flopping off-screen.\n const model = CURLS[this.#curlType];\n const deform = flip.fill && model.deformFill ? model.deformFill : model.deform;\n deform(this.#mesh, leafW, h, this.#flipT, this.#anchor);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.#posBuf);\n gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.#mesh.positions);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.#normBuf);\n gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.#mesh.normals);\n\n this.#bindFace(gl.TEXTURE0, flip.front);\n this.#bindFace(gl.TEXTURE1, flip.back);\n gl.activeTexture(gl.TEXTURE0);\n\n gl.useProgram(this.#curl);\n gl.bindVertexArray(this.#curlVao);\n gl.uniform2f(this.#curlU.uViewport ?? null, w, h);\n gl.uniform3f(\n this.#curlU.uView ?? null,\n this.#view.scale,\n this.#view.tx * this.#dpr + this.#shiftX,\n this.#view.ty * this.#dpr,\n );\n gl.uniform1f(this.#curlU.uOriginX ?? null, originX);\n gl.uniform1f(this.#curlU.uDir ?? null, flip.dir);\n gl.uniform1f(this.#curlU.uLeafW ?? null, leafW);\n gl.uniform1f(this.#curlU.uGloss ?? null, this.#curlType === 'simple' ? 0 : 1); // flat curl = no shine\n gl.drawElements(gl.TRIANGLES, this.#idxCount, gl.UNSIGNED_SHORT, 0);\n }\n\n #useFlat(): void {\n const gl = this.#gl!;\n gl.useProgram(this.#flat);\n gl.bindVertexArray(this.#quadVao);\n gl.activeTexture(gl.TEXTURE0);\n gl.uniform2f(this.#flatU.uViewport ?? null, this.#bookW, this.#bookH);\n gl.uniform3f(\n this.#flatU.uView ?? null,\n this.#view.scale,\n this.#view.tx * this.#dpr + this.#shiftX,\n this.#view.ty * this.#dpr,\n );\n }\n\n #drawQuad(rect: Rect, source: TexImageSource | null, gutterSide = 0): void {\n const gl = this.#gl!;\n if (source === null) return;\n this.#bindFace(gl.TEXTURE0, source);\n gl.uniform4f(this.#flatU.uRect ?? null, rect.x, rect.y, rect.w, rect.h);\n gl.uniform1f(this.#flatU.uGutterSide ?? null, gutterSide);\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n\n #bindFace(unit: number, source: TexImageSource | null): void {\n const gl = this.#gl!;\n gl.activeTexture(unit);\n gl.bindTexture(gl.TEXTURE_2D, source !== null ? this.#textureFor(source) : this.#blankTex);\n }\n\n #textureFor(source: TexImageSource): WebGLTexture {\n const gl = this.#gl!;\n const existing = this.#texCache.get(source);\n if (existing !== undefined) {\n this.#texCache.delete(source);\n this.#texCache.set(source, existing);\n return existing.tex;\n }\n const tex = createTexture(gl);\n uploadTexture(gl, tex, source);\n const dims = source as unknown as { width: number; height: number };\n const bytes = dims.width * dims.height * 4;\n this.#texCache.set(source, { tex, bytes });\n this.#cacheBytes += bytes;\n this.#evictTextures();\n return tex;\n }\n\n #evictTextures(): void {\n const gl = this.#gl!;\n for (const [src, cached] of this.#texCache) {\n if (this.#cacheBytes <= MAX_TEXTURE_CACHE_BYTES || this.#texCache.size <= 4) break;\n this.#texCache.delete(src);\n this.#cacheBytes -= cached.bytes;\n gl.deleteTexture(cached.tex);\n }\n }\n\n #makeBlankTexture(gl: WebGL2RenderingContext): WebGLTexture {\n const tex = createTexture(gl);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));\n return tex;\n }\n\n #resize(): void {\n const gl = this.#gl;\n const canvas = this.#canvas;\n const el = this.#container;\n if (gl === null || canvas === null || el === null) return;\n this.#dpr = typeof devicePixelRatio === 'number' ? devicePixelRatio : 1;\n const w = Math.max(1, Math.round(el.clientWidth * this.#dpr));\n const h = Math.max(1, Math.round(el.clientHeight * this.#dpr));\n if (canvas.width !== w || canvas.height !== h) {\n canvas.width = w;\n canvas.height = h;\n gl.viewport(0, 0, w, h);\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/zine.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type Direction } from './engine/spread';
|
|
1
|
+
import { type Direction, type SpreadMode } from './engine/spread';
|
|
2
2
|
import { type ZineEventMap } from './engine/emitter';
|
|
3
|
+
import { type CurlType } from './geometry/curls/types';
|
|
3
4
|
import { type RendererOption } from './renderer/select';
|
|
4
5
|
import type { FlipDirection } from './renderer/types';
|
|
5
6
|
import type { Source } from './source/types';
|
|
@@ -24,14 +25,22 @@ export interface ZineOptions {
|
|
|
24
25
|
source: Source;
|
|
25
26
|
/** Renderer selection; default 'auto'. */
|
|
26
27
|
renderer?: RendererOption;
|
|
28
|
+
/** Page-curl model for the WebGL2 renderer: 'roll' | 'simple' | 'fold' | 'peel'. Default 'roll'. */
|
|
29
|
+
curl?: CurlType;
|
|
27
30
|
/** Fixed container width in px; omit to let the container/CSS drive the size. */
|
|
28
31
|
width?: number;
|
|
29
32
|
/** Fixed container height in px; omit to let the container/CSS drive the size. */
|
|
30
33
|
height?: number;
|
|
31
34
|
/** Reading direction; default 'ltr'. */
|
|
32
35
|
direction?: Direction;
|
|
33
|
-
/**
|
|
34
|
-
|
|
36
|
+
/** How pages group into spreads: 'double' | 'single' | 'cover' | 'book'. Default 'cover'. */
|
|
37
|
+
spreadMode?: SpreadMode;
|
|
38
|
+
/** Image URL prepended as a lone front cover (adds a page). */
|
|
39
|
+
frontCover?: string;
|
|
40
|
+
/** Image URL appended as a lone back cover (adds a page). */
|
|
41
|
+
backCover?: string;
|
|
42
|
+
/** Replace source pages with image URLs, keyed by 0-based source index (negative = from the end). */
|
|
43
|
+
pages?: Record<number, string>;
|
|
35
44
|
/** Page to open on; default 0. */
|
|
36
45
|
startPage?: number;
|
|
37
46
|
/** Flip animation duration in ms; default 500. */
|
package/options.schema.json
CHANGED
|
@@ -40,10 +40,30 @@
|
|
|
40
40
|
"default": "ltr",
|
|
41
41
|
"description": "Reading direction."
|
|
42
42
|
},
|
|
43
|
-
"
|
|
44
|
-
"type": "
|
|
45
|
-
"
|
|
46
|
-
"
|
|
43
|
+
"spreadMode": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"enum": ["double", "single", "cover", "book"],
|
|
46
|
+
"default": "cover",
|
|
47
|
+
"description": "How pages group into spreads: 'double' (paired from page 0), 'single' (one per spread), 'cover' (lone first page), 'book' (lone first and last)."
|
|
48
|
+
},
|
|
49
|
+
"curl": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"enum": ["roll", "simple", "fold", "peel"],
|
|
52
|
+
"default": "roll",
|
|
53
|
+
"description": "Page-curl model for the WebGL2 renderer: 'roll' (roll up then flop over; a lone page bends and turns instead, slightly slower), 'simple' (plain flat page turn, no bend), 'fold' (flat origami fold, anchored at tap), 'peel' (smooth diagonal corner peel, anchored at tap)."
|
|
54
|
+
},
|
|
55
|
+
"frontCover": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Image URL prepended as a lone front cover (adds a page)."
|
|
58
|
+
},
|
|
59
|
+
"backCover": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "Image URL appended as a lone back cover (adds a page)."
|
|
62
|
+
},
|
|
63
|
+
"pages": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"description": "Replace source pages with image URLs, keyed by 0-based source index (negative = from the end).",
|
|
66
|
+
"additionalProperties": { "type": "string" }
|
|
47
67
|
},
|
|
48
68
|
"startPage": {
|
|
49
69
|
"type": "integer",
|
|
@@ -54,7 +74,7 @@
|
|
|
54
74
|
"flipDuration": {
|
|
55
75
|
"type": "number",
|
|
56
76
|
"minimum": 0,
|
|
57
|
-
"default":
|
|
77
|
+
"default": 800,
|
|
58
78
|
"description": "Flip animation duration in milliseconds."
|
|
59
79
|
},
|
|
60
80
|
"clickToFlip": {
|
|
@@ -100,7 +120,7 @@
|
|
|
100
120
|
"type": "number",
|
|
101
121
|
"minimum": 0,
|
|
102
122
|
"default": 640,
|
|
103
|
-
"description": "Container widths (px) below this switch to one page per spread."
|
|
123
|
+
"description": "Container widths (px) below this switch to one page per spread; 0 disables the responsive fallback."
|
|
104
124
|
}
|
|
105
125
|
}
|
|
106
126
|
}
|
package/package.json
CHANGED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./flipProgressToPose-DZWSuKoE.js";
|
|
2
|
-
//#region src/renderer/cssRenderer.ts
|
|
3
|
-
var t = 180 / Math.PI;
|
|
4
|
-
function n(e) {
|
|
5
|
-
let t = e === "forward" ? "to right" : "to left";
|
|
6
|
-
return [
|
|
7
|
-
`linear-gradient(${t}, rgba(0,0,0,0.6), rgba(0,0,0,0.15) 40%, rgba(0,0,0,0) 60%)`,
|
|
8
|
-
`linear-gradient(${t}, transparent 40%, rgba(255,255,255,0.32) 66%, transparent 90%)`,
|
|
9
|
-
`linear-gradient(${t}, transparent 66%, rgba(0,0,0,0.35))`
|
|
10
|
-
].join(",");
|
|
11
|
-
}
|
|
12
|
-
var r = class {
|
|
13
|
-
#e = null;
|
|
14
|
-
#t;
|
|
15
|
-
#n;
|
|
16
|
-
#r;
|
|
17
|
-
#i;
|
|
18
|
-
#a;
|
|
19
|
-
#o;
|
|
20
|
-
#s;
|
|
21
|
-
#c;
|
|
22
|
-
#l;
|
|
23
|
-
mount(e) {
|
|
24
|
-
let t = e.ownerDocument;
|
|
25
|
-
return this.#e = e, this.#t = t.createElement("div"), this.#t.className = "zine-clip", this.#t.style.cssText = "position:relative;width:100%;height:100%;overflow:hidden;", this.#n = t.createElement("div"), this.#n.className = "zine-viewport", this.#n.style.cssText = "position:absolute;inset:0;transform-origin:0 0;", this.#r = t.createElement("div"), this.#r.className = "zine-book", this.#r.style.cssText = "position:absolute;inset:0;perspective:2000px;transform-style:preserve-3d;", this.#i = this.#u(t, "zine-page-left", "left:0;"), this.#a = this.#u(t, "zine-page-right", "right:0;"), this.#o = t.createElement("div"), this.#o.className = "zine-leaf", this.#o.style.cssText = "position:absolute;top:0;width:50%;height:100%;transform-style:preserve-3d;display:none;", this.#s = t.createElement("canvas"), this.#s.className = "zine-leaf-front", this.#s.style.cssText = "position:absolute;inset:0;width:100%;height:100%;backface-visibility:hidden;", this.#c = t.createElement("canvas"), this.#c.className = "zine-leaf-back", this.#c.style.cssText = "position:absolute;inset:0;width:100%;height:100%;backface-visibility:hidden;transform:rotateY(180deg);", this.#l = t.createElement("div"), this.#l.className = "zine-leaf-shadow", this.#l.style.cssText = "position:absolute;inset:0;opacity:0;pointer-events:none;", this.#l.style.background = n("forward"), this.#o.append(this.#s, this.#c, this.#l), this.#r.append(this.#i, this.#a, this.#o), this.#n.append(this.#r), this.#t.append(this.#n), e.append(this.#t), Promise.resolve();
|
|
26
|
-
}
|
|
27
|
-
destroy() {
|
|
28
|
-
this.#t?.remove(), this.#e = null;
|
|
29
|
-
}
|
|
30
|
-
renderSpread(e, t, n) {
|
|
31
|
-
n?.fill ? (this.#i.style.width = "100%", this.#a.style.display = "none", this.#d(this.#i, t.right ?? t.left)) : (this.#i.style.width = "50%", this.#a.style.display = "", this.#d(this.#i, t.left), this.#d(this.#a, t.right)), this.#o.style.display = "none", this.#o.style.transform = "", this.#l.style.opacity = "0";
|
|
32
|
-
}
|
|
33
|
-
beginFlip(e, t, r, i) {
|
|
34
|
-
i?.fill ? (this.#i.style.width = "100%", this.#a.style.display = "none", this.#o.style.width = "100%", this.#o.style.left = "0", this.#o.style.transformOrigin = r === "forward" ? "left center" : "right center", this.#d(this.#i, t.right ?? t.left), this.#d(this.#s, e.right ?? e.left), this.#d(this.#c, t.right ?? t.left)) : (this.#i.style.width = "50%", this.#a.style.display = "", this.#o.style.width = "50%", r === "forward" ? (this.#d(this.#i, e.left), this.#d(this.#a, t.right), this.#d(this.#s, e.right), this.#d(this.#c, t.left), this.#o.style.left = "50%", this.#o.style.transformOrigin = "left center") : (this.#d(this.#a, e.right), this.#d(this.#i, t.left), this.#d(this.#s, e.left), this.#d(this.#c, t.right), this.#o.style.left = "0", this.#o.style.transformOrigin = "right center")), this.#o.style.display = "block", this.#o.style.transform = "rotateY(0deg)", this.#l.style.background = n(r), this.#l.style.opacity = "0";
|
|
35
|
-
}
|
|
36
|
-
setFlipProgress(n, r) {
|
|
37
|
-
let i = e(n), a = i.angle * t;
|
|
38
|
-
this.#o.style.display = "block", this.#o.style.transform = `rotateY(${r === "forward" ? -a : a}deg)`, this.#l.style.opacity = String(i.shadowAlpha);
|
|
39
|
-
}
|
|
40
|
-
setViewTransform(e, t, n) {
|
|
41
|
-
this.#n.style.transform = `translate(${t}px, ${n}px) scale(${e})`;
|
|
42
|
-
}
|
|
43
|
-
measure() {
|
|
44
|
-
let e = this.#e, t = e?.clientWidth ?? 0, n = e?.clientHeight ?? 0;
|
|
45
|
-
return {
|
|
46
|
-
containerWidth: t,
|
|
47
|
-
containerHeight: n,
|
|
48
|
-
pageWidth: t / 2,
|
|
49
|
-
pageHeight: n
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
#u(e, t, n) {
|
|
53
|
-
let r = e.createElement("canvas");
|
|
54
|
-
return r.className = t, r.style.cssText = `position:absolute;top:0;width:50%;height:100%;${n}`, r;
|
|
55
|
-
}
|
|
56
|
-
#d(e, t) {
|
|
57
|
-
let n = e.getContext("2d");
|
|
58
|
-
if (t === null) {
|
|
59
|
-
n?.clearRect(0, 0, e.width, e.height);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
e.width = t.width, e.height = t.height, n?.drawImage(t, 0, 0);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
//#endregion
|
|
66
|
-
export { r as CssRenderer };
|
|
67
|
-
|
|
68
|
-
//# sourceMappingURL=cssRenderer-BACNXO8M.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cssRenderer-BACNXO8M.js","names":["#container","#clip","#viewport","#book","#pageLeft","#makeCanvas","#pageRight","#leaf","#leafFront","#leafBack","#leafShadow","#paint"],"sources":["../src/renderer/cssRenderer.ts"],"sourcesContent":["import { flipProgressToPose } from '../geometry/flipProgressToPose';\nimport type { Spread } from '../engine/spread';\nimport type {\n FlipDirection,\n LayoutMetrics,\n PageContent,\n Renderer,\n RenderOptions,\n SpreadContent,\n} from './types';\n\nconst RAD_TO_DEG = 180 / Math.PI;\n\n/**\n * Layered gradients painted over the turning leaf to fake paper depth on the flat\n * fold: a crease shadow at the spine, a soft curve sheen, and a free-edge shade.\n * The spine is the leaf's pivot edge — left for a forward flip, right for backward.\n */\nfunction foldOverlay(direction: FlipDirection): string {\n const dir = direction === 'forward' ? 'to right' : 'to left';\n return [\n `linear-gradient(${dir}, rgba(0,0,0,0.6), rgba(0,0,0,0.15) 40%, rgba(0,0,0,0) 60%)`,\n `linear-gradient(${dir}, transparent 40%, rgba(255,255,255,0.32) 66%, transparent 90%)`,\n `linear-gradient(${dir}, transparent 66%, rgba(0,0,0,0.35))`,\n ].join(',');\n}\n\n/**\n * Baseline renderer: a 2-panel CSS-3D fold. Runs on every browser, no GPU. Pages\n * are <canvas> elements; the turning leaf (front + back faces, backface-hidden)\n * rotates about the spine driven by flipProgressToPose(), with a gradient shadow\n * overlay for depth. `beginFlip` stages the surfaces; `setFlipProgress` animates.\n */\nexport class CssRenderer implements Renderer {\n #container: HTMLElement | null = null;\n #clip!: HTMLDivElement;\n #viewport!: HTMLDivElement;\n #book!: HTMLDivElement;\n #pageLeft!: HTMLCanvasElement;\n #pageRight!: HTMLCanvasElement;\n #leaf!: HTMLDivElement;\n #leafFront!: HTMLCanvasElement;\n #leafBack!: HTMLCanvasElement;\n #leafShadow!: HTMLDivElement;\n\n mount(container: HTMLElement): Promise<void> {\n const doc = container.ownerDocument;\n this.#container = container;\n\n // Fixed-size clip box; NOT transformed, so zoom can't scale the crop region.\n this.#clip = doc.createElement('div');\n this.#clip.className = 'zine-clip';\n this.#clip.style.cssText = 'position:relative;width:100%;height:100%;overflow:hidden;';\n\n // Transform target for zoom/pan; lives inside the clip.\n this.#viewport = doc.createElement('div');\n this.#viewport.className = 'zine-viewport';\n this.#viewport.style.cssText = 'position:absolute;inset:0;transform-origin:0 0;';\n\n this.#book = doc.createElement('div');\n this.#book.className = 'zine-book';\n this.#book.style.cssText =\n 'position:absolute;inset:0;perspective:2000px;transform-style:preserve-3d;';\n\n this.#pageLeft = this.#makeCanvas(doc, 'zine-page-left', 'left:0;');\n this.#pageRight = this.#makeCanvas(doc, 'zine-page-right', 'right:0;');\n\n this.#leaf = doc.createElement('div');\n this.#leaf.className = 'zine-leaf';\n this.#leaf.style.cssText =\n 'position:absolute;top:0;width:50%;height:100%;transform-style:preserve-3d;display:none;';\n\n this.#leafFront = doc.createElement('canvas');\n this.#leafFront.className = 'zine-leaf-front';\n this.#leafFront.style.cssText =\n 'position:absolute;inset:0;width:100%;height:100%;backface-visibility:hidden;';\n\n this.#leafBack = doc.createElement('canvas');\n this.#leafBack.className = 'zine-leaf-back';\n this.#leafBack.style.cssText =\n 'position:absolute;inset:0;width:100%;height:100%;backface-visibility:hidden;transform:rotateY(180deg);';\n\n this.#leafShadow = doc.createElement('div');\n this.#leafShadow.className = 'zine-leaf-shadow';\n this.#leafShadow.style.cssText = 'position:absolute;inset:0;opacity:0;pointer-events:none;';\n this.#leafShadow.style.background = foldOverlay('forward'); // default; beginFlip sets per direction\n\n this.#leaf.append(this.#leafFront, this.#leafBack, this.#leafShadow);\n this.#book.append(this.#pageLeft, this.#pageRight, this.#leaf);\n this.#viewport.append(this.#book);\n this.#clip.append(this.#viewport);\n container.append(this.#clip);\n\n return Promise.resolve();\n }\n\n destroy(): void {\n this.#clip?.remove();\n this.#container = null;\n }\n\n renderSpread(_spread: Spread, content: SpreadContent, options?: RenderOptions): void {\n if (options?.fill) {\n // Single-page mode: the lone page fills the container; hide the other panel.\n this.#pageLeft.style.width = '100%';\n this.#pageRight.style.display = 'none';\n this.#paint(this.#pageLeft, content.right ?? content.left);\n } else {\n this.#pageLeft.style.width = '50%';\n this.#pageRight.style.display = '';\n this.#paint(this.#pageLeft, content.left);\n this.#paint(this.#pageRight, content.right);\n }\n // A fresh spread cancels any in-progress flip.\n this.#leaf.style.display = 'none';\n this.#leaf.style.transform = '';\n this.#leafShadow.style.opacity = '0';\n }\n\n beginFlip(\n from: SpreadContent,\n to: SpreadContent,\n direction: FlipDirection,\n options?: RenderOptions,\n ): void {\n if (options?.fill) {\n // Single-page: a full-width leaf turns about one edge, revealing `to` underneath.\n this.#pageLeft.style.width = '100%';\n this.#pageRight.style.display = 'none';\n this.#leaf.style.width = '100%';\n this.#leaf.style.left = '0';\n this.#leaf.style.transformOrigin = direction === 'forward' ? 'left center' : 'right center';\n this.#paint(this.#pageLeft, to.right ?? to.left);\n this.#paint(this.#leafFront, from.right ?? from.left);\n this.#paint(this.#leafBack, to.right ?? to.left);\n } else {\n this.#pageLeft.style.width = '50%';\n this.#pageRight.style.display = '';\n this.#leaf.style.width = '50%';\n if (direction === 'forward') {\n // Right page lifts and swings left about the spine. Left stays; right reveals `to`.\n this.#paint(this.#pageLeft, from.left);\n this.#paint(this.#pageRight, to.right);\n this.#paint(this.#leafFront, from.right);\n this.#paint(this.#leafBack, to.left);\n this.#leaf.style.left = '50%';\n this.#leaf.style.transformOrigin = 'left center';\n } else {\n // Left page swings right about its right edge. Right stays; left reveals `to`.\n this.#paint(this.#pageRight, from.right);\n this.#paint(this.#pageLeft, to.left);\n this.#paint(this.#leafFront, from.left);\n this.#paint(this.#leafBack, to.right);\n this.#leaf.style.left = '0';\n this.#leaf.style.transformOrigin = 'right center';\n }\n }\n this.#leaf.style.display = 'block';\n this.#leaf.style.transform = 'rotateY(0deg)';\n this.#leafShadow.style.background = foldOverlay(direction);\n this.#leafShadow.style.opacity = '0';\n }\n\n setFlipProgress(t: number, direction: FlipDirection): void {\n const pose = flipProgressToPose(t);\n const deg = pose.angle * RAD_TO_DEG;\n this.#leaf.style.display = 'block';\n this.#leaf.style.transform = `rotateY(${direction === 'forward' ? -deg : deg}deg)`;\n this.#leafShadow.style.opacity = String(pose.shadowAlpha);\n }\n\n setViewTransform(scale: number, x: number, y: number): void {\n this.#viewport.style.transform = `translate(${x}px, ${y}px) scale(${scale})`;\n }\n\n measure(): LayoutMetrics {\n const el = this.#container;\n const w = el?.clientWidth ?? 0;\n const h = el?.clientHeight ?? 0;\n return { containerWidth: w, containerHeight: h, pageWidth: w / 2, pageHeight: h };\n }\n\n #makeCanvas(doc: Document, className: string, extra: string): HTMLCanvasElement {\n const c = doc.createElement('canvas');\n c.className = className;\n c.style.cssText = `position:absolute;top:0;width:50%;height:100%;${extra}`;\n return c;\n }\n\n #paint(canvas: HTMLCanvasElement, content: PageContent | null): void {\n const ctx = canvas.getContext('2d');\n if (content === null) {\n ctx?.clearRect(0, 0, canvas.width, canvas.height);\n return;\n }\n canvas.width = content.width;\n canvas.height = content.height;\n ctx?.drawImage(content, 0, 0);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
//#region src/geometry/flipProgressToPose.ts
|
|
2
|
-
function e(e) {
|
|
3
|
-
let t = e < 0 ? 0 : e > 1 ? 1 : e, n = Math.sin(t * Math.PI);
|
|
4
|
-
return {
|
|
5
|
-
angle: t * Math.PI,
|
|
6
|
-
curl: n,
|
|
7
|
-
shadowAlpha: n
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
//#endregion
|
|
11
|
-
export { e as t };
|
|
12
|
-
|
|
13
|
-
//# sourceMappingURL=flipProgressToPose-DZWSuKoE.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"flipProgressToPose-DZWSuKoE.js","names":[],"sources":["../src/geometry/flipProgressToPose.ts"],"sourcesContent":["export interface FlipPose {\n /** Turning-page rotation about the spine, radians: 0 (flat/open) → π (fully turned). */\n angle: number;\n /** Mesh cylinder-wrap curl, 0..1 — flat at the ends, peaks edge-on at t=0.5. CSS renderer ignores it. */\n curl: number;\n /** Fold-shadow intensity, 0..1 — peaks edge-on at t=0.5. */\n shadowAlpha: number;\n}\n\n/**\n * Map a 0→1 flip progress to a renderer-neutral fold pose.\n *\n * Linear in `t` on purpose — easing belongs to the animator that produces `t`,\n * so seeking to a fixed progress (e.g. the 0/.25/.5/.75/1 visual-regression stops)\n * is deterministic. The CSS renderer consumes `angle` as a spine `rotateY`; the\n * WebGL2 renderer additionally bends its mesh by `curl`.\n */\nexport function flipProgressToPose(t: number): FlipPose {\n const clamped = t < 0 ? 0 : t > 1 ? 1 : t;\n const arc = Math.sin(clamped * Math.PI);\n return {\n angle: clamped * Math.PI,\n curl: arc,\n shadowAlpha: arc,\n };\n}\n"],"mappings":";AAiBA,SAAgB,EAAmB,GAAqB;CACtD,IAAM,IAAU,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAClC,IAAM,KAAK,IAAI,IAAU,KAAK,EAAE;CACtC,OAAO;EACL,OAAO,IAAU,KAAK;EACtB,MAAM;EACN,aAAa;CACf;AACF"}
|