Más curvatura al agarrar la hoja por arriba o por abajo, con el lomo fijo

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 22:38:14 -06:00
co-authored by Claude Opus 5.5
parent ca97cca7f0
commit 811df85349
+10 -9
View File
@@ -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, // 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 // 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. // (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 N = Math.max(10, Math.min(22, Math.round(r.w / 24)))
const w = r.w / N 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, // 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. // 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 sgn = spineLeft ? -1 : 1 // hacia dónde gira: el borde libre se levanta hacia quien mira
const el = document.createElement('div') const el = document.createElement('div')
el.className = 'curl' 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. // 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 // tiltZ/tiltX: curvatura total de la hoja agarrada por arriba o por abajo. Se reparte entre las tiras salvo la
// que junto al lomo apenas se nota y el borde libre se lleva todo: la hoja sigue pegada al libro. Cambia de // 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. // signo con la cara visible para que la parte agarrada vaya siempre por delante.
function set(g, reverse, tiltZ = 0, tiltX = 0) { function set(g, reverse, tiltZ = 0, tiltX = 0) {
let prev = 0, lift = 0 let prev = 0, lift = 0
angles(g, reverse).forEach((a, i) => { angles(g, reverse).forEach((a, i) => {
const t = strips[i] const t = strips[i]
const c = Math.cos((a * Math.PI) / 180) 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}` t.s.style.transform = `${i ? `translateX(${step}px) ` : ''}rotateY(${(sgn * (a - prev)).toFixed(2)}deg)${bend}`
prev = a prev = a
// Se oscurece al inclinarse. Con un filtro en la propia cara y no con una capa negra encima: en Safari // 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}` }) const pivot = y => strips.forEach(t => { t.s.style.transformOrigin = `${spineLeft ? 'left' : 'right'} ${y}` })
return { el, set, span, solve, pivot } 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. // drag: la página sigue al dedo; devuelve el control (move/release) en vez de animarse sola.
function turnPage(old, stage, dir, drag = false) { 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. // "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') const drop = document.createElement('i')
drop.className = `curldrop ${spineLeft ? 'l' : 'r'}` drop.className = `curldrop ${spineLeft ? 'l' : 'r'}`
place(drop, rect) 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) place(curl.el, rect)
box.append(drop, curl.el) box.append(drop, curl.el)
extra.push(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 // 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. // 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. // 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') if (grab) curl.pivot(grab < 0 ? 'bottom' : 'top')
step = g => { step = g => {
const k = grab ? grab * Math.sin(Math.PI * Math.min(1, Math.max(0, (g - g0) / (g1 - g0 || 1)))) : 0 const k = grab ? grab * Math.sin(Math.PI * Math.min(1, Math.max(0, (g - g0) / (g1 - g0 || 1)))) : 0