Spaces:
Running
Running
taurus: 首页入口、珍藏卡预览优化、README 看板完成
Browse files- index.html +1 -0
- taurus/README.md +19 -6
- taurus/js/main.js +6 -4
index.html
CHANGED
|
@@ -12,6 +12,7 @@ a.card div{padding:14px 16px}a.card b{font-size:19px}a.card span{display:block;o
|
|
| 12 |
</style></head><body>
|
| 13 |
<h1>知识动画工作室</h1><p>网页即播 · 浏览器本地导出 MP4 · 横屏 B站 / 竖屏抖音</p>
|
| 14 |
<div class="grid">
|
|
|
|
| 15 |
<a class="card" href="aries/index.html"><img src="aries/covers/cover-bilibili-16x9.jpg" alt="白羊座 · 春天从这里出发" onerror="this.style.visibility='hidden'"><div><b>♈ 白羊座:春天从这里出发</b><span>星座动画 · 全新制作 · 烈焰金红 · 约 3 分 33 秒 · 专属荣耀铭牌 · 横竖屏 · 24/30/60fps 导出</span></div></a>
|
| 16 |
<a class="card" href="libra-scales/index.html"><img src="libra-scales/covers/cover-landscape.jpg" alt="天秤座 · 星空中的一杆秤" onerror="this.style.visibility='hidden'"><div><b>♎ 天秤座:星空中的一杆秤</b><span>星座动画解说 · 全新制作 · 金色星图风 · 约 3 分 40 秒 · 横竖屏 · 24/30/60fps 导出</span></div></a>
|
| 17 |
<a class="card" href="solana-clock/index.html"><img src="solana-clock/covers/cover-landscape.png" alt="SOLANA 给区块链装上时钟的人" onerror="this.style.visibility='hidden'"><div><b>⏱️ SOLANA:给区块链装上时钟的人</b><span>2017 → 2026 · 全新制作 · 时间之链霓虹风 · 约 7 分钟</span></div></a>
|
|
|
|
| 12 |
</style></head><body>
|
| 13 |
<h1>知识动画工作室</h1><p>网页即播 · 浏览器本地导出 MP4 · 横屏 B站 / 竖屏抖音</p>
|
| 14 |
<div class="grid">
|
| 15 |
+
<a class="card" href="taurus/index.html"><img src="taurus/covers/cover-bili-16x9.jpg" alt="金牛座 · 慢慢来,也能抵达美好" onerror="this.style.visibility='hidden'"><div><b>♉ 金牛座:慢慢来,也能抵达美好</b><span>星座动画 · 全新制作 · 新艺术鎏金花卉 · 约 4 分 10 秒 · 鎏金珍藏卡 · 横竖屏 · 24/30/60fps 导出</span></div></a>
|
| 16 |
<a class="card" href="aries/index.html"><img src="aries/covers/cover-bilibili-16x9.jpg" alt="白羊座 · 春天从这里出发" onerror="this.style.visibility='hidden'"><div><b>♈ 白羊座:春天从这里出发</b><span>星座动画 · 全新制作 · 烈焰金红 · 约 3 分 33 秒 · 专属荣耀铭牌 · 横竖屏 · 24/30/60fps 导出</span></div></a>
|
| 17 |
<a class="card" href="libra-scales/index.html"><img src="libra-scales/covers/cover-landscape.jpg" alt="天秤座 · 星空中的一杆秤" onerror="this.style.visibility='hidden'"><div><b>♎ 天秤座:星空中的一杆秤</b><span>星座动画解说 · 全新制作 · 金色星图风 · 约 3 分 40 秒 · 横竖屏 · 24/30/60fps 导出</span></div></a>
|
| 18 |
<a class="card" href="solana-clock/index.html"><img src="solana-clock/covers/cover-landscape.png" alt="SOLANA 给区块链装上时钟的人" onerror="this.style.visibility='hidden'"><div><b>⏱️ SOLANA:给区块链装上时钟的人</b><span>2017 → 2026 · 全新制作 · 时间之链霓虹风 · 约 7 分钟</span></div></a>
|
taurus/README.md
CHANGED
|
@@ -15,12 +15,25 @@
|
|
| 15 |
|---|---|---|
|
| 16 |
| 1 | 计划 / 大纲 / 步骤(本文件) | ✅ 完成 |
|
| 17 |
| 2 | 旁白文案定稿(10 幕)+ 男声 TTS(云扬,沉稳)+ 字幕对齐 + 时间轴 | ✅ 完成(110 条字幕,总长 4:10) |
|
| 18 |
-
| 3 | 渲染引擎:水彩天幕纹理 + 散景 + 分层视差;10 个场景(横/竖屏) |
|
| 19 |
-
| 4 | 鎏金珍藏卡:3D 翻转、镭射全息闪光、认证印章、个性化署名、单独导出 PNG |
|
| 20 |
-
| 5 | 田园抒情配乐(F 大调 · 电钢琴 / 大提琴 / 八音盒 / 圆号)+ 音效,预渲染 |
|
| 21 |
-
| 6 | 浏览器本地导出 MP4(24/30/60 fps,横/竖屏,1080p/720p) |
|
| 22 |
-
| 7 | 四平台标题/描述/话题 + 8 张封面 |
|
| 23 |
-
| 8 | 首页入口、全片自检、最终推送 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
## 1. 美术与技术方案(区别于以往作品的新架构)
|
| 26 |
|
|
|
|
| 15 |
|---|---|---|
|
| 16 |
| 1 | 计划 / 大纲 / 步骤(本文件) | ✅ 完成 |
|
| 17 |
| 2 | 旁白文案定稿(10 幕)+ 男声 TTS(云扬,沉稳)+ 字幕对齐 + 时间轴 | ✅ 完成(110 条字幕,总长 4:10) |
|
| 18 |
+
| 3 | 渲染引擎:水彩天幕纹理 + 散景 + 分层视差;10 个场景(横/竖屏) | ✅ 完成(逐幕横竖屏截图校对) |
|
| 19 |
+
| 4 | 鎏金珍藏卡:3D 翻转、镭射全息闪光、认证印章、个性化署名、单独导出 PNG | ✅ 完成(9:16 / 1:1 / 3:4) |
|
| 20 |
+
| 5 | 田园抒情配乐(F 大调 · 电钢琴 / 大提琴 / 八音盒 / 圆号)+ 音效,预渲染 | ✅ 完成(audio/film.mp3,−18.7 LUFS,旁白自动闪避) |
|
| 21 |
+
| 6 | 浏览器本地导出 MP4(24/30/60 fps,横/竖屏,1080p/720p) | ✅ 完成(WebCodecs H.264 + AAC/Opus;不支持时实时录制) |
|
| 22 |
+
| 7 | 四平台标题/描述/话题 + 8 张封面 | ✅ 完成(covers/) |
|
| 23 |
+
| 8 | 首页入口、全片自检、最终推送 | ✅ 完成 |
|
| 24 |
+
|
| 25 |
+
## 0.1 文件结构
|
| 26 |
+
|
| 27 |
+
| 文件 | 作用 |
|
| 28 |
+
|---|---|
|
| 29 |
+
| `index.html` · `style.css` · `js/main.js` | 页面:播放器、珍藏卡、导出、发布包 |
|
| 30 |
+
| `js/canvas-kit.js` · `js/backdrop.js` | 绘图库与水彩天幕 |
|
| 31 |
+
| `js/acts.js` · `js/film.js` | 十幕动画与合成器(画面 = 时间 t 的纯函数,播放与导出逐帧一致) |
|
| 32 |
+
| `js/card.js` | 鎏金珍藏卡 |
|
| 33 |
+
| `js/music.js` · `tools/mix.mjs` | 配乐合成与混音(`FF=ffmpeg路径 node tools/mix.mjs`) |
|
| 34 |
+
| `js/encoder.js` · `vendor/mp4-muxer.mjs` | 本地 MP4 导出 |
|
| 35 |
+
| `js/release.js` · `covers/` | 四平台文案与封面 |
|
| 36 |
+
| `tools/voice.py` · `js/script.js` · `audio/v01..v10.mp3` | 旁白与字幕时间轴 |
|
| 37 |
|
| 38 |
## 1. 美术与技术方案(区别于以往作品的新架构)
|
| 39 |
|
taurus/js/main.js
CHANGED
|
@@ -78,13 +78,15 @@ function save() { localStorage.setItem('taurus.name', $('#name').value.trim());
|
|
| 78 |
$('#name').value = store.name; if (store.bday) { const [m, d] = store.bday.split('-'); bm.value = m; fillDays(); bd.value = d; } else fillDays(); save();
|
| 79 |
const cardCv = $('#cardView'), cg = cardCv.getContext('2d'); let showBack = false, flipK = 1, cardT0 = performance.now();
|
| 80 |
$('#flipBtn').onclick = () => { showBack = !showBack; $('#flipBtn').textContent = showBack ? '翻回正面' : '翻转看看背面'; };
|
| 81 |
-
let cardVisible = false, cardDrawn = false; new IntersectionObserver(es => es.forEach(e => cardVisible = e.isIntersecting)).observe(cardCv);
|
| 82 |
function cardLoop() {
|
| 83 |
if ((!cardVisible || document.hidden) && cardDrawn) return requestAnimationFrame(cardLoop);
|
| 84 |
cardDrawn = true;
|
| 85 |
-
const
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
| 88 |
drawCard(cg, W / 2, H / 2, H * .86, { t, flip: flipK, fields: 1, shine: ((t * .22) % 1.4) / 1.4, holo: .8, stamp: 1, holder: opts().name, birthday: opts().birthday, tilt: Math.sin(t * .7) * .015 });
|
| 89 |
requestAnimationFrame(cardLoop);
|
| 90 |
}
|
|
|
|
| 78 |
$('#name').value = store.name; if (store.bday) { const [m, d] = store.bday.split('-'); bm.value = m; fillDays(); bd.value = d; } else fillDays(); save();
|
| 79 |
const cardCv = $('#cardView'), cg = cardCv.getContext('2d'); let showBack = false, flipK = 1, cardT0 = performance.now();
|
| 80 |
$('#flipBtn').onclick = () => { showBack = !showBack; $('#flipBtn').textContent = showBack ? '翻回正面' : '翻转看看背面'; };
|
| 81 |
+
let cardVisible = false, cardDrawn = false, lastCard = 0, cardBg = null; new IntersectionObserver(es => es.forEach(e => cardVisible = e.isIntersecting)).observe(cardCv);
|
| 82 |
function cardLoop() {
|
| 83 |
if ((!cardVisible || document.hidden) && cardDrawn) return requestAnimationFrame(cardLoop);
|
| 84 |
cardDrawn = true;
|
| 85 |
+
const nowMs = performance.now(); if (cardDrawn > 1 && nowMs - lastCard < 33) return requestAnimationFrame(cardLoop); lastCard = nowMs; cardDrawn = 2;
|
| 86 |
+
const t = (nowMs - cardT0) / 1000, W = cardCv.width, H = cardCv.height;
|
| 87 |
+
flipK += ((showBack ? 0 : 1) - flipK) * .2;
|
| 88 |
+
if (!cardBg) { cardBg = document.createElement('canvas'); cardBg.width = W; cardBg.height = H; paintBg(cardBg.getContext('2d'), W, H, 1); }
|
| 89 |
+
cg.drawImage(cardBg, 0, 0);
|
| 90 |
drawCard(cg, W / 2, H / 2, H * .86, { t, flip: flipK, fields: 1, shine: ((t * .22) % 1.4) / 1.4, holo: .8, stamp: 1, holder: opts().name, birthday: opts().birthday, tilt: Math.sin(t * .7) * .015 });
|
| 91 |
requestAnimationFrame(cardLoop);
|
| 92 |
}
|