chore: 初始化仓库,提交 vibevideo 源码
- src/: HyperFrames 封装 CLI(hv)、组件、品牌样式、音频(TTS/混音)工具 - projects/: 各项目 project.yaml 与文案 - interactive/: 井字棋演示页 - .gitignore: 忽略 node_modules、构建产物与 HyperFrames 运行期缓存
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN"
|
||||
data-composition-variables='[{"id": "heading", "type": "string", "label": "标题", "default": "用 **加粗** 和 [[高亮]]"},{"id": "body", "type": "string", "label": "正文(可空)", "default": "支持 **加粗** 与 [[强调色]]"}]'>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>文字块</title>
|
||||
<link rel="stylesheet" href="assets/brand/tokens.css" />
|
||||
<link rel="stylesheet" href="assets/brand/base.css" />
|
||||
<style>
|
||||
.stage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: calc(60 * var(--u));
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: calc(24 * var(--u));
|
||||
}
|
||||
|
||||
.body {
|
||||
font-size: calc(30 * var(--u));
|
||||
font-weight: 500;
|
||||
line-height: 1.55;
|
||||
color: var(--text-dim);
|
||||
max-width: 82%;
|
||||
}
|
||||
|
||||
/* 逐行浮现:每行一个块 */
|
||||
.line { overflow: hidden; }
|
||||
.line > span { display: inline-block; }
|
||||
|
||||
.accent { color: var(--accent); }
|
||||
.strong { color: var(--text); font-weight: 700; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
class="stage"
|
||||
data-composition-id="textblock"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
data-duration="4"
|
||||
>
|
||||
<div id="heading" class="heading"></div>
|
||||
<div id="body" class="body"></div>
|
||||
|
||||
<script>
|
||||
const v = window.hv ? hv.readVars() : null;
|
||||
const str = (k, d) => (v ? v.str(k, d) : d);
|
||||
|
||||
const HEAD = str("heading", "未命名标题");
|
||||
const BODY = str("body", "");
|
||||
|
||||
const headingEl = document.getElementById("heading");
|
||||
const bodyEl = document.getElementById("body");
|
||||
|
||||
/* 支持在文字里用 **强调** 和 [[高亮]] 标记 */
|
||||
const rich = (text) => {
|
||||
const frag = document.createDocumentFragment();
|
||||
const re = /\*\*([^*]+)\*\*|\[\[([^\]]+)\]\]/g;
|
||||
let last = 0;
|
||||
let m;
|
||||
while ((m = re.exec(text)) !== null) {
|
||||
if (m.index > last) frag.appendChild(document.createTextNode(text.slice(last, m.index)));
|
||||
const el = document.createElement("span");
|
||||
if (m[1]) {
|
||||
el.className = "strong";
|
||||
el.textContent = m[1];
|
||||
} else {
|
||||
el.className = "accent";
|
||||
el.textContent = m[2];
|
||||
}
|
||||
frag.appendChild(el);
|
||||
last = m.index + m[0].length;
|
||||
}
|
||||
if (last < text.length) frag.appendChild(document.createTextNode(text.slice(last)));
|
||||
return frag;
|
||||
};
|
||||
|
||||
headingEl.textContent = "";
|
||||
headingEl.appendChild(rich(HEAD));
|
||||
|
||||
bodyEl.textContent = "";
|
||||
if (BODY) {
|
||||
const span = document.createElement("span");
|
||||
span.appendChild(rich(BODY));
|
||||
bodyEl.appendChild(span);
|
||||
} else {
|
||||
bodyEl.style.display = "none";
|
||||
}
|
||||
|
||||
// 时长跟随宿主(可能由配音决定),拿不到才用组件默认值
|
||||
const DUR = (window.hv && hv.duration()) || 4;
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
tl.fromTo(headingEl, { opacity: 0, y: 28 }, { opacity: 1, y: 0, duration: 0.5, ease: "power3.out" }, 0.1)
|
||||
.fromTo(bodyEl, { opacity: 0, y: 24 }, { opacity: 1, y: 0, duration: 0.5, ease: "power3.out" }, 0.35)
|
||||
.to(".stage", { opacity: 0, duration: 0.3, ease: "power1.in" }, DUR - 0.3);
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["textblock"] = tl;
|
||||
tl.seek(0);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user