Add 3D loose-leaf chronicle of human civilisation
A Three.js site presenting 20 world history milestones as double-sided loose-leaf pages, with three views: a leather binder with page-flip animation, a golden timeline, and a dotted globe with great-circle arcs. All textures (paper, leather, registration cards, and 18 hand-drawn sketches) are generated procedurally via Canvas 2D, so no external image assets are needed beyond the Earth map. Includes dev helper scripts for screenshots, ASCII previews, and headless verification.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { CATEGORY_INFO } from '../data/entries.js';
|
||||
import { renderSketch } from '../textures/sketch.js';
|
||||
|
||||
// HTML 覆盖层:标题、视图切换、底部导航、登记卡详情
|
||||
export class Overlay {
|
||||
constructor({ onView, onPrev, onNext, onFlip }) {
|
||||
this.onView = onView;
|
||||
this.onPrev = onPrev;
|
||||
this.onNext = onNext;
|
||||
this.onFlip = onFlip;
|
||||
|
||||
this.els = {
|
||||
iris: document.getElementById('iris'),
|
||||
pageNum: document.getElementById('page-num'),
|
||||
pageTotal: document.getElementById('page-total'),
|
||||
pageName: document.getElementById('page-name'),
|
||||
detailNo: document.getElementById('detail-no'),
|
||||
detailCat: document.getElementById('detail-cat'),
|
||||
detailName: document.getElementById('detail-name'),
|
||||
detailName2: document.getElementById('detail-name2'),
|
||||
detailTime: document.getElementById('detail-time'),
|
||||
detailRegion: document.getElementById('detail-region'),
|
||||
detailDesc: document.getElementById('detail-desc'),
|
||||
detailSketch: document.getElementById('detail-sketch-canvas'),
|
||||
detailCard: document.getElementById('detail-card'),
|
||||
hint: document.getElementById('hint'),
|
||||
};
|
||||
|
||||
this._wire();
|
||||
}
|
||||
|
||||
_wire() {
|
||||
document.querySelectorAll('.view-btn').forEach((btn) => {
|
||||
btn.addEventListener('click', () => this.onView(btn.dataset.view));
|
||||
});
|
||||
document.getElementById('btn-prev').addEventListener('click', () => this.onPrev());
|
||||
document.getElementById('btn-next').addEventListener('click', () => this.onNext());
|
||||
document.getElementById('btn-flip').addEventListener('click', () => this.onFlip());
|
||||
}
|
||||
|
||||
setTotal(n) {
|
||||
this.els.pageTotal.textContent = String(n).padStart(2, '0');
|
||||
}
|
||||
|
||||
setView(view) {
|
||||
document.querySelectorAll('.view-btn').forEach((btn) => {
|
||||
btn.classList.toggle('active', btn.dataset.view === view);
|
||||
});
|
||||
const HINTS = {
|
||||
binder: '拖动旋转 · 滚轮缩放 · 点击书页选中 · ← → 翻页(翻面查看素描)',
|
||||
timeline: '拖拽左右滑动 · 滚轮缩放 · 点击卡片查看详情',
|
||||
globe: '拖动旋转 · 滚轮缩放 · 点击标记查看详情',
|
||||
};
|
||||
this.els.hint.textContent = HINTS[view] || '';
|
||||
}
|
||||
|
||||
setEntry(entry, index, total) {
|
||||
const info = CATEGORY_INFO[entry.category] || CATEGORY_INFO['事'];
|
||||
this.els.pageNum.textContent = String(index + 1).padStart(2, '0');
|
||||
this.els.pageName.textContent = entry.name;
|
||||
this.els.detailNo.textContent = entry.id;
|
||||
this.els.detailCat.textContent = entry.category;
|
||||
this.els.detailCat.style.background = info.color;
|
||||
this.els.detailName.textContent = entry.name;
|
||||
this.els.detailName2.textContent = entry.name;
|
||||
this.els.detailTime.textContent = entry.time;
|
||||
this.els.detailRegion.textContent = entry.region;
|
||||
this.els.detailDesc.textContent = entry.description;
|
||||
|
||||
// 素描缩略图
|
||||
const c = this.els.detailSketch;
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.clearRect(0, 0, c.width, c.height);
|
||||
const sketch = renderSketch(entry, 560);
|
||||
ctx.drawImage(sketch, (c.width - c.height) / 2, 0, c.height, c.height);
|
||||
}
|
||||
|
||||
irisTo(visible) {
|
||||
this.els.iris.style.opacity = visible ? '1' : '0';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user