From 811df85349382720073ba48e0dae0501826fd0e7 Mon Sep 17 00:00:00 2001 From: Nino Date: Sun, 1 Nov 2026 22:38:14 -0600 Subject: [PATCH] =?UTF-8?q?M=C3=A1s=20curvatura=20al=20agarrar=20la=20hoja?= =?UTF-8?q?=20por=20arriba=20o=20por=20abajo,=20con=20el=20lomo=20fijo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Curvatura de 14° en el plano de la página y 10° hacia quien lee. - La tira del lomo no se curva, así que la hoja no se separa del libro. - Más solape entre tiras al curvarse, para que no se abran huecos. Co-Authored-By: Claude Opus 5.5 --- app/public/app.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/public/app.js b/app/public/app.js index 5c09472..98446b4 100644 --- a/app/public/app.js +++ b/app/public/app.js @@ -1571,12 +1571,13 @@ async function viewReader(mangaId, chapterId, startPage) { // Hoja de papel que se dobla: tiras verticales anidadas, cada una girada un poco respecto a la anterior, // así que la hoja forma una curva. Cada tira enseña su trozo de la página por delante y del reverso // (la página siguiente en doble página, o papel en blanco) por detrás. - function paperCurl(r, frontSrc, backSrc, spineLeft) { + function paperCurl(r, frontSrc, backSrc, spineLeft, bent = false) { const N = Math.max(10, Math.min(22, Math.round(r.w / 24))) const w = r.w / N // Cada cara se sale OV px por los lados de su tira para tapar la unión con la vecina: sin solape, // Safari deja una rendija oscura entre tiras. - const OV = 1.5 + // Curvada, las tiras dejan de encajar justas lejos del punto fijo: hace falta más solape. + const OV = bent ? 4 : 1.5 const sgn = spineLeft ? -1 : 1 // hacia dónde gira: el borde libre se levanta hacia quien mira const el = document.createElement('div') el.className = 'curl' @@ -1610,15 +1611,15 @@ async function viewReader(mangaId, chapterId, startPage) { }) } // Devuelve cuánto está levantada la hoja (0 a 1), para la sombra de debajo. - // tiltZ/tiltX: curvatura total de la hoja agarrada por arriba o por abajo. Se reparte entre las tiras, de modo - // que junto al lomo apenas se nota y el borde libre se lleva todo: la hoja sigue pegada al libro. Cambia de + // tiltZ/tiltX: curvatura total de la hoja agarrada por arriba o por abajo. Se reparte entre las tiras salvo la + // del lomo, que no se curva, y el borde libre se lleva todo: la hoja sigue pegada al libro. Cambia de // signo con la cara visible para que la parte agarrada vaya siempre por delante. function set(g, reverse, tiltZ = 0, tiltX = 0) { let prev = 0, lift = 0 angles(g, reverse).forEach((a, i) => { const t = strips[i] const c = Math.cos((a * Math.PI) / 180) - const bend = tiltZ || tiltX ? ` rotateZ(${((tiltZ * c) / N).toFixed(3)}deg) rotateX(${((tiltX * c) / N).toFixed(3)}deg)` : '' + const bend = i && (tiltZ || tiltX) ? ` rotateZ(${((tiltZ * c) / (N - 1)).toFixed(3)}deg) rotateX(${((tiltX * c) / (N - 1)).toFixed(3)}deg)` : '' t.s.style.transform = `${i ? `translateX(${step}px) ` : ''}rotateY(${(sgn * (a - prev)).toFixed(2)}deg)${bend}` prev = a // Se oscurece al inclinarse. Con un filtro en la propia cara y no con una capa negra encima: en Safari @@ -1654,7 +1655,7 @@ async function viewReader(mangaId, chapterId, startPage) { const pivot = y => strips.forEach(t => { t.s.style.transformOrigin = `${spineLeft ? 'left' : 'right'} ${y}` }) return { el, set, span, solve, pivot } } - const GRAB_TILT_Z = 8, GRAB_TILT_X = 6 // curvatura total, en grados, de la hoja agarrada por arriba o por abajo + const GRAB_TILT_Z = 14, GRAB_TILT_X = 10 // curvatura total, en grados, de la hoja agarrada por arriba o por abajo // drag: la página sigue al dedo; devuelve el control (move/release) en vez de animarse sola. function turnPage(old, stage, dir, drag = false) { // "Delante" es hacia la izquierda en izq→der (la hoja derecha pasa a la izquierda) y al revés en der→izq. @@ -1731,7 +1732,9 @@ async function viewReader(mangaId, chapterId, startPage) { const drop = document.createElement('i') drop.className = `curldrop ${spineLeft ? 'l' : 'r'}` place(drop, rect) - const curl = paperCurl(rect, frontSrc, backSrc, spineLeft) + const grabAt = drag && grabY != null ? (grabY - B.top - rect.t) / rect.h : 0.5 + const grab = grabAt < 1 / 3 ? -1 : grabAt > 2 / 3 ? 1 : 0 + const curl = paperCurl(rect, frontSrc, backSrc, spineLeft, !!grab) place(curl.el, rect) box.append(drop, curl.el) extra.push(drop, curl.el) @@ -1739,8 +1742,6 @@ async function viewReader(mangaId, chapterId, startPage) { // Agarrada por arriba o por abajo (tercio superior o inferior), esa parte va por delante: la hoja se curva // sin separarse del lomo. Sin capas nuevas; es 0 al principio y al final del giro, así que cae exacta. // Por el centro, como siempre. - const grabAt = drag && grabY != null ? (grabY - B.top - rect.t) / rect.h : 0.5 - const grab = grabAt < 1 / 3 ? -1 : grabAt > 2 / 3 ? 1 : 0 if (grab) curl.pivot(grab < 0 ? 'bottom' : 'top') step = g => { const k = grab ? grab * Math.sin(Math.PI * Math.min(1, Math.max(0, (g - g0) / (g1 - g0 || 1)))) : 0