40760aa884
- server:Go 网关(WS 流式识别中继/计费配额/微信登录支付 mock/反馈/埋点),gummy provider 已真实联调 - desktop:Tauri 2(全局快捷键 push-to-talk/浮层/托盘/设置/登录购买/反馈/首启引导) - android:Compose 主 App + IME(键盘内录音直传) - ios:App + 键盘扩展(1A spike 实证键盘内不可录音,走 deep link 听写) - design/design-pipeline:设计系统 + token 导出 iOS/Android 主题 - doc:前后端设计文档(HTML);web:官网宣传页;todo:任务看板 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
624 lines
28 KiB
HTML
624 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>dudu — 按住说话,文字上屏</title>
|
||
<meta name="description" content="dudu 语音输入法:大模型识别,流式实时上屏。Mac / Windows / iPhone / iPad / Android。每天免费 3 分钟。">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@500;600;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
/* ────────────────────────────────────────────────────────────────
|
||
令牌来源 design/tokens/(宣传页独立部署,按值内联;改动需同步)
|
||
品牌硬规则:单一品牌蓝 · 无渐变 · 无 emoji · 波形动画仅录音中
|
||
──────────────────────────────────────────────────────────────── */
|
||
:root {
|
||
--bg: #F6F7FA;
|
||
--card: #FFFFFF;
|
||
--surface-2: #F0F1F4;
|
||
--surface-3: #E4E6EB;
|
||
--border-1: #E4E6EB;
|
||
--border-2: #D2D5DD;
|
||
--text-1: #1B1E26;
|
||
--text-2: #5A6072;
|
||
--text-3: #A6ABB8;
|
||
--accent: #4F6EF7;
|
||
--accent-hover: #3D58DB;
|
||
--accent-press: #2F44B3;
|
||
--accent-soft: #EEF1FE;
|
||
--accent-text: #3D58DB;
|
||
--positive: #16A34A;
|
||
--positive-soft: #DCFCE7;
|
||
--warning: #E8890C;
|
||
--warning-soft: #FCEFD7;
|
||
--ink: #11131A;
|
||
--ink-card: #171A22;
|
||
--ink-border: #272C38;
|
||
--ink-text-2: #9AA1B2;
|
||
--overlay-bg: rgba(24, 26, 34, 0.95);
|
||
--overlay-text: #F2F4FA;
|
||
--overlay-text-2: #9AA3BD;
|
||
--overlay-text-3: #646E8C;
|
||
--shadow-overlay: 0 12px 36px rgba(10, 12, 18, 0.38);
|
||
--shadow-card: 0 1px 3px rgba(17, 19, 26, 0.05);
|
||
--shadow-window: 0 8px 28px rgba(17, 19, 26, 0.14);
|
||
--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
|
||
--sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "MiSans",
|
||
"HarmonyOS Sans SC", "Microsoft YaHei", sans-serif;
|
||
--latin: "Outfit", -apple-system, sans-serif;
|
||
--mono: ui-monospace, "SF Mono", Menlo, monospace;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; }
|
||
html { scroll-behavior: smooth; }
|
||
body {
|
||
font-family: var(--sans);
|
||
background: var(--bg);
|
||
color: var(--text-1);
|
||
line-height: 1.6;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
::selection { background: var(--accent-soft); color: var(--accent-text); }
|
||
a { color: inherit; text-decoration: none; }
|
||
.latin { font-family: var(--latin); }
|
||
|
||
/* ── 结构网格:1px 边线是主要分隔语言 ── */
|
||
.frame { max-width: 1120px; margin: 0 auto; padding: 0 32px; }
|
||
@media (max-width: 720px) { .frame { padding: 0 20px; } }
|
||
|
||
/* ── 导航 ── */
|
||
nav {
|
||
position: sticky; top: 0; z-index: 50;
|
||
background: color-mix(in srgb, var(--bg) 88%, transparent);
|
||
backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
|
||
border-bottom: 1px solid var(--border-1);
|
||
}
|
||
.nav-inner {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
height: 60px;
|
||
}
|
||
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--text-1); }
|
||
.brand svg { color: var(--accent); }
|
||
.brand .name { font-family: var(--latin); font-weight: 600; font-size: 19px; letter-spacing: .02em; }
|
||
.nav-links { display: flex; align-items: center; gap: 28px; font-size: 14px; color: var(--text-2); }
|
||
.nav-links a:hover { color: var(--text-1); }
|
||
.btn {
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
||
border: 1px solid transparent; border-radius: 6px; cursor: pointer;
|
||
font-family: var(--sans); font-weight: 500; white-space: nowrap;
|
||
transition: background 120ms var(--ease-out), border-color 120ms var(--ease-out);
|
||
}
|
||
.btn-sm { height: 32px; padding: 0 14px; font-size: 13px; }
|
||
.btn-lg { height: 48px; padding: 0 26px; font-size: 16px; border-radius: 999px; }
|
||
.btn-primary { background: var(--accent); color: #fff; }
|
||
.btn-primary:hover { background: var(--accent-hover); }
|
||
.btn-primary:active { background: var(--accent-press); }
|
||
.btn-secondary { background: var(--card); border-color: var(--border-2); color: var(--text-1); }
|
||
.btn-secondary:hover { background: var(--surface-2); }
|
||
.btn-ghost { background: transparent; color: var(--accent-text); }
|
||
.btn-ghost:hover { background: var(--accent-soft); }
|
||
|
||
/* ── Hero ── */
|
||
.hero { border-bottom: 1px solid var(--border-1); overflow: hidden; }
|
||
.hero-grid {
|
||
display: grid; grid-template-columns: 6fr 5fr; gap: 48px;
|
||
padding: 96px 0 110px; align-items: center;
|
||
}
|
||
@media (max-width: 920px) {
|
||
.hero-grid { grid-template-columns: 1fr; padding: 64px 0 72px; gap: 56px; }
|
||
}
|
||
.hero h1 {
|
||
font-size: clamp(48px, 7.2vw, 84px);
|
||
line-height: 1.12; letter-spacing: -0.015em;
|
||
font-weight: 700;
|
||
}
|
||
.hero h1 .accent-word { color: var(--accent); }
|
||
.hero .sub {
|
||
margin-top: 22px; font-size: 18px; color: var(--text-2);
|
||
display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
|
||
}
|
||
.hero .sub .dot { color: var(--text-3); }
|
||
.hero-cta { margin-top: 38px; display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
|
||
.hero-note { font-size: 13px; color: var(--text-3); }
|
||
.hero-note .num { font-family: var(--latin); }
|
||
|
||
/* ── 演示剧场 ── */
|
||
.stage { position: relative; }
|
||
.demo-window {
|
||
background: var(--card); border: 1px solid var(--border-1);
|
||
border-radius: 14px; box-shadow: var(--shadow-window); overflow: hidden;
|
||
}
|
||
.demo-titlebar {
|
||
display: flex; align-items: center; gap: 7px;
|
||
padding: 11px 14px; border-bottom: 1px solid var(--border-1); background: var(--surface-2);
|
||
}
|
||
.demo-titlebar i { width: 11px; height: 11px; border-radius: 50%; background: var(--border-2); }
|
||
.demo-titlebar span { margin-left: 8px; font-size: 12px; color: var(--text-3); }
|
||
.demo-doc { padding: 22px 24px 30px; min-height: 150px; font-size: 16px; line-height: 1.9; }
|
||
.demo-doc .placeholder { color: var(--text-3); }
|
||
.caret {
|
||
display: inline-block; width: 2px; height: 1.15em; vertical-align: -0.18em;
|
||
background: var(--accent); animation: blink 1.1s steps(1) infinite;
|
||
}
|
||
@keyframes blink { 50% { opacity: 0; } }
|
||
|
||
.overlay {
|
||
position: absolute; left: 50%; transform: translateX(-50%);
|
||
bottom: -34px; width: min(340px, 86%);
|
||
background: var(--overlay-bg); color: var(--overlay-text);
|
||
border-radius: 14px; padding: 14px 18px;
|
||
box-shadow: var(--shadow-overlay);
|
||
backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
|
||
opacity: 0; translate: 0 4px;
|
||
transition: opacity 200ms var(--ease-out), translate 200ms var(--ease-out);
|
||
pointer-events: none;
|
||
}
|
||
.overlay.show { opacity: 1; translate: 0 0; }
|
||
.wave { display: inline-flex; align-items: center; gap: 3px; height: 24px; color: #7C9BFF; }
|
||
.wave i { display: block; width: 3px; border-radius: 2px; background: currentColor; }
|
||
.recording .wave i { animation: wavebounce 0.9s cubic-bezier(0.45, 0, 0.2, 1) infinite alternate; }
|
||
@keyframes wavebounce { from { transform: scaleY(0.35); } to { transform: scaleY(1); } }
|
||
.overlay .ovl-status {
|
||
display: flex; align-items: center; gap: 8px;
|
||
color: var(--overlay-text-2); font-size: 15px; margin-top: 8px;
|
||
}
|
||
.overlay .ovl-text { font-size: 15px; line-height: 1.6; margin-top: 8px; min-height: 24px; }
|
||
.overlay .ovl-text .partial { color: var(--overlay-text-2); }
|
||
.overlay .ovl-hint { font-size: 11px; color: var(--overlay-text-3); margin-top: 8px; text-align: right; }
|
||
|
||
.micbar {
|
||
margin-top: 64px; width: 100%; height: 56px; border: none; cursor: pointer;
|
||
border-radius: 999px; background: var(--accent); color: #fff;
|
||
font-family: var(--sans); font-size: 16px; font-weight: 600;
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 9px;
|
||
user-select: none; -webkit-user-select: none; touch-action: none;
|
||
transition: background 120ms var(--ease-out);
|
||
}
|
||
.micbar:active, .micbar.recording { background: var(--positive); }
|
||
.micbar:hover:not(.recording) { background: var(--accent-hover); }
|
||
.stage-tip { margin-top: 12px; text-align: center; font-size: 13px; color: var(--text-3); }
|
||
kbd {
|
||
font-family: var(--mono); font-size: 12px; color: var(--text-2);
|
||
background: var(--surface-2); border: 1px solid var(--border-2); border-bottom-width: 2px;
|
||
border-radius: 4px; padding: 1px 7px;
|
||
}
|
||
|
||
/* ── 指标带(墨色) ── */
|
||
.metrics { background: var(--ink); color: #ECEEF4; border-bottom: 1px solid var(--ink-border); }
|
||
.metrics-grid { display: grid; grid-template-columns: repeat(3, 1fr); }
|
||
.metric {
|
||
padding: 44px 32px; text-align: center;
|
||
border-left: 1px solid var(--ink-border);
|
||
}
|
||
.metric:first-child { border-left: none; }
|
||
.metric .num {
|
||
font-family: var(--latin); font-weight: 700;
|
||
font-size: clamp(34px, 4.4vw, 52px); line-height: 1.1;
|
||
}
|
||
.metric .num small { font-size: 0.45em; font-weight: 600; color: var(--ink-text-2); margin-left: 2px; }
|
||
.metric .label { margin-top: 8px; font-size: 14px; color: var(--ink-text-2); }
|
||
@media (max-width: 720px) {
|
||
.metrics-grid { grid-template-columns: 1fr; }
|
||
.metric { border-left: none; border-top: 1px solid var(--ink-border); padding: 28px 20px; }
|
||
.metric:first-child { border-top: none; }
|
||
}
|
||
|
||
/* ── 章节通用 ── */
|
||
section { border-bottom: 1px solid var(--border-1); }
|
||
.section-head { padding: 88px 0 0; }
|
||
.kicker {
|
||
font-size: 13px; font-weight: 600; color: var(--accent-text);
|
||
letter-spacing: .12em;
|
||
}
|
||
.section-head h2 {
|
||
margin-top: 12px; font-size: clamp(30px, 4vw, 44px);
|
||
line-height: 1.25; letter-spacing: -0.01em; font-weight: 700; max-width: 18em;
|
||
}
|
||
.reveal { opacity: 0; translate: 0 14px; transition: opacity 320ms var(--ease-out), translate 320ms var(--ease-out); }
|
||
.reveal.in { opacity: 1; translate: 0 0; }
|
||
|
||
/* ── 特性 ── */
|
||
.features {
|
||
display: grid; grid-template-columns: repeat(3, 1fr);
|
||
margin: 56px 0 96px; border: 1px solid var(--border-1); border-radius: 14px;
|
||
background: var(--card); overflow: hidden;
|
||
}
|
||
.feature { padding: 36px 32px 40px; border-left: 1px solid var(--border-1); }
|
||
.feature:first-child { border-left: none; }
|
||
.feature .icon {
|
||
width: 44px; height: 44px; border-radius: 10px;
|
||
background: var(--accent-soft); color: var(--accent-text);
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
}
|
||
.feature h3 { margin-top: 18px; font-size: 19px; font-weight: 600; }
|
||
.feature p { margin-top: 8px; font-size: 14.5px; color: var(--text-2); }
|
||
@media (max-width: 820px) {
|
||
.features { grid-template-columns: 1fr; }
|
||
.feature { border-left: none; border-top: 1px solid var(--border-1); }
|
||
.feature:first-child { border-top: none; }
|
||
}
|
||
|
||
/* ── 三步 ── */
|
||
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; margin: 64px 0 96px; }
|
||
.step .no {
|
||
font-family: var(--latin); font-weight: 700; font-size: 72px; line-height: 1;
|
||
color: var(--surface-3);
|
||
}
|
||
.step h3 { margin-top: 14px; font-size: 19px; font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||
.step p { margin-top: 6px; font-size: 14.5px; color: var(--text-2); max-width: 26em; }
|
||
@media (max-width: 820px) { .steps { grid-template-columns: 1fr; gap: 40px; } }
|
||
|
||
/* ── 平台 ── */
|
||
.platforms { display: flex; flex-wrap: wrap; gap: 12px; margin: 48px 0 96px; }
|
||
.platform {
|
||
display: inline-flex; align-items: center; gap: 9px;
|
||
padding: 12px 22px; border: 1px solid var(--border-1); border-radius: 999px;
|
||
background: var(--card); font-size: 15px; font-weight: 500; box-shadow: var(--shadow-card);
|
||
}
|
||
.platform svg { color: var(--text-2); }
|
||
.platform .soon { font-size: 12px; color: var(--text-3); }
|
||
|
||
/* ── 定价 ── */
|
||
.pricing { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin: 56px 0 28px; align-items: stretch; }
|
||
.price-card {
|
||
background: var(--card); border: 1px solid var(--border-1); border-radius: 14px;
|
||
padding: 30px 28px 28px; display: flex; flex-direction: column; box-shadow: var(--shadow-card);
|
||
position: relative;
|
||
}
|
||
.price-card.hot { border: 1.5px solid var(--accent); }
|
||
.price-card .tag {
|
||
position: absolute; top: -11px; left: 24px;
|
||
font-size: 12px; font-weight: 600; padding: 2px 12px; border-radius: 999px;
|
||
background: var(--warning-soft); color: var(--warning);
|
||
}
|
||
.price-card .minutes { font-size: 17px; font-weight: 600; }
|
||
.price-card .minutes .num { font-family: var(--latin); font-size: 22px; }
|
||
.price-card .price { margin-top: 14px; font-family: var(--latin); font-weight: 700; font-size: 56px; line-height: 1; }
|
||
.price-card .price .cur { font-size: 24px; font-weight: 600; vertical-align: 14px; margin-right: 2px; }
|
||
.price-card .unit { margin-top: 8px; font-size: 13px; color: var(--text-3); }
|
||
.price-card .btn { margin-top: 26px; width: 100%; }
|
||
.pricing-note { text-align: center; font-size: 14px; color: var(--text-2); padding-bottom: 96px; }
|
||
.pricing-note .num { font-family: var(--latin); }
|
||
@media (max-width: 820px) { .pricing { grid-template-columns: 1fr; } }
|
||
|
||
/* ── 尾部 CTA(墨色) ── */
|
||
.cta { background: var(--ink); color: #ECEEF4; border-bottom: 1px solid var(--ink-border); }
|
||
.cta-inner { padding: 110px 0; text-align: center; }
|
||
.cta .logo-big { color: #5C77E8; }
|
||
.cta h2 { margin-top: 26px; font-size: clamp(34px, 5vw, 56px); font-weight: 700; letter-spacing: -0.01em; }
|
||
.cta p { margin-top: 14px; color: var(--ink-text-2); font-size: 16px; }
|
||
.cta .btn { margin-top: 36px; }
|
||
.cta .btn-primary { background: #5C77E8; }
|
||
.cta .btn-primary:hover { background: #6E89F9; }
|
||
|
||
/* ── 页脚 ── */
|
||
footer { background: var(--ink); color: var(--ink-text-2); }
|
||
.footer-inner {
|
||
display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px;
|
||
padding: 28px 0; font-size: 13px;
|
||
}
|
||
.footer-inner .brand { color: #ECEEF4; }
|
||
.footer-inner .brand svg { color: #5C77E8; }
|
||
.footer-links { display: flex; gap: 22px; }
|
||
.footer-links a:hover { color: #ECEEF4; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ════════ 导航 ════════ -->
|
||
<nav>
|
||
<div class="frame nav-inner">
|
||
<a class="brand" href="#">
|
||
<svg width="26" height="26" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"><circle cx="24" cy="24" r="18"/><path d="M17 20.5v7"/><path d="M24 16.5v15"/><path d="M31 20.5v7"/></svg>
|
||
<span class="name">dudu</span>
|
||
</a>
|
||
<div class="nav-links">
|
||
<a href="#features">特性</a>
|
||
<a href="#how">怎么用</a>
|
||
<a href="#pricing">价格</a>
|
||
<a class="btn btn-sm btn-secondary" href="#download">下载</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- ════════ Hero ════════ -->
|
||
<header class="hero">
|
||
<div class="frame hero-grid">
|
||
<div>
|
||
<h1>按住说话<br><span class="accent-word">文字上屏</span></h1>
|
||
<div class="sub">
|
||
<span>大模型识别</span><span class="dot">·</span>
|
||
<span>流式实时</span><span class="dot">·</span>
|
||
<span>全平台输入法</span>
|
||
</div>
|
||
<div class="hero-cta">
|
||
<a class="btn btn-lg btn-primary" href="#download">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
|
||
免费下载
|
||
</a>
|
||
<span class="hero-note">每天免费 <span class="num">3</span> 分钟 · 微信登录即用</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 演示剧场:按住下方按钮(或长按空格)真实体验流式上屏 -->
|
||
<div class="stage" id="stage">
|
||
<div class="demo-window">
|
||
<div class="demo-titlebar"><i></i><i></i><i></i><span>备忘录 — 周报草稿</span></div>
|
||
<div class="demo-doc">
|
||
<span id="doc-text"></span><span class="placeholder" id="doc-placeholder">光标在这里 — 在任意应用按住快捷键即可语音输入</span><span class="caret"></span>
|
||
</div>
|
||
<div class="overlay" id="overlay">
|
||
<span class="wave" id="wave"></span>
|
||
<div class="ovl-status" id="ovl-status">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
|
||
<span>聆听中…</span>
|
||
</div>
|
||
<div class="ovl-text" id="ovl-text" hidden></div>
|
||
<div class="ovl-hint">松开 ⌘⇧Space 完成输入</div>
|
||
</div>
|
||
</div>
|
||
<button class="micbar" id="micbar" type="button">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
|
||
<span id="micbar-label">按住试一试</span>
|
||
</button>
|
||
<div class="stage-tip">就像在你电脑上按住 <kbd>⌘</kbd><kbd>⇧</kbd><kbd>Space</kbd> 一样</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ════════ 指标带 ════════ -->
|
||
<div class="metrics">
|
||
<div class="frame metrics-grid">
|
||
<div class="metric reveal">
|
||
<div class="num"><500<small>ms</small></div>
|
||
<div class="label">按下到首字出现</div>
|
||
</div>
|
||
<div class="metric reveal" style="transition-delay:60ms">
|
||
<div class="num">5<small>端</small></div>
|
||
<div class="label">Mac · Windows · iPhone · iPad · Android</div>
|
||
</div>
|
||
<div class="metric reveal" style="transition-delay:120ms">
|
||
<div class="num">3<small>分钟/天</small></div>
|
||
<div class="label">免费试用 · 不需要先付费</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════ 特性 ════════ -->
|
||
<section id="features">
|
||
<div class="frame">
|
||
<div class="section-head reveal">
|
||
<div class="kicker">为什么是 dudu</div>
|
||
<h2>识别得准,上屏得快<br>用起来像没有这层软件</h2>
|
||
</div>
|
||
<div class="features reveal">
|
||
<div class="feature">
|
||
<span class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 10v3"/><path d="M6 6v11"/><path d="M10 3v18"/><path d="M14 8v7"/><path d="M18 5v13"/><path d="M22 10v3"/></svg></span>
|
||
<h3>大模型识别</h3>
|
||
<p>云端大模型听写,中英混说、专有名词、自动标点都不在话下,说完即定稿</p>
|
||
</div>
|
||
<div class="feature">
|
||
<span class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg></span>
|
||
<h3>流式实时上屏</h3>
|
||
<p>说话的同时文字就出现,不等你说完;松开按键,整句注入焦点输入框</p>
|
||
</div>
|
||
<div class="feature">
|
||
<span class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg></span>
|
||
<h3>哪里都能用</h3>
|
||
<p>桌面端快捷键全局可用,手机上是真正的系统键盘——微信、文档、代码编辑器,焦点在哪写到哪</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════ 怎么用 ════════ -->
|
||
<section id="how">
|
||
<div class="frame">
|
||
<div class="section-head reveal">
|
||
<div class="kicker">怎么用</div>
|
||
<h2>三个动作,零学习成本</h2>
|
||
</div>
|
||
<div class="steps">
|
||
<div class="step reveal">
|
||
<div class="no">01</div>
|
||
<h3>按住 <kbd>⌘</kbd><kbd>⇧</kbd><kbd>Space</kbd></h3>
|
||
<p>桌面端任意输入框旁按住快捷键;手机上按住键盘的大麦克风</p>
|
||
</div>
|
||
<div class="step reveal" style="transition-delay:60ms">
|
||
<div class="no">02</div>
|
||
<h3>说话</h3>
|
||
<p>识别结果实时浮现,说错了上滑取消,不打断你的思路</p>
|
||
</div>
|
||
<div class="step reveal" style="transition-delay:120ms">
|
||
<div class="no">03</div>
|
||
<h3>松开,上屏</h3>
|
||
<p>整句文字直接写进光标处,标点已排好,接着说下一句就行</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════ 平台 ════════ -->
|
||
<section id="download">
|
||
<div class="frame">
|
||
<div class="section-head reveal">
|
||
<div class="kicker">全平台</div>
|
||
<h2>你的每一台设备</h2>
|
||
</div>
|
||
<div class="platforms reveal">
|
||
<span class="platform"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg>Mac</span>
|
||
<span class="platform"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg>Windows</span>
|
||
<span class="platform"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="20" x="5" y="2" rx="2"/><path d="M12 18h.01"/></svg>iPhone<span class="soon">键盘扩展</span></span>
|
||
<span class="platform"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="22" x="3" y="1" rx="2"/><path d="M12 19h.01"/></svg>iPad</span>
|
||
<span class="platform"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="20" x="5" y="2" rx="2"/><path d="M12 18h.01"/></svg>Android<span class="soon">系统输入法</span></span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════ 价格 ════════ -->
|
||
<section id="pricing">
|
||
<div class="frame">
|
||
<div class="section-head reveal">
|
||
<div class="kicker">价格</div>
|
||
<h2>按时长买,永不过期<br>不订阅、不扣月费</h2>
|
||
</div>
|
||
<div class="pricing reveal">
|
||
<div class="price-card">
|
||
<div class="minutes"><span class="num">100</span> 分钟</div>
|
||
<div class="price"><span class="cur">¥</span>9</div>
|
||
<div class="unit">约 ¥0.09 / 分钟</div>
|
||
<a class="btn btn-lg btn-secondary" href="#download">选这档</a>
|
||
</div>
|
||
<div class="price-card hot">
|
||
<span class="tag">最多人选 · 省 13%</span>
|
||
<div class="minutes"><span class="num">500</span> 分钟</div>
|
||
<div class="price"><span class="cur">¥</span>39</div>
|
||
<div class="unit">约 ¥0.078 / 分钟</div>
|
||
<a class="btn btn-lg btn-primary" href="#download">选这档</a>
|
||
</div>
|
||
<div class="price-card">
|
||
<span class="tag">省 28%</span>
|
||
<div class="minutes"><span class="num">2000</span> 分钟</div>
|
||
<div class="price"><span class="cur">¥</span>129</div>
|
||
<div class="unit">约 ¥0.065 / 分钟</div>
|
||
<a class="btn btn-lg btn-secondary" href="#download">选这档</a>
|
||
</div>
|
||
</div>
|
||
<div class="pricing-note">每天另有 <span class="num">3</span> 分钟免费试用 · 微信支付,到账即用 · 时长永不过期</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════ 尾部 CTA ════════ -->
|
||
<div class="cta">
|
||
<div class="frame cta-inner reveal">
|
||
<svg class="logo-big" width="64" height="64" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"><circle cx="24" cy="24" r="18"/><path d="M17 20.5v7"/><path d="M24 16.5v15"/><path d="M31 20.5v7"/></svg>
|
||
<h2>从下一句话开始<br>用说的</h2>
|
||
<p>下载 dudu,微信扫码登录,每天免费 3 分钟</p>
|
||
<a class="btn btn-lg btn-primary" href="#download">免费下载 dudu</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════ 页脚 ════════ -->
|
||
<footer>
|
||
<div class="frame footer-inner">
|
||
<a class="brand" href="#">
|
||
<svg width="20" height="20" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"><circle cx="24" cy="24" r="18"/><path d="M17 20.5v7"/><path d="M24 16.5v15"/><path d="M31 20.5v7"/></svg>
|
||
<span class="name latin" style="font-size:15px;font-weight:600">dudu</span>
|
||
</a>
|
||
<div class="footer-links">
|
||
<a href="#">用户协议</a>
|
||
<a href="#">隐私政策</a>
|
||
<a href="#">联系我们</a>
|
||
</div>
|
||
<div>© 2026 dudu · 沪ICP备XXXXXXXX号</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
/* ── 波形条 ── */
|
||
const WAVE_PATTERN = [0.3, 0.55, 0.85, 0.45, 0.7, 1, 0.4, 0.6, 0.25, 0.8, 0.5, 0.35];
|
||
const wave = document.getElementById('wave');
|
||
WAVE_PATTERN.forEach((p, i) => {
|
||
const bar = document.createElement('i');
|
||
bar.style.height = Math.max(4, Math.round(p * 24)) + 'px';
|
||
bar.style.animationDelay = (i % 6) * 0.07 + 's';
|
||
wave.appendChild(bar);
|
||
});
|
||
|
||
/* ── 演示剧场:按住 = 流式识别,松开 = 上屏 ── */
|
||
const SENTENCES = [
|
||
'帮我把这份周报整理一下,重点突出本周的进展和下周的计划。',
|
||
'好的,我马上把文件发给你,大概十分钟之内。',
|
||
'明天上午十点的会改到下午三点,记得同步给项目组。',
|
||
];
|
||
const stage = document.getElementById('stage');
|
||
const overlay = document.getElementById('overlay');
|
||
const ovlStatus = document.getElementById('ovl-status');
|
||
const ovlText = document.getElementById('ovl-text');
|
||
const docText = document.getElementById('doc-text');
|
||
const docPlaceholder = document.getElementById('doc-placeholder');
|
||
const micbar = document.getElementById('micbar');
|
||
const micbarLabel = document.getElementById('micbar-label');
|
||
|
||
let timer = null, idx = 0, finalText = '', partialText = '', recording = false;
|
||
|
||
function render() {
|
||
const has = finalText || partialText;
|
||
ovlStatus.hidden = !!has;
|
||
ovlText.hidden = !has;
|
||
ovlText.innerHTML = '';
|
||
ovlText.append(finalText);
|
||
if (partialText) {
|
||
const s = document.createElement('span');
|
||
s.className = 'partial';
|
||
s.textContent = partialText;
|
||
ovlText.appendChild(s);
|
||
}
|
||
}
|
||
|
||
function start() {
|
||
if (recording) return;
|
||
recording = true;
|
||
finalText = ''; partialText = '';
|
||
render();
|
||
stage.classList.add('recording');
|
||
micbar.classList.add('recording');
|
||
micbarLabel.textContent = '松开完成输入';
|
||
overlay.classList.add('show');
|
||
|
||
const sentence = SENTENCES[idx % SENTENCES.length];
|
||
let i = 0;
|
||
timer = setInterval(() => {
|
||
i += 1;
|
||
if (i > sentence.length) { clearInterval(timer); return; }
|
||
const seen = sentence.slice(0, i);
|
||
const lastPunct = Math.max(seen.lastIndexOf(','), seen.lastIndexOf('。'));
|
||
finalText = sentence.slice(0, lastPunct + 1);
|
||
partialText = sentence.slice(lastPunct + 1, i);
|
||
render();
|
||
}, 75);
|
||
}
|
||
|
||
function stop() {
|
||
if (!recording) return;
|
||
recording = false;
|
||
clearInterval(timer);
|
||
stage.classList.remove('recording');
|
||
micbar.classList.remove('recording');
|
||
micbarLabel.textContent = '按住试一试';
|
||
overlay.classList.remove('show');
|
||
|
||
const committed = finalText + partialText;
|
||
if (committed) {
|
||
idx += 1;
|
||
docPlaceholder.hidden = true;
|
||
// 模拟注入:整句快速"打"进文档
|
||
let j = 0;
|
||
const base = docText.textContent;
|
||
const typeTimer = setInterval(() => {
|
||
j += 3;
|
||
docText.textContent = base + committed.slice(0, j);
|
||
if (j >= committed.length) clearInterval(typeTimer);
|
||
}, 16);
|
||
}
|
||
finalText = ''; partialText = '';
|
||
}
|
||
|
||
micbar.addEventListener('pointerdown', (e) => { e.preventDefault(); start(); });
|
||
window.addEventListener('pointerup', stop);
|
||
micbar.addEventListener('pointercancel', stop);
|
||
window.addEventListener('keydown', (e) => {
|
||
if (e.code === 'Space' && !e.repeat && e.target === document.body) { e.preventDefault(); start(); }
|
||
});
|
||
window.addEventListener('keyup', (e) => { if (e.code === 'Space') stop(); });
|
||
window.addEventListener('blur', stop);
|
||
|
||
/* ── 滚动浮现 ── */
|
||
const io = new IntersectionObserver((entries) => {
|
||
entries.forEach((en) => { if (en.isIntersecting) { en.target.classList.add('in'); io.unobserve(en.target); } });
|
||
}, { threshold: 0.15 });
|
||
document.querySelectorAll('.reveal').forEach((el) => io.observe(el));
|
||
</script>
|
||
</body>
|
||
</html>
|