Make page curl lift across full width
Replace localized spine curl with whole-page arc so the free edge rises highest, and float the topic label with the selected card.
This commit is contained in:
+5
-9
@@ -236,25 +236,21 @@ export class Binder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_updateCurvature() {
|
_updateCurvature() {
|
||||||
// 翻页时:靠近书脊的一段平滑卷曲,顶边卷曲略强于底边(斜向扭转),静止时完全平整
|
// 翻页时整页弧形抬升:自由边最高、书脊侧贴环(如帆鼓风),顶边略强于底边;静止时完全平整
|
||||||
this.pages.forEach((p) => {
|
this.pages.forEach((p) => {
|
||||||
const pos = p.geometry.attributes.position;
|
const pos = p.geometry.attributes.position;
|
||||||
const arr = pos.array;
|
const arr = pos.array;
|
||||||
const theta = Math.abs(p.angle); // 0..π
|
const theta = Math.abs(p.angle); // 0..π
|
||||||
const c = Math.sin(theta); // 0 静止,1 翻页中途
|
const c = Math.sin(theta); // 0 静止,1 翻页中途
|
||||||
const L = 0.16 * c; // 卷曲延伸长度
|
const A = 0.42 * c; // 抬升幅度(整页弧)
|
||||||
const h = 0.15 * c; // 卷曲抬升高度
|
|
||||||
for (let i = 0; i < arr.length; i += 3) {
|
for (let i = 0; i < arr.length; i += 3) {
|
||||||
const x = p.base[i];
|
const x = p.base[i];
|
||||||
const y = p.base[i + 1];
|
const y = p.base[i + 1];
|
||||||
const s = Math.max(0, x - PAGE_GAP); // 距页内缘 0..W(防浮点误差产生负值→NaN)
|
const s = Math.max(0, x - PAGE_GAP); // 距页内缘 0..W(防浮点误差产生负值→NaN)
|
||||||
let z = 0;
|
const u = Math.min(1, s / PAGE_W); // 0 内缘 .. 1 自由边
|
||||||
if (s < L) {
|
|
||||||
const t = s / L;
|
|
||||||
const yNorm = (y + PAGE_H / 2) / PAGE_H; // 0 底边 .. 1 顶边
|
const yNorm = (y + PAGE_H / 2) / PAGE_H; // 0 底边 .. 1 顶边
|
||||||
const twist = 0.55 + 0.45 * yNorm;
|
const twist = 0.7 + 0.3 * yNorm; // 顶边抬升略强
|
||||||
z = h * twist * Math.sin((Math.PI * t) / 2);
|
const z = A * twist * Math.sin((Math.PI * u) / 2);
|
||||||
}
|
|
||||||
arr[i + 2] = p.base[i + 2] + z;
|
arr[i + 2] = p.base[i + 2] + z;
|
||||||
}
|
}
|
||||||
pos.needsUpdate = true;
|
pos.needsUpdate = true;
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { makeTextSprite } from '../utils/labels.js';
|
|||||||
|
|
||||||
export const TIMELINE_SPREAD = 6.2; // 横轴范围 ±6.2(超出视口,可横向滚动)
|
export const TIMELINE_SPREAD = 6.2; // 横轴范围 ±6.2(超出视口,可横向滚动)
|
||||||
|
|
||||||
|
const CARD_FLOAT = 0.14; // 选中时上浮高度
|
||||||
|
const NAME_Y = 0.42; // 主题标签基准高度(活页上方)
|
||||||
|
|
||||||
function cardX(i, n) {
|
function cardX(i, n) {
|
||||||
return (i / (n - 1)) * 2 * TIMELINE_SPREAD - TIMELINE_SPREAD;
|
return (i / (n - 1)) * 2 * TIMELINE_SPREAD - TIMELINE_SPREAD;
|
||||||
}
|
}
|
||||||
@@ -69,18 +72,18 @@ export class Timeline {
|
|||||||
outline.visible = false;
|
outline.visible = false;
|
||||||
card.add(outline);
|
card.add(outline);
|
||||||
|
|
||||||
// 活页上方:主题(常显)
|
// 活页上方:主题(常显,随选中上浮)
|
||||||
const name = makeTextSprite(entry.name, { fontSize: 30, color: '#e8d9b4' });
|
const name = makeTextSprite(entry.name, { fontSize: 30, color: '#e8d9b4' });
|
||||||
name.scale.multiplyScalar(0.22);
|
name.scale.multiplyScalar(0.22);
|
||||||
name.position.set(x, 0.42, 0);
|
name.position.set(x, NAME_Y, 0);
|
||||||
|
|
||||||
// 轴下方:时间(常显)
|
// 轴下方:时间(常显,固定不动)
|
||||||
const time = makeTextSprite(entry.time, { fontSize: 22, color: '#c9a227' });
|
const time = makeTextSprite(entry.time, { fontSize: 22, color: '#c9a227' });
|
||||||
time.scale.multiplyScalar(0.18);
|
time.scale.multiplyScalar(0.18);
|
||||||
time.position.set(x, -1.06, 0);
|
time.position.set(x, -1.06, 0);
|
||||||
|
|
||||||
this.group.add(card, name, time);
|
this.group.add(card, name, time);
|
||||||
this.cards.push({ card, outline, index: i });
|
this.cards.push({ card, outline, name, nameBaseY: NAME_Y, index: i });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +92,8 @@ export class Timeline {
|
|||||||
this.cards.forEach((c) => {
|
this.cards.forEach((c) => {
|
||||||
const on = c.index === index;
|
const on = c.index === index;
|
||||||
c.outline.visible = on;
|
c.outline.visible = on;
|
||||||
c.card.position.y = on ? 0.14 : 0;
|
c.card.position.y = on ? CARD_FLOAT : 0;
|
||||||
|
c.name.position.y = c.nameBaseY + (on ? CARD_FLOAT : 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user