feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library: - Connects to IMAP server (IMAPFlow, TLS on port 993) - Fetches recent messages (configurable limit, default 20) - Extracts: from, fromEmail, subject, date, snippet (200 chars) - Thread-to-contact matching (PRD §6.1): 1. Match sender email against persons table 2. If no person match, extract domain and match against company.website 3. Returns matchedCompany + matchedPerson for each email - Audit-logged (action: crm.mailsync:imap) - Zod validated (host, port, user, password, folder, limit) - 502 on IMAP connection failure with detail - 400 on validation failure Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
This commit is contained in:
1 parent
7acc0c5451
commit
e45b68bb8b
1089 files changed
+260614
No files matched your search
+293
@@ -0,0 +1,293 @@
|
||||
---
|
||||
name: ascii-cosmos
|
||||
description: Generate a single-file product landing page with a layered ASCII cosmos hero (rotating ASCII planet, drifting irregular asteroids, comet trails, nebula gas clouds — each in its own harmonious color) plus full marketing sections (about / features grid / showcase / spec / pricing / CTA / footer). Takes a natural-language description of one product (any kind — a note app, a coffee subscription, a CLI tool, an indie hardware, a hosting service…) and outputs ONE working .html file with deep-space dark or warm-letterpress theme. Runs in BOTH Claude Code (writes to ~/Desktop) and GLM / online chat (outputs HTML inline). Trigger phrases: "做个宇宙风产品落地页", "生成 ASCII 产品官网", "ascii-cosmos-gen ...", "做个独立软件 / 独立硬件落地页", or when user drags this skill folder into chat asking for an atmospheric product landing page.
|
||||
visibility: public
|
||||
mode: template
|
||||
carrier: web-page
|
||||
scenario: marketing-landing
|
||||
pattern_source: reference.html
|
||||
source_priority: skill-first
|
||||
triggers:
|
||||
- "做个宇宙风产品落地页"
|
||||
- "生成 ASCII 产品官网"
|
||||
- "做个独立软件落地页"
|
||||
- "做个独立硬件落地页"
|
||||
- "ascii 落地页"
|
||||
- "ascii hero"
|
||||
legacy_gen: true
|
||||
---
|
||||
|
||||
# ascii-cosmos — ASCII 宇宙调性产品落地页生成器
|
||||
|
||||
## 作品长什么样
|
||||
|
||||
单文件 HTML 产品官网,hero 是动态分层 ASCII 宇宙:
|
||||
|
||||
- **星云气体云**(紫,柔和散点)
|
||||
- **闪烁星点**(冷白,带 ★ ✦ 装饰)
|
||||
- **旋转 ASCII 行星**(青,带 3 圈卫星轨道)
|
||||
- **漂浮不规则小行星**(琥珀金,6 种带陨石坑的多行 ASCII 形状,自旋 + 缓慢漂移)
|
||||
- **彗星拖尾**(粉,从屏幕掠过)
|
||||
|
||||
下面是常规 marketing 区块:关于 / 核心功能 6 卡片网格 / 界面预览 2 块 / 技术规格 / 订阅方案 3 档 / CTA / 页脚。所有强调字段都有"hover 时打乱-解码"的字符滚动动效(`.pt` 类)。
|
||||
|
||||
适合:独立软件、笔记/写作工具、本地优先 AI、CLI、独立硬件、订阅服务、SaaS、电子书、播客、任何"安静 / 克制 / 有手作味"的产品。
|
||||
|
||||
## 何时触发
|
||||
|
||||
用户说:
|
||||
- "做个宇宙风产品落地页"
|
||||
- "做个 ASCII 风格的产品官网"
|
||||
- "生成一个像墨思那样的落地页 — 我的产品是 ..."
|
||||
- "ascii-cosmos-gen ..."
|
||||
- 把本 skill 文件夹拖进对话框并描述一个产品
|
||||
|
||||
提供:
|
||||
- **产品名字 + 一句话定位**(必需)
|
||||
- **核心卖点 / 功能清单**(推荐 — 给 6 张卡片用)
|
||||
- **定价档位**(可选 — 没说就编 3 档:免费 / 专业 / 团队)
|
||||
- **风格**(可选 — 深空青 / 暖色羊皮纸 / 赛博紫 / 终端绿 …,照抄 `styles.md` 预设名或自由描述。不写就用默认深空青)
|
||||
|
||||
如果用户**只给产品名一句话**:自己围绕这个产品把 6 个功能、3 档价、技术规格全部脑补出来。语气要克制、有手作味,不要 marketing 鸡血。
|
||||
|
||||
## 两种运行模式
|
||||
|
||||
### 模式 A · Claude Code / 本地(有文件系统)
|
||||
- 输出位置:`~/Desktop/{slug}.html`(**单文件**,所有图片走 unsplash URL 或用户给的链接)
|
||||
- slug 用产品英文名小写连字符
|
||||
- 一句话报告:路径 + 用了什么风格 + 多少卡片
|
||||
|
||||
### 模式 B · GLM / 在线对话(无文件系统)
|
||||
- 直接把完整 HTML 包在 ` ```html ` 代码块里输出
|
||||
- 图片全部用 `https://images.unsplash.com/photo-XXX?...` 或 `https://picsum.photos/seed/{slug}-NN/W/H` 占位
|
||||
- 不要尝试访问本地文件、不要建议命令行操作
|
||||
- 末尾一行说明:保存为 .html 双击打开即可
|
||||
|
||||
判断:如果你**无法 Read 看到本地图片**或工作目录不是用户家目录 → 默认走 B。
|
||||
|
||||
## 工作步骤
|
||||
|
||||
### 1. 读模板
|
||||
读取本 skill 同目录下的 `template.html`。这是完整骨架,包含:
|
||||
- 全部 CSS(深空主题色 + 5 个 ASCII 层 + 卡片 / 价格 / 规格 / hero 网点字 …)
|
||||
- 全部 JS(5 层 ASCII 渲染、信息区轨道、字符滚动、标签筛选、平滑滚动、实时时钟)
|
||||
- 12 处 `<!-- SWAP-N -->` 注释标记需要替换的位置
|
||||
|
||||
**JS / CSS 不要改**(除了主题色变量与字体可以按 styles 改)。
|
||||
|
||||
### 2. 读风格预设
|
||||
看 `styles.md`:5 种预设。如果用户的风格描述匹配某个名字,照那份的 CSS 变量替换 `:root`。否则按用户描述自己设计 6 个变量(`--bg / --fg / --dim / --accent / --accent2 / --accent3 / --line / --paper`)+ 5 个 ASCII 层颜色(`#ascii-nebula / -stars / -planet / -asteroids / -comets`)。
|
||||
|
||||
> 配色铁律:5 个 ASCII 层颜色必须**互相和谐 + 都能在背景上读出来**。深色背景 → 高饱和发光色;浅色背景 → 中低明度有重量的色。
|
||||
|
||||
### 3. 解析用户输入抽信息
|
||||
|
||||
抽:
|
||||
- **PRODUCT_EN** — 英文 logo 名,全大写 ASCII 字(用于 hero 大字 ASCII title)
|
||||
- **PRODUCT_CN** — 1-2 个中文字(hero 副标题大字,会渲染为网点字)
|
||||
- **TAGLINE** — 一句话副标语(30-50 字)
|
||||
- **VERSION / TYPE / PLATFORM** — 3 个 meta 标签
|
||||
- **ABOUT_PARAGRAPH** — 80-150 字关于段落,3-5 个 `<span class="pt">` 强调短语
|
||||
- **3 HIGHLIGHT** — 3 个 cell(标题 + 一句话描述)
|
||||
- **6 FEATURES** — 6 张卡片:图片 URL + ASCII overlay(5-6 行小图) + 标题 + 副标题 + 1-2 个标签 + 分类(ai/write/graph/sync 任选或自定义)
|
||||
- **2 SHOWCASE** — 2 段界面介绍(截图 URL + mini-ascii 装饰 + 标题带 `<em>` 强调 + 2 段 60-100 字描述)
|
||||
- **10 SPEC ROWS** — 10 行键值对(版本 / 平台 / 引擎 / 数据格式 / 同步协议 …)
|
||||
- **3 PRICING** — 3 档(名字 / 价格数字 / 价格单位 / 描述 / 5-6 条特性 / CTA 按钮文案)。**中间一档加 `featured` 类**
|
||||
- **CTA_BIG** — 大字号 CTA 文案(带 `<em>` 强调)+ 副文案 + 3 个按钮 + 6-7 个平台标签
|
||||
|
||||
### 4. 替换 12 处 SWAP
|
||||
|
||||
| SWAP | 位置 | 内容 |
|
||||
|---|---|---|
|
||||
| 1 | `<title>` | `{PRODUCT_CN}{PRODUCT_EN} — {TAGLINE}` |
|
||||
| 2 | header logo + 5 项 nav | logo 文字 + 导航锚点(默认:功能 / 界面 / 规格 / 订阅 / 关于) |
|
||||
| 3 | hero ASCII title | `PRODUCT_EN` 的 ANSI Shadow 风 ASCII(用 https://patorjk.com/software/taag 风格,5 行高) |
|
||||
| 4 | hero 中文大字 + tagline + 3 meta | `PRODUCT_CN` + `TAGLINE` + 3 个 meta span |
|
||||
| 5 | intro 段落 + 3 cells | `ABOUT_PARAGRAPH`(含 `<span class="pt">` 强调)+ 3 个 `<div class="cell">` |
|
||||
| 6 | 6 张 features 卡片 | 每张:`data-cat` + `<img src>` + ASCII overlay + 标题 + small + tags |
|
||||
| 7 | 4 个 filter tag | 分类 chip(all + 你定的 3-4 个分类,匹配卡片的 data-cat) |
|
||||
| 8 | 2 块 showcase | 每块:截图 + mini-ascii + h3(含 em) + 2 段 p(含 pt 强调) |
|
||||
| 9 | 10 行 spec table | 每行:`<b>` 字段名 + `<span class="pt">` 值 |
|
||||
| 10 | 3 档 pricing | name / num / desc / ul li / btn |
|
||||
| 11 | info 区 CTA | info-big 大字(含 em)+ hero-sub 副文案 + 3 个按钮 + platforms |
|
||||
| 12 | footer | 版权 + 一句署名 |
|
||||
|
||||
**强制保留**(不能动):
|
||||
- 全部 `<script>` 块(5 层 ASCII 渲染 / 信息区轨道 / scramble / 标签筛选 / 平滑滚动 / 时钟)
|
||||
- `.pt` 类的字符滚动机制(要在用户文案里手动撒 `<span class="pt" data-text="...">...</span>`)
|
||||
- 5 个 ascii-layer 的 `<pre id="ascii-...">` 节点
|
||||
- `<pre id="info-ascii">` 节点
|
||||
- 6 张卡片的 data-cat 和筛选逻辑
|
||||
- featured 价格档的样式
|
||||
|
||||
### 5. 写 ASCII 大字标题(SWAP-3)
|
||||
|
||||
用 ANSI Shadow / ANSI Regular 字体的 5 行 ASCII 文字。每个字母用 ╗╔═║║║╝╚═╝ 这种线条字符。例如 INKWEAVE 是:
|
||||
```
|
||||
██╗███╗ ██╗██╗ ██╗██╗ ██╗███████╗ █████╗ ██╗ ██╗███████╗
|
||||
██║████╗ ██║██║ ██╔╝██║ ██║██╔════╝██╔══██╗██║ ██║██╔════╝
|
||||
██║██╔██╗ ██║█████╔╝ ██║ █╗ ██║█████╗ ███████║██║ ██║█████╗
|
||||
██║██║╚██╗██║██╔═██╗ ██║███╗██║██╔══╝ ██╔══██║╚██╗ ██╔╝██╔══╝
|
||||
██║██║ ╚██╗██║██║ ██╗╚███╔███╔╝███████╗██║ ██║ ╚████╔╝ ███████╗
|
||||
╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝
|
||||
```
|
||||
|
||||
**如果产品英文名超过 8 字母** → 缩到 8 字母内的简称(如 `INTERSTELLAR` → `STELLAR`),或者用更窄的 ANSI 字体(更细的 `▌▐█`)。
|
||||
|
||||
**如果你不确定字模长什么样** → 至少给个看得过去的 5 行 ASCII,不要留空。
|
||||
|
||||
### 6. 写卡片 ASCII overlay(SWAP-6 内嵌)
|
||||
|
||||
每张卡片右下盖一段 4-5 行的小 ASCII 装饰,对应这张卡片的功能。例如:
|
||||
- "智能续写" → `╭─────╮ │ ... │ ╰──→──╯`
|
||||
- "知识图谱" → `●─●─● │\\ │/│ ●─●`
|
||||
- "搜索" → `┌─────┐ │ ⌕ 找到 │ │ 3 处 │ └─────┘`
|
||||
- "同步" → `▣ ⇄ ▢ ╲ ╱ ⇅ ╱ ╲ ▤ ⇄ ▥`
|
||||
- "时间线" → `←─◆─◆─◆─→ ░░▒▒▓▓██`
|
||||
|
||||
不要超过 5 行,不要超过 14 字符宽。
|
||||
|
||||
### 7. 撒 `<span class="pt" data-text="...">` 强调
|
||||
|
||||
模板里所有需要"hover 时字符乱码再解码"的强调短语都用:
|
||||
```html
|
||||
<span class="pt" data-text="原文">原文</span>
|
||||
```
|
||||
- intro 段落里撒 3-5 处
|
||||
- showcase p 里每段撒 1-2 处
|
||||
- spec 每行的值都包一层 `<span class="pt">`
|
||||
- pricing btn 文字也加 `pt`
|
||||
- 标题里的色字用 `<span class="em">...</span>`(不是 `pt`)
|
||||
|
||||
### 8. 风格变体(可选)
|
||||
如果用户指定了风格,照 `styles.md` 改:
|
||||
- `:root` 8 个变量
|
||||
- `.hero` background 渐变
|
||||
- 5 个 `#ascii-{layer}` 颜色
|
||||
- 图片滤镜(深色用 `hue-rotate(200deg) brightness(.7)`,暖色用 `sepia(.3) saturate(.85)`)
|
||||
|
||||
> **没有指定风格时**:根据产品的"调性词"自动选色 — 见下面「调性 → 配色映射」。
|
||||
|
||||
### 8.5 替换 ASCII 场景 motif(推荐!)
|
||||
|
||||
模板的 hero 默认是宇宙(行星 + 小行星 + 彗星 + 星云)。**这个 motif 应该跟着产品调性换** — 一个咖啡订阅不该飘小行星,一个森林漫步 app 不该有彗星。
|
||||
|
||||
改法**只改一个对象**:找到 `<script>` 里的 `// SWAP-13 · ASCII 场景资产` 标记,整体替换里面的 `SCENE = { ... }`。其它 JS 不要动。
|
||||
|
||||
`SCENE` 11 个键各自的角色:
|
||||
|
||||
| 键 | 默认(宇宙) | 角色 / 视觉职责 |
|
||||
|---|---|---|
|
||||
| `floaters` | 6 个不规则小行星 ASCII 形状(多行数组) | 屏幕里漂浮 + 自旋的"主物体" |
|
||||
| `centerShade` | `" .,:;-~+*=%#@█"` | 中央旋转球体的明暗梯度(暗→亮 14 字符) |
|
||||
| `diffuseChars` | `"·∙∶⋮⋯░▒"` | 弥漫底层的"气体云"密度梯度(淡→浓) |
|
||||
| `sparkRare` | `"★"` | 稀有大亮点 (4% 概率) |
|
||||
| `sparkBright` | `"✦"` | 中频亮点 (15%) |
|
||||
| `sparkDim` | `".∙"` | 普通暗点 |
|
||||
| `streakHead` | `"☄"` | 飞掠物的"头"(最亮的一格) |
|
||||
| `streakTailNear/Mid/Far/End` | `"*","∙","·","."` | 拖尾从近到远 4 段渐隐 |
|
||||
| `centerSats` | `"◉◎○"` | 围绕中央球体的 3 颗"卫星",由内向外 |
|
||||
| `orbitDots` | `".∙•"` | 3 圈轨道线的点字符,由内向外 |
|
||||
|
||||
#### Motif 库(按产品类型选)
|
||||
|
||||
| 产品类型 | floaters 主体 | centerShade | diffuseChars | sparks | streakHead/tail | centerSats | 视觉故事 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **咖啡 / 茶 / 饮品** | 咖啡豆 / 茶叶 形状 | `" .'-=+oO0@#"` | `"·~,'\""` (蒸汽) | `★ ✧ . ∙` | `"☕"` 头 + `"*∙·."` 拖尾 | `"●◐○"` (三种烘焙) | 一杯咖啡 + 飘豆 + 蒸汽 + 偶尔一颗豆掉过 |
|
||||
| **森林 / 自然 / 户外** | 树叶 / 松果 形状 | `" .,;:!|tT#"` (年轮) | `"~^,'\""` (雾) | `"⁂"` 萤火 + `"·"` 露珠 | `"𓅯"` 鸟 + `"~~~~"` 飞行轨迹 | `"❀✿✾"` (花) | 一棵树 + 落叶 + 雾 + 萤火虫 + 偶尔飞鸟 |
|
||||
| **音乐 / 播客 / 音频** | 音符 / 唱片碎片 | `" .─=≡▪▫■□"` | `"·∶⋮⁞"` (噪点) | `"♪♫"` + `"·"` | `"♬"` 头 + `"~∼≈"` 波 | `"◉◎○"` (黑胶圈) | 一张唱片 + 飘音符 + 频谱噪点 + 节拍 |
|
||||
| **代码 / CLI / 开发者** | `{}` `[]` `()` `<>` 包裹的小 ASCII 块 | `" 01[](){}<>#"` | `"·.,:|"` | `"★ ✦ . _"` | `"_"` 光标 + `" "` (闪现型) | `"◉⊞⊟"` 模块 | 一个调用图 + 飞过的 commit + 闪烁光标 + 矩阵雨 |
|
||||
| **手作 / 工艺 / 极物** | 锤 / 刨花 / 钉 / 锈点 | `" .·°*+x#X@"` | `"·:.,"` (尘) | `"✦ ▪ ▫"` | `"→"` 头 + `"-—"` 切线 | `"◉○⊙"` (齿轮) | 一个工作台 + 飘刨花 + 木屑 + 偶尔一记 |
|
||||
| **冥想 / 健康 / 慢生活** | 涟漪 / 圆 / 波浪 形状 | `" ··∘○●"` | `"~∼≈"` (呼吸波) | `". ∙ °"` | `"◌"` 头 + `"·· ··"` 散点 | `"◯◌○"` (空圆) | 一个呼吸圆 + 涟漪 + 暖色尘埃 + 缓慢 |
|
||||
| **极客硬件 / 电子** | 芯片 / 电路块 (`├┤├ ┐└┘`) | `" .·•◊◆■"` | `"⋅¨"` (电气噪) | `"⚡"` 偶现 + `"·"` | `"►"` 头 + `"==="` 数据流 | `"⊙⊚⊛"` (LED) | PCB + 漂浮电容 + 数据流飞过 |
|
||||
| **写作 / 笔记 / 思考** | `"…"` `"——"` `"§"` 标点形状 | `" .,;:!?¶§"` | `"··¨"` | `". ∶ ⋮"` | `"✒"` 头 + `"~~~"` 笔迹 | `"◉◯●"` (墨点) | 一支笔 + 飘标点 + 笔迹划过 + 思绪点 |
|
||||
| **意式 / 复古 / 80s** | 像素方块 / 撞色几何 | `" .░▒▓█"` | `"·:|"` | `"◆ ◇ ▰"` | `"◉"` 头 + `"━━"` 实线 | `"■□▣"` | 一个 CRT + 像素粒子 + 扫描线 |
|
||||
| **海洋 / 旅行 / 远方** | 鱼 / 帆 / 船 / 浪 | `" .~≈≋"` 水波 | `"~∼≈≋"` (波纹) | `"·°"` (气泡) | `"⛵"` 头 + `"~~~"` 尾流 | `"◐◑◯"` (月相) | 一艘船 + 浪 + 气泡 + 海鸟划过 |
|
||||
|
||||
#### 怎么写 floaters 形状
|
||||
|
||||
每个形状是 3-4 行的多行 ASCII,宽 8-12 字符。一定要:
|
||||
- 形状轮廓闭合(比如树叶 `,/^\,` `( )` `'\_/'` )
|
||||
- 多个变体不要长得一模一样,至少 3-6 个不同的
|
||||
- 单字符宽度,不要用全角
|
||||
- 留 1 格 padding 让旋转抖动好看
|
||||
|
||||
**树叶示例**:
|
||||
```js
|
||||
floaters: [
|
||||
[" ,-^-, ",
|
||||
"| |",
|
||||
" '---' "],
|
||||
[" /\\\\ ",
|
||||
" / \\\\ ",
|
||||
" \\__/ "],
|
||||
[" *_* ",
|
||||
" /...\\\\ ",
|
||||
" '___' "]
|
||||
]
|
||||
```
|
||||
|
||||
**音符示例**:
|
||||
```js
|
||||
floaters: [
|
||||
[" ♪ ",
|
||||
" /| ",
|
||||
"♪ |____♪"],
|
||||
[" ♫ ",
|
||||
" /\\\\ ",
|
||||
"♪__/ \\\\"],
|
||||
[" ♩ ",
|
||||
" | ",
|
||||
" o "]
|
||||
]
|
||||
```
|
||||
|
||||
**写完检查**:把 SCENE 直接贴进控制台,跑一下 `JSON.stringify(SCENE)` 不报错就 OK。
|
||||
|
||||
#### 自由发挥的判定
|
||||
|
||||
如果用户的产品**不在上表 10 类**里 → 自己设计 motif,遵守原则:
|
||||
1. **floaters** 选 3-6 个跟产品有具象关联的小图形("它会让人想到这个产品")
|
||||
2. **centerShade** 14 字符明暗梯度,暗的字符(` .,`)开头,亮的(`@#█`)结尾,中间渐变
|
||||
3. **diffuseChars** 5-8 个"半透明"字符(点 / 顿点 / 小方块 / 波浪),表现氛围底层
|
||||
4. **streak** 整套要"前重后轻"渐隐
|
||||
5. **sat / orbitDots** 各 3 个,表现轻重三级
|
||||
|
||||
> 一句话原则:**让人不戴眼镜、3 米外瞄一眼,能猜出这是个什么产品的网站。**
|
||||
|
||||
### 9. 调性 → 配色映射(用户没指定风格时按这个选)
|
||||
|
||||
| 用户描述里的关键词 | 推荐预设 | 主色 |
|
||||
|---|---|---|
|
||||
| 安静 / 克制 / 思考 / 写作 / 笔记 / 本地优先 | **深空青**(默认) | `#8be9ff` 青 |
|
||||
| 温暖 / 手作 / 中式 / 文艺 / 慢 / 茶 / 复古 | **暖色羊皮纸** | `#660125` 酒红 |
|
||||
| 高能 / 未来 / AI / Web3 / 游戏 / 极客 | **赛博紫** | `#ff00aa` 霓虹品红 |
|
||||
| CLI / 终端 / 安全 / 复古 80s / phosphor | **终端绿** | `#00ff66` |
|
||||
| 自然 / 健康 / 冥想 / 旅行 / 北欧 | **极光** | `#7fe3c4` 极光绿 |
|
||||
| 咖啡 / 烘焙 / 工艺 / 食品 / 沙漠 / 大地 | **沙漠日落** | `#ff8b3d` 烤橙 |
|
||||
| 海洋 / 远方 / 蓝色 / 冷静 / 数据 | 自由设计 | `#4fc3f7` 海洋蓝 |
|
||||
| 黑金 / 奢侈 / 精确 / 钟表 / 高端硬件 | 自由设计 | `#d4af37` 香槟金 + 黑底 |
|
||||
|
||||
判定流程:
|
||||
1. **用户明确说了风格名** → 照 `styles.md` 改
|
||||
2. **用户没说,但产品有明显调性** → 按上表选最贴的预设
|
||||
3. **完全开放** → 默认深空青
|
||||
|
||||
### 10. 报告
|
||||
|
||||
**模式 A**:一句话。
|
||||
> 已生成 ~/Desktop/{slug}.html — {产品名}产品落地页,6 个功能卡片 + 3 档价格,深空青风格。双击打开。
|
||||
|
||||
**模式 B**:完整 HTML 代码块 + 一行说明。
|
||||
> 保存为 `{slug}.html` 双击打开。要换图就把图片 URL 换成你自己的。要改 ASCII 主标题去顶部的 `<pre class="ascii-title lg">` 替换字模。
|
||||
|
||||
## 易错速查
|
||||
|
||||
- **5 个 ASCII 层颜色互相打架**:测试一下深色背景下 5 色都要清晰可读。粉太亮、紫太暗都会塌
|
||||
- **ASCII 大字模板对不上**:5 行 ANSI Shadow 字符高度必须严格 5-6 行,不要 3 行
|
||||
- **`<pre>` 里换行用真换行**,不要 `\n` 字符串字面量
|
||||
- **卡片 data-cat 和 filter 不匹配** → 筛选失效。一致性检查
|
||||
- **pricing 中间档忘加 `featured`** → 视觉重心丢
|
||||
- **JSON / 对象里中文引号**:ASCII overlay 里如果用引号一律用全角 `""`
|
||||
- **删了 `<pre id="ascii-comets">` 之类的层** → JS 报错。5 层节点必须全部保留
|
||||
- **没给图片 URL** → 用 unsplash 的横图(`?auto=format&fit=crop&w=900&q=70`)或 picsum
|
||||
+958
@@ -0,0 +1,958 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>墨思 — 智能思维编织工作台</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#05060f;
|
||||
--fg:#e6e8ff;
|
||||
--dim:#7a7fa8;
|
||||
--accent:#8be9ff;
|
||||
--accent2:#ff6ad5;
|
||||
--accent3:#c792ea;
|
||||
--line:#1a1d3a;
|
||||
--hl:#0d1024;
|
||||
--paper:#0a0c1f;
|
||||
}
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
html,body{background:var(--bg);color:var(--fg);font-family:"Songti SC","STSong","Source Han Serif SC","Noto Serif CJK SC","SimSun",serif;font-size:14px;line-height:1.8;-webkit-font-smoothing:antialiased}
|
||||
/* 中文段落均使用宋体 */
|
||||
:lang(zh),.intro-text,.showcase-text p,.showcase-text h3,.info-big,.hero-title,.card-title,.price .desc,.cell{font-family:"Songti SC","STSong","Source Han Serif SC","Noto Serif CJK SC","SimSun",serif}
|
||||
/* 数字 / 拉丁字母仍保持等宽,但中文遇到等宽会 fallback 回宋体 */
|
||||
.header-logo,.nav a,.hero-meta,.hero-sub,.sec-head,.scroll-cue,.tag,.card-num,.card-tags,.spec-row b,.price h4,.price .num,.platforms,.footer,.here{font-family:"SF Mono","Menlo","Consolas","Songti SC","STSong","SimSun",serif}
|
||||
a{color:inherit;text-decoration:none;cursor:pointer}
|
||||
::selection{background:var(--accent);color:var(--bg)}
|
||||
|
||||
/* HEADER */
|
||||
.header{position:fixed;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:center;padding:18px 28px;z-index:100;color:var(--fg);background:rgba(5,6,15,.85);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||||
.header-logo{font-weight:600;letter-spacing:0.18em;font-size:13px}
|
||||
.header-logo span{color:var(--accent)}
|
||||
.nav ul{display:flex;gap:26px;list-style:none}
|
||||
.nav a{font-size:12px;letter-spacing:0.2em;position:relative;padding:4px 0;transition:color .3s}
|
||||
.nav a:hover{color:var(--accent)}
|
||||
.nav a::after{content:"";position:absolute;left:0;bottom:0;height:1px;width:0;background:var(--accent);transition:width .3s}
|
||||
.nav a:hover::after{width:100%}
|
||||
|
||||
/* HEY */
|
||||
.here{position:fixed;right:32px;bottom:28px;font-size:12px;letter-spacing:0.2em;z-index:90;text-align:right;color:var(--dim)}
|
||||
.here .blink{display:inline-block;animation:blink 1.2s infinite}
|
||||
@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}
|
||||
|
||||
/* HERO ASCII */
|
||||
.hero{height:100vh;width:100%;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;border-bottom:1px solid var(--line);background:radial-gradient(ellipse at 30% 20%, #1a0a2e 0%, #05060f 55%, #000 100%)}
|
||||
.ascii-layer{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:12px;line-height:12px;letter-spacing:0;white-space:pre;font-family:"SF Mono",monospace;pointer-events:none;font-weight:600;margin:0}
|
||||
#ascii-nebula{color:#a87bd6;opacity:.55;text-shadow:0 0 8px rgba(168,123,214,.45);mix-blend-mode:screen}
|
||||
#ascii-stars{color:#cfe6ff;opacity:.85;text-shadow:0 0 4px rgba(207,230,255,.5)}
|
||||
#ascii-planet{color:var(--accent);opacity:.95;text-shadow:0 0 6px rgba(139,233,255,.45)}
|
||||
#ascii-asteroids{color:#f5b971;opacity:.92;text-shadow:0 0 6px rgba(245,185,113,.45)}
|
||||
#ascii-comets{color:#ff8fd2;opacity:.95;text-shadow:0 0 8px rgba(255,143,210,.55)}
|
||||
.hero-overlay{position:relative;z-index:2;text-align:center;padding:0 24px;color:var(--fg)}
|
||||
.hero-title{font-size:clamp(40px,7vw,98px);font-weight:300;letter-spacing:0.04em;line-height:1;margin-bottom:18px}
|
||||
.hero-title .em{font-style:italic;color:var(--accent)}
|
||||
.hero-sub{font-size:14px;letter-spacing:0.3em;color:var(--dim);margin-bottom:36px}
|
||||
|
||||
/* ASCII 大标题 */
|
||||
.ascii-title{font-family:"SF Mono","Menlo","Consolas",monospace;color:var(--accent);white-space:pre;display:inline-block;text-align:left;line-height:1;font-weight:700;margin:0 auto;letter-spacing:0;text-shadow:0 0 12px rgba(139,233,255,.5),0 0 2px rgba(199,146,234,.4)}
|
||||
.ascii-title.lg{font-size:clamp(8px,1.25vw,14px);line-height:1.05}
|
||||
.ascii-title.md{font-size:clamp(6px,.85vw,10px);color:var(--fg)}
|
||||
.ascii-title.sm{font-size:clamp(5px,.55vw,7px);color:var(--accent);opacity:.85}
|
||||
.brand-wrap-outer{text-align:center}
|
||||
.brand-cn-sub{font-family:"Songti SC","STSong","SimSun",serif;font-size:clamp(28px,3.6vw,46px);font-weight:400;letter-spacing:0.5em;color:var(--fg);margin:24px 0 0;padding-left:0.5em}
|
||||
.ascii-cn{display:none}
|
||||
.sec-ascii{display:block;color:var(--accent);white-space:pre;font-family:"SF Mono",monospace;font-size:14px;line-height:1;margin-bottom:10px;opacity:.85;letter-spacing:0.4em}
|
||||
|
||||
/* 中文真字宋体大标题 + ASCII 网点背景 */
|
||||
.brand-block{text-align:center;line-height:1;display:inline-block;padding:36px 64px;position:relative}
|
||||
.brand-block::before{
|
||||
content:"";position:absolute;inset:0;
|
||||
background-image:radial-gradient(circle, rgba(139,233,255,.22) 1px, transparent 1.4px);
|
||||
background-size:7px 7px;
|
||||
pointer-events:none;
|
||||
opacity:.85;
|
||||
z-index:0;
|
||||
/* 中间淡四角浓的渐隐遮罩,像深空尘埃 */
|
||||
-webkit-mask-image:radial-gradient(ellipse at center, transparent 30%, #000 90%);
|
||||
mask-image:radial-gradient(ellipse at center, transparent 30%, #000 90%);
|
||||
}
|
||||
.brand-deco-top,.brand-deco-bot{font-family:"SF Mono","Menlo",monospace;color:var(--accent);font-size:clamp(12px,1.4vw,18px);letter-spacing:0.6em;opacity:.85;margin:0 0 18px;position:relative;z-index:1}
|
||||
.brand-deco-bot{margin:18px 0 0}
|
||||
.brand-title-cn{
|
||||
font-family:"Songti SC","STSong","Source Han Serif SC","SimSun",serif;
|
||||
font-weight:700;
|
||||
font-size:clamp(140px,20vw,240px);
|
||||
line-height:1;
|
||||
letter-spacing:0.32em;
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
z-index:1;
|
||||
|
||||
/* 字内填充:星云网点,模拟字符画/星尘 */
|
||||
background-image:
|
||||
radial-gradient(circle, var(--accent) 0.95px, transparent 1.3px),
|
||||
radial-gradient(circle, var(--accent2) 0.45px, transparent 0.85px);
|
||||
background-size: 5px 5px, 3px 3px;
|
||||
background-position: 0 0, 1.5px 1.5px;
|
||||
-webkit-background-clip:text;
|
||||
background-clip:text;
|
||||
color:transparent;
|
||||
-webkit-text-fill-color:transparent;
|
||||
|
||||
/* 极细描边 */
|
||||
-webkit-text-stroke:0.4px rgba(139,233,255,.55);
|
||||
text-shadow:0 0 8px rgba(139,233,255,.3);
|
||||
}
|
||||
.brand-title-cn .gap{display:inline-block;width:.2em}
|
||||
.brand-wrap-outer{text-align:center}
|
||||
.sec-head h2{font-size:18px;font-weight:400;letter-spacing:0.4em;color:var(--fg);display:flex;flex-direction:column;gap:6px;font-family:"Songti SC","STSong","SimSun",serif}
|
||||
.hero-meta{display:flex;gap:32px;justify-content:center;font-size:11px;letter-spacing:0.16em;color:var(--dim);flex-wrap:wrap}
|
||||
.hero-meta span b{color:var(--fg);font-weight:400}
|
||||
|
||||
.scroll-cue{position:absolute;left:50%;bottom:32px;transform:translateX(-50%);font-size:11px;letter-spacing:0.3em;color:var(--dim);z-index:3;animation:bob 2.4s ease-in-out infinite}
|
||||
@keyframes bob{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,8px)}}
|
||||
|
||||
/* SECTION */
|
||||
.sec{padding:96px 48px;border-bottom:1px solid var(--line);position:relative}
|
||||
.sec-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:48px;font-size:11px;letter-spacing:0.24em;color:var(--dim);border-bottom:1px solid var(--line);padding-bottom:18px}
|
||||
.sec-head .idx{font-family:monospace}
|
||||
|
||||
/* INTRO */
|
||||
.intro{max-width:980px;margin:0 auto}
|
||||
.intro-text{font-size:clamp(20px,2.4vw,30px);line-height:1.55;font-weight:300;letter-spacing:0.02em}
|
||||
.intro-text .pt{display:inline;cursor:default;transition:color .2s}
|
||||
.intro-text .pt:hover{color:var(--accent)}
|
||||
.intro-text .pt.scrambling{color:var(--accent)}
|
||||
.intro-text .em{color:var(--accent);font-style:italic}
|
||||
.intro-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:32px;margin-top:64px;padding-top:48px;border-top:1px dashed var(--line)}
|
||||
.intro-grid .cell{font-size:12px;color:var(--dim);line-height:1.7}
|
||||
.intro-grid .cell b{display:block;color:var(--fg);font-weight:400;margin-bottom:8px;letter-spacing:0.16em;font-size:11px}
|
||||
|
||||
/* TAGS */
|
||||
.filter{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:36px}
|
||||
.tag{padding:6px 14px;border:1px solid var(--line);font-size:11px;letter-spacing:0.16em;color:var(--dim);transition:all .25s;cursor:pointer;user-select:none}
|
||||
.tag:hover{color:var(--fg);border-color:var(--dim)}
|
||||
.tag.active{background:var(--accent);color:var(--bg);border-color:var(--accent)}
|
||||
.tag .dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:currentColor;margin-right:8px;vertical-align:middle;opacity:.6}
|
||||
|
||||
/* GRID */
|
||||
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
|
||||
.card{border:1px solid var(--line);background:var(--paper);transition:all .3s;position:relative;overflow:hidden;display:flex;flex-direction:column}
|
||||
.card:hover{border-color:var(--accent);transform:translateY(-4px);box-shadow:0 12px 32px -16px rgba(139,233,255,.5)}
|
||||
.card-img{aspect-ratio:4/3;background:#0d1024;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}
|
||||
.card-img img{width:100%;height:100%;object-fit:cover;transition:transform .8s ease,filter .4s;filter:hue-rotate(200deg) saturate(.9) contrast(1.1) brightness(.6)}
|
||||
.card:hover .card-img img{transform:scale(1.06);filter:hue-rotate(220deg) saturate(1.1) contrast(1.05) brightness(.75)}
|
||||
.card-img .ascii-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:9px;line-height:9px;color:var(--accent);white-space:pre;opacity:.75;pointer-events:none;mix-blend-mode:screen;text-shadow:0 0 4px rgba(139,233,255,.6)}
|
||||
.card-meta{padding:18px 20px;display:flex;justify-content:space-between;align-items:flex-start;gap:14px}
|
||||
.card-title{font-size:14px;letter-spacing:0.04em;font-weight:400}
|
||||
.card-title .pt{cursor:default}
|
||||
.card-title small{display:block;color:var(--dim);font-size:11px;margin-top:6px;letter-spacing:0.12em}
|
||||
.card-num{color:var(--dim);font-size:10px;font-family:monospace;letter-spacing:0.1em}
|
||||
.card-tags{display:flex;gap:6px;padding:0 20px 18px;flex-wrap:wrap}
|
||||
.card-tags span{font-size:10px;letter-spacing:0.16em;color:var(--dim);padding:2px 8px;border:1px solid var(--line);text-transform:uppercase}
|
||||
.card.hidden{display:none}
|
||||
|
||||
/* SHOWCASE / UI 截图 */
|
||||
.showcase{display:grid;grid-template-columns:1.2fr 1fr;gap:48px;align-items:center;margin-top:24px}
|
||||
.showcase-shot{border:1px solid var(--line);background:var(--paper);padding:14px;position:relative;box-shadow:0 8px 28px -16px rgba(139,233,255,.3)}
|
||||
.showcase-shot .bar{display:flex;gap:6px;margin-bottom:10px}
|
||||
.showcase-shot .bar i{display:block;width:10px;height:10px;border-radius:50%;background:#3a3f6a}
|
||||
.showcase-shot img{width:100%;height:auto;display:block;filter:hue-rotate(200deg) saturate(.85) contrast(1.05) brightness(.7);border:1px solid var(--line)}
|
||||
.showcase-text h3{font-size:clamp(24px,3vw,36px);font-weight:300;line-height:1.3;margin-bottom:18px;letter-spacing:0.02em;color:var(--fg)}
|
||||
.showcase-text h3 .em{color:var(--accent);font-style:italic}
|
||||
.showcase-text .mini-ascii{font-family:"SF Mono",monospace;color:var(--accent);font-size:10px;line-height:1;white-space:pre;display:block;margin-bottom:18px;opacity:.85}
|
||||
.showcase-text p{color:var(--dim);font-size:13px;line-height:1.8;margin-bottom:14px}
|
||||
.showcase-text p .pt:hover{color:var(--accent)}
|
||||
|
||||
/* SPEC */
|
||||
.spec-wrap{display:grid;grid-template-columns:1.3fr 1fr;gap:64px;align-items:start}
|
||||
.spec-visual{position:relative;aspect-ratio:4/5;background:var(--paper);border:1px solid var(--line);overflow:hidden}
|
||||
.spec-visual img{width:100%;height:100%;object-fit:cover;filter:hue-rotate(220deg) saturate(.9) contrast(1.05) brightness(.7)}
|
||||
.spec-visual::before{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(5,6,15,0) 50%,rgba(5,6,15,.85) 100%);z-index:2}
|
||||
.spec-visual .label{position:absolute;left:20px;bottom:18px;z-index:3;font-size:11px;letter-spacing:0.2em;color:var(--accent)}
|
||||
.spec-table{width:100%}
|
||||
.spec-row{display:grid;grid-template-columns:140px 1fr;padding:14px 0;border-bottom:1px dashed var(--line);font-size:12px;align-items:baseline}
|
||||
.spec-row b{color:var(--dim);font-weight:400;letter-spacing:0.16em;font-size:11px}
|
||||
.spec-row span{color:var(--fg)}
|
||||
.spec-row .pt{cursor:default}
|
||||
|
||||
/* PRICING */
|
||||
.price-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
|
||||
.price{border:1px solid var(--line);padding:32px 26px;background:var(--paper);transition:.3s;display:flex;flex-direction:column}
|
||||
.price:hover{border-color:var(--accent);box-shadow:0 12px 32px -16px rgba(139,233,255,.4)}
|
||||
.price.featured{border-color:var(--accent);background:#0f1233;border-width:1px;box-shadow:0 0 0 1px var(--accent) inset, 0 0 32px -8px rgba(139,233,255,.3)}
|
||||
.price h4{font-size:13px;letter-spacing:0.2em;color:var(--dim);margin-bottom:18px;font-weight:400}
|
||||
.price .num{font-size:42px;font-weight:300;letter-spacing:0.02em;margin-bottom:6px;color:var(--fg)}
|
||||
.price .num small{font-size:13px;color:var(--dim);letter-spacing:0.1em;margin-left:6px}
|
||||
.price .desc{color:var(--dim);font-size:12px;margin-bottom:24px;line-height:1.6}
|
||||
.price ul{list-style:none;font-size:12px;color:var(--fg);line-height:2;margin-bottom:28px;flex:1}
|
||||
.price ul li::before{content:"+ ";color:var(--accent);margin-right:4px}
|
||||
.price .btn{display:block;text-align:center;padding:12px;border:1px solid var(--fg);font-size:11px;letter-spacing:0.2em;transition:.25s;color:var(--fg)}
|
||||
.price .btn:hover{background:var(--fg);color:var(--bg);border-color:var(--fg)}
|
||||
.price.featured .btn{background:var(--accent);color:var(--bg);border-color:var(--accent)}
|
||||
.price.featured .btn:hover{background:var(--accent2);border-color:var(--accent2);color:var(--bg)}
|
||||
|
||||
/* INFO */
|
||||
.info{text-align:center;padding:120px 24px}
|
||||
.info-big{font-size:clamp(36px,6vw,76px);font-weight:300;line-height:1.1;letter-spacing:0.02em;margin-bottom:36px}
|
||||
.info-big .em{font-style:italic;color:var(--accent)}
|
||||
.info-links{display:flex;gap:28px;justify-content:center;flex-wrap:wrap;margin-top:28px;font-size:12px;letter-spacing:0.2em}
|
||||
.info-links a{padding:14px 28px;border:1px solid var(--fg);transition:all .25s;color:var(--fg)}
|
||||
.info-links a:hover{background:var(--accent);color:var(--bg);border-color:var(--accent)}
|
||||
.info-orbit{margin:0 auto 48px;width:280px;height:280px;position:relative;color:var(--dim);font-size:9px;line-height:9px;white-space:pre;font-family:monospace;display:flex;align-items:center;justify-content:center}
|
||||
#info-ascii{color:var(--accent);opacity:1;font-weight:700}
|
||||
|
||||
/* PLATFORMS */
|
||||
.platforms{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-top:24px;font-size:11px;letter-spacing:0.18em;color:var(--dim)}
|
||||
.platforms span{padding:6px 14px;border:1px solid var(--line)}
|
||||
|
||||
/* FOOTER */
|
||||
.footer{padding:24px 32px;display:flex;justify-content:space-between;font-size:10px;letter-spacing:0.18em;color:var(--dim);border-top:1px solid var(--line);flex-wrap:wrap;gap:12px;background:var(--paper)}
|
||||
|
||||
/* HERO 后景柔光遮罩,仅在中心给标题留出可读区,四周让 ASCII 充分显现 */
|
||||
.hero::after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:min(82vw,1100px);height:min(58vh,520px);background:radial-gradient(ellipse at center,rgba(5,6,15,.85) 0%,rgba(5,6,15,.6) 40%,rgba(5,6,15,0) 80%);pointer-events:none;z-index:1;border-radius:50%}
|
||||
|
||||
@media(max-width:900px){
|
||||
.grid{grid-template-columns:1fr 1fr}
|
||||
.intro-grid{grid-template-columns:1fr}
|
||||
.spec-wrap,.showcase{grid-template-columns:1fr;gap:32px}
|
||||
.price-grid{grid-template-columns:1fr}
|
||||
.sec{padding:64px 24px}
|
||||
.header{padding:14px 18px}
|
||||
}
|
||||
@media(max-width:560px){
|
||||
.grid{grid-template-columns:1fr}
|
||||
#ascii-stage{font-size:7px;line-height:7px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="header-logo"><a href="#top">墨思</a></div>
|
||||
<nav class="nav">
|
||||
<ul>
|
||||
<li><a href="#features">功能</a></li>
|
||||
<li><a href="#showcase">界面</a></li>
|
||||
<li><a href="#spec">规格</a></li>
|
||||
<li><a href="#price">订阅</a></li>
|
||||
<li><a href="#info">关于</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- HEY -->
|
||||
<div class="here">你好<br><span class="blink">▮</span> 现在是 凌晨 03:14</div>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="hero" id="top">
|
||||
<pre id="ascii-nebula" class="ascii-layer"></pre>
|
||||
<pre id="ascii-stars" class="ascii-layer"></pre>
|
||||
<pre id="ascii-planet" class="ascii-layer"></pre>
|
||||
<pre id="ascii-asteroids" class="ascii-layer"></pre>
|
||||
<pre id="ascii-comets" class="ascii-layer"></pre>
|
||||
<div class="hero-overlay">
|
||||
<div class="brand-wrap-outer">
|
||||
<pre class="ascii-title lg">
|
||||
██╗███╗ ██╗██╗ ██╗██╗ ██╗███████╗ █████╗ ██╗ ██╗███████╗
|
||||
██║████╗ ██║██║ ██╔╝██║ ██║██╔════╝██╔══██╗██║ ██║██╔════╝
|
||||
██║██╔██╗ ██║█████╔╝ ██║ █╗ ██║█████╗ ███████║██║ ██║█████╗
|
||||
██║██║╚██╗██║██╔═██╗ ██║███╗██║██╔══╝ ██╔══██║╚██╗ ██╔╝██╔══╝
|
||||
██║██║ ╚██╗██║██║ ██╗╚███╔███╔╝███████╗██║ ██║ ╚████╔╝ ███████╗
|
||||
╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝
|
||||
</pre>
|
||||
<div class="brand-cn-sub">墨 思</div>
|
||||
</div>
|
||||
<div class="hero-sub" style="margin-top:28px">让 散 落 的 想 法,自 己 长 出 经 纬</div>
|
||||
<div class="hero-meta">
|
||||
<span>版本 <b>v3.2 / 2026.05</b></span>
|
||||
<span>类型 <b>本地智能笔记 / 知识星图</b></span>
|
||||
<span>平台 <b>桌面 / 移动 / 网页</b></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-cue">↓ 向 下 滚 动 探 索 ↓</div>
|
||||
</section>
|
||||
|
||||
<!-- INTRO -->
|
||||
<section class="sec">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ❖ ═══</pre>关于</h2><div class="idx">[ 01 / 05 ]</div></div>
|
||||
<div class="intro">
|
||||
<p class="intro-text">
|
||||
<span class="pt" data-text="墨思">墨思</span> 是一款 <span class="em">本地优先</span> 的智能思维工作台。
|
||||
它会在你写字时,悄悄读懂上下文,
|
||||
把你过去 <span class="pt" data-text="三年">三年</span> 写过的 <span class="pt" data-text="所有笔记、网页摘录、文献批注">所有笔记、网页摘录、文献批注</span>
|
||||
自动织成一张 <span class="em">双向链接图谱</span>。
|
||||
所有推理 <span class="pt" data-text="完全在你的电脑上运行">完全在你的电脑上运行</span>,
|
||||
你的想法 <span class="em">永远不离开本机</span>。
|
||||
</p>
|
||||
<div class="intro-grid">
|
||||
<div class="cell"><b>✦ 本地优先</b>内置量化大模型,主流芯片可全程离线运行,不依赖任何云端接口。</div>
|
||||
<div class="cell"><b>✦ 双链图谱</b>每一个想法都是一颗节点,智能模型自动发现它们之间的潜在引用,让旧笔记重新被点亮。</div>
|
||||
<div class="cell"><b>✦ 端到端加密</b>多端同步走点对点链路,密钥只在你的设备里,连我们也读不到。</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES GRID -->
|
||||
<section class="sec" id="features">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ❍ ═══</pre>核心功能</h2><div class="idx">[ 02 / 05 ]</div></div>
|
||||
<div class="filter">
|
||||
<a class="tag active" data-cat="all"><span class="dot"></span>全部</a>
|
||||
<a class="tag" data-cat="ai"><span class="dot"></span>智能协作</a>
|
||||
<a class="tag" data-cat="write"><span class="dot"></span>写作</a>
|
||||
<a class="tag" data-cat="graph"><span class="dot"></span>图谱</a>
|
||||
<a class="tag" data-cat="sync"><span class="dot"></span>同步</a>
|
||||
</div>
|
||||
<div class="grid" id="grid">
|
||||
|
||||
<div class="card" data-cat="ai write">
|
||||
<div class="card-img">
|
||||
<img src="../assets/ai-01-search.jpg" alt="AI 续写">
|
||||
<pre class="ascii-overlay">▌ AI ▍▎▏
|
||||
╭─────╮
|
||||
│ ... │
|
||||
╰──→──╯</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="智能续写">智能续写</span><small>本机模型</small></div>
|
||||
<div class="card-num">/01</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>智能</span><span>写作</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="graph">
|
||||
<div class="card-img">
|
||||
<img src="../assets/ai-02-document.jpg" alt="知识图谱">
|
||||
<pre class="ascii-overlay"> ●─●─●
|
||||
│\ │ /│
|
||||
● ●─● ●
|
||||
│/ │ \│
|
||||
●─●─●</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="思维双链图谱">思维双链图谱</span><small>反向链接</small></div>
|
||||
<div class="card-num">/02</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>图谱</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="ai">
|
||||
<div class="card-img">
|
||||
<img src="../assets/ai-03-video.jpg" alt="语义搜索">
|
||||
<pre class="ascii-overlay">┌─────────┐
|
||||
│ ⌕ 找到 │
|
||||
│ 3 处 │
|
||||
│ 相似 │
|
||||
└─────────┘</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="语义搜索">语义搜索</span><small>向量检索</small></div>
|
||||
<div class="card-num">/03</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>智能</span><span>搜索</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="write">
|
||||
<div class="card-img">
|
||||
<img src="../assets/ai-04-orchestration.jpg" alt="Markdown 编辑">
|
||||
<pre class="ascii-overlay"># Heading
|
||||
- list
|
||||
- item
|
||||
> quote
|
||||
**bold**</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="纯文本写作">纯文本写作</span><small>原生标记</small></div>
|
||||
<div class="card-num">/04</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>写作</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="sync">
|
||||
<div class="card-img">
|
||||
<img src="../assets/ai-05-voice.jpg" alt="多端同步">
|
||||
<pre class="ascii-overlay"> ▣ ⇄ ▢
|
||||
╲ ╱
|
||||
⇅
|
||||
╱ ╲
|
||||
▤ ⇄ ▥</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="端到端同步">端到端同步</span><small>全程加密</small></div>
|
||||
<div class="card-num">/05</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>同步</span><span>加密</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="ai graph">
|
||||
<div class="card-img">
|
||||
<img src="../assets/ai-06-integration.jpg" alt="时光回溯">
|
||||
<pre class="ascii-overlay">←─◆─◆─◆─◆─→
|
||||
2023 24 25 26
|
||||
░░▒▒▓▓██</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="时间线回溯">时间线回溯</span><small>变迁追踪</small></div>
|
||||
<div class="card-num">/06</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>图谱</span><span>智能</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SHOWCASE -->
|
||||
<section class="sec" id="showcase">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ◈ ═══</pre>界面预览</h2><div class="idx">[ 03 / 05 ]</div></div>
|
||||
|
||||
<div class="showcase">
|
||||
<div class="showcase-shot">
|
||||
<div class="bar"><i></i><i></i><i></i></div>
|
||||
<img src="../assets/ai-07-hero.jpg" alt="编辑器界面">
|
||||
</div>
|
||||
<div class="showcase-text">
|
||||
<pre class="mini-ascii">┌──────────────────────┐
|
||||
│ ▓▓░░░░░░░░░░░░░░░░░ │
|
||||
└──────────────────────┘</pre>
|
||||
<h3>一个 <span class="em">极克制</span> 的写作环境,<br>但每一行都被静静听见。</h3>
|
||||
<p><span class="pt" data-text="左侧">左侧</span> 是无限层级的笔记本,
|
||||
<span class="pt" data-text="中间">中间</span> 是纯净的写作区域,
|
||||
<span class="pt" data-text="右侧">右侧</span> 折叠面板里,正在为你浮现三条相关旧笔记。</p>
|
||||
<p>没有花里胡哨的图标,没有打扰你的弹窗。所有的智能,<span class="pt" data-text="都安静地待在角落">都安静地待在角落</span>,等你召唤。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="showcase" style="margin-top:64px">
|
||||
<div class="showcase-text">
|
||||
<pre class="mini-ascii"> ●─●─●─●
|
||||
│╲│╱│╲│
|
||||
●─●─●─●
|
||||
│╱│╲│╱│
|
||||
●─●─●─●</pre>
|
||||
<h3>用 <span class="em">图谱视图</span>,<br>看见你思考的<span class="em">形状</span>。</h3>
|
||||
<p>每一个 <span class="pt" data-text="双向链接">双向链接</span> 都会变成一条引力线。后台持续计算笔记之间的语义距离,让相关的想法在画布上相互靠近。</p>
|
||||
<p>支持 <span class="pt" data-text="立体力导向布局、自由排布、时间线视图">立体力导向布局、自由排布、时间线视图</span>,鼠标滚轮缩放穿越十万个节点也不卡顿。</p>
|
||||
</div>
|
||||
<div class="showcase-shot">
|
||||
<div class="bar"><i></i><i></i><i></i></div>
|
||||
<img src="../assets/ai-08-architecture.jpg" alt="图谱视图">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SPEC -->
|
||||
<section class="sec" id="spec">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ✦ ═══</pre>技术规格</h2><div class="idx">[ 04 / 05 ]</div></div>
|
||||
<div class="spec-wrap">
|
||||
<div class="spec-visual">
|
||||
<img src="../assets/ai-01-search.jpg" alt="技术架构">
|
||||
<div class="label">✦ 本地推理 / 跨平台 / 开源核心</div>
|
||||
</div>
|
||||
<div class="spec-table">
|
||||
<div class="spec-row"><b>软件版本</b><span class="pt" data-text="墨思 v3.2.1,构建 26513">墨思 v3.2.1,构建 26513</span></div>
|
||||
<div class="spec-row"><b>支持平台</b><span class="pt" data-text="桌面三端 / 手机平板 / 网页">桌面三端 / 手机平板 / 网页</span></div>
|
||||
<div class="spec-row"><b>本机模型</b><span class="pt" data-text="千问 7B / 羊驼 8B 量化">千问 7B / 羊驼 8B 量化</span></div>
|
||||
<div class="spec-row"><b>云端模型</b><span class="pt" data-text="可选接入主流大模型,或自托管">可选接入主流大模型,或自托管</span></div>
|
||||
<div class="spec-row"><b>数据格式</b><span class="pt" data-text="纯文本标记 + 本地索引数据库">纯文本标记 + 本地索引数据库</span></div>
|
||||
<div class="spec-row"><b>同步协议</b><span class="pt" data-text="冲突自由 / 点对点 / 端到端加密">冲突自由 / 点对点 / 端到端加密</span></div>
|
||||
<div class="spec-row"><b>插件系统</b><span class="pt" data-text="脚本 + 沙盒,已有 280 余款插件">脚本 + 沙盒,已有 280 余款插件</span></div>
|
||||
<div class="spec-row"><b>导入导出</b><span class="pt" data-text="兼容主流笔记软件迁移">兼容主流笔记软件迁移</span></div>
|
||||
<div class="spec-row"><b>离线能力</b><span class="pt" data-text="完全可用,同步可选,无需账号">完全可用,同步可选,无需账号</span></div>
|
||||
<div class="spec-row"><b>开源情况</b><span class="pt" data-text="核心引擎开源,星标超 25k">核心引擎开源,星标超 25k</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PRICING -->
|
||||
<section class="sec" id="price">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ❑ ═══</pre>订阅方案</h2><div class="idx">[ 05 / 05 ]</div></div>
|
||||
<div class="price-grid">
|
||||
|
||||
<div class="price">
|
||||
<h4>个人版</h4>
|
||||
<div class="num">¥0<small>/ 永久免费</small></div>
|
||||
<p class="desc">适合个人写作者、学生、研究者</p>
|
||||
<ul>
|
||||
<li>所有本地功能 不限笔记数</li>
|
||||
<li>本机智能模型</li>
|
||||
<li>两台设备局域网同步</li>
|
||||
<li>社区插件 主题</li>
|
||||
<li>核心代码开源 可自托管</li>
|
||||
</ul>
|
||||
<a class="btn pt" data-text="立即下载 ↓">立即下载 ↓</a>
|
||||
</div>
|
||||
|
||||
<div class="price featured">
|
||||
<h4>★ 专业版</h4>
|
||||
<div class="num">¥38<small>/ 月,年付 ¥328</small></div>
|
||||
<p class="desc">为创作者提供云同步与高级智能</p>
|
||||
<ul>
|
||||
<li>包含个人版全部功能</li>
|
||||
<li>无限设备 端到端加密同步</li>
|
||||
<li>云端大模型接入</li>
|
||||
<li>语义搜索 自动双链建议</li>
|
||||
<li>高级图谱视图 主题市场</li>
|
||||
<li>优先邮件支持</li>
|
||||
</ul>
|
||||
<a class="btn pt" data-text="开始 14 天试用 →">开始 14 天试用 →</a>
|
||||
</div>
|
||||
|
||||
<div class="price">
|
||||
<h4>团队版</h4>
|
||||
<div class="num">¥98<small>/ 人 / 月</small></div>
|
||||
<p class="desc">适合 5–50 人的研究 创作团队</p>
|
||||
<ul>
|
||||
<li>包含专业版全部功能</li>
|
||||
<li>共享工作区 多人协作编辑</li>
|
||||
<li>权限分级 审计日志</li>
|
||||
<li>统一身份认证</li>
|
||||
<li>私有部署 内网托管</li>
|
||||
<li>专属客户成功经理</li>
|
||||
</ul>
|
||||
<a class="btn pt" data-text="联系销售 ✉">联系销售 ✉</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- INFO / CTA -->
|
||||
<section class="sec info" id="info">
|
||||
<div class="info-orbit"><pre id="info-ascii"></pre></div>
|
||||
<div class="info-big">让你的 <span class="em">想法</span>,<br>开始彼此对话。</div>
|
||||
<div class="hero-sub">v3.2 已上线 / 桌面、移动、网页全平台</div>
|
||||
<div class="info-links">
|
||||
<a class="pt" data-text="下载墨思 ↓">下载墨思 ↓</a>
|
||||
<a class="pt" data-text="网页版打开 →">网页版打开 →</a>
|
||||
<a class="pt" data-text="开发者文档 ✦">开发者文档 ✦</a>
|
||||
</div>
|
||||
<div class="platforms">
|
||||
<span>★ 苹果电脑</span><span>★ 视窗系统</span><span>★ 开源系统</span><span>★ 苹果手机</span><span>★ 平板</span><span>★ 安卓</span><span>★ 网页</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="footer">
|
||||
<span>© 2026 墨思工作室,版权所有</span>
|
||||
<span>以 ASCII 与 茶 写就于 凌晨三点</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ============================================================
|
||||
1. HERO ASCII :旋转球体 + 行星轨道 + 星点场
|
||||
============================================================ */
|
||||
(function(){
|
||||
const elNeb = document.getElementById('ascii-nebula');
|
||||
const elStar = document.getElementById('ascii-stars');
|
||||
const elPlanet = document.getElementById('ascii-planet');
|
||||
const elAst = document.getElementById('ascii-asteroids');
|
||||
const elComet = document.getElementById('ascii-comets');
|
||||
if(!elPlanet) return;
|
||||
let cols, rows, t = 0;
|
||||
let stars = [];
|
||||
let asteroids = [];
|
||||
let comets = [];
|
||||
let nebulae = [];
|
||||
|
||||
// 不规则小行星形状(多种轮廓 + 表面陨石坑)
|
||||
const ASTEROID_SHAPES = [
|
||||
[
|
||||
" .-~~-. ",
|
||||
" ( o..o )",
|
||||
" `-~~-' "
|
||||
],
|
||||
[
|
||||
" _.--._ ",
|
||||
" / .o \\ ",
|
||||
" \\ o. / ",
|
||||
" `~--~' "
|
||||
],
|
||||
[
|
||||
" .-=-. ",
|
||||
" /,o o.\\ ",
|
||||
" \\ `-' / ",
|
||||
" `~~-' "
|
||||
],
|
||||
[
|
||||
" _.+~+._ ",
|
||||
"/ . o \\",
|
||||
"\\ o . o/",
|
||||
" `~--~~' "
|
||||
],
|
||||
[
|
||||
" .o0o. ",
|
||||
" 0o..oO ",
|
||||
" `OoO' "
|
||||
],
|
||||
[
|
||||
" ,-~^~-. ",
|
||||
"( :*. * )",
|
||||
" `-,_,-' "
|
||||
]
|
||||
];
|
||||
|
||||
function reseed(){
|
||||
cols = Math.floor(window.innerWidth / 7);
|
||||
rows = Math.floor(window.innerHeight / 11);
|
||||
stars = [];
|
||||
const n = Math.floor(cols*rows*0.05);
|
||||
for(let i=0;i<n;i++){
|
||||
stars.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows,
|
||||
z: Math.random()*0.9+0.1,
|
||||
c: Math.random()<.04?'★':(Math.random()<.15?'✦':(Math.random()<.5?'.':'∙'))
|
||||
});
|
||||
}
|
||||
asteroids = [];
|
||||
const aN = Math.max(4, Math.floor(Math.min(cols,rows)/14));
|
||||
for(let i=0;i<aN;i++){
|
||||
asteroids.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows,
|
||||
vx: (Math.random()-0.5)*0.08,
|
||||
vy: (Math.random()-0.5)*0.04,
|
||||
shape: ASTEROID_SHAPES[Math.floor(Math.random()*ASTEROID_SHAPES.length)],
|
||||
rot: Math.random()*Math.PI*2,
|
||||
rs: (Math.random()-0.5)*0.01
|
||||
});
|
||||
}
|
||||
comets = [];
|
||||
for(let i=0;i<2;i++){
|
||||
comets.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows*0.6,
|
||||
vx: 0.35 + Math.random()*0.25,
|
||||
vy: 0.12 + Math.random()*0.1,
|
||||
life: Math.random()*200
|
||||
});
|
||||
}
|
||||
nebulae = [];
|
||||
for(let i=0;i<3;i++){
|
||||
nebulae.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows,
|
||||
r: 9 + Math.random()*15,
|
||||
seed: Math.random()*100
|
||||
});
|
||||
}
|
||||
}
|
||||
reseed();
|
||||
window.addEventListener('resize', reseed);
|
||||
|
||||
function newBuf(){
|
||||
const b = new Array(rows);
|
||||
for(let y=0;y<rows;y++) b[y] = new Array(cols).fill(' ');
|
||||
return b;
|
||||
}
|
||||
function flush(buf){ return buf.map(r=>r.join('')).join('\n'); }
|
||||
|
||||
function drawAsteroid(buf, ax, ay, shape){
|
||||
for(let i=0;i<shape.length;i++){
|
||||
const line = shape[i];
|
||||
const yy = Math.floor(ay) + i - Math.floor(shape.length/2);
|
||||
if(yy<0||yy>=rows) continue;
|
||||
for(let j=0;j<line.length;j++){
|
||||
const ch = line[j];
|
||||
if(ch===' ') continue;
|
||||
const xx = Math.floor(ax) + j - Math.floor(line.length/2);
|
||||
if(xx<0||xx>=cols) continue;
|
||||
buf[yy][xx] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chars = " .,:;-~+*=%#@█";
|
||||
const nebChars = "·∙∶⋮⋯░▒";
|
||||
function render(){
|
||||
t += 0.012;
|
||||
const bufNeb = newBuf();
|
||||
const bufStar = newBuf();
|
||||
const bufPlanet = newBuf();
|
||||
const bufAst = newBuf();
|
||||
const bufComet = newBuf();
|
||||
|
||||
// 星云气体
|
||||
for(const n of nebulae){
|
||||
const cx = n.x + Math.sin(t*0.2 + n.seed)*3;
|
||||
const cy = n.y + Math.cos(t*0.15 + n.seed)*2;
|
||||
for(let dy=-n.r; dy<=n.r; dy++){
|
||||
for(let dx=-n.r*1.6; dx<=n.r*1.6; dx++){
|
||||
const xx = Math.floor(cx+dx);
|
||||
const yy = Math.floor(cy+dy);
|
||||
if(xx<0||xx>=cols||yy<0||yy>=rows) continue;
|
||||
const d = Math.sqrt((dx/1.6)*(dx/1.6) + dy*dy);
|
||||
if(d > n.r) continue;
|
||||
const noise = Math.sin(xx*0.3 + n.seed)*Math.cos(yy*0.4 - n.seed*0.7) + Math.sin(t*0.5+n.seed);
|
||||
const intensity = (1 - d/n.r) * (0.5 + 0.5*noise);
|
||||
if(intensity > 0.55){
|
||||
const ci = Math.min(nebChars.length-1, Math.floor(intensity * nebChars.length));
|
||||
bufNeb[yy][xx] = nebChars[ci];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 闪烁星点
|
||||
for(const s of stars){
|
||||
const xi = Math.floor((s.x + Math.sin(t*0.3 + s.y)*0.3 + cols)%cols);
|
||||
const yi = Math.floor(s.y);
|
||||
if(yi>=0 && yi<rows){
|
||||
const tw = (Math.sin(t*2 + s.x*0.7 + s.y*0.3)+1)*0.5;
|
||||
if(tw>0.55 || s.c==='★' || s.c==='✦') bufStar[yi][xi] = s.c;
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转行星
|
||||
const cx = cols/2, cy = rows/2;
|
||||
const R = Math.min(cols,rows*2)*0.18;
|
||||
const aspect = 0.5;
|
||||
for(let phi=0; phi<Math.PI; phi+=0.06){
|
||||
for(let theta=0; theta<2*Math.PI; theta+=0.04){
|
||||
const x3 = R*Math.sin(phi)*Math.cos(theta + t);
|
||||
const y3 = R*Math.cos(phi);
|
||||
const z3 = R*Math.sin(phi)*Math.sin(theta + t);
|
||||
const lx = Math.cos(t*0.6), ly = -0.3, lz = Math.sin(t*0.6);
|
||||
const nx = Math.sin(phi)*Math.cos(theta + t);
|
||||
const ny = Math.cos(phi);
|
||||
const nz = Math.sin(phi)*Math.sin(theta + t);
|
||||
const lum = nx*lx + ny*ly + nz*lz;
|
||||
if(z3 < 0) continue;
|
||||
const sx = Math.floor(cx + x3/aspect);
|
||||
const sy = Math.floor(cy + y3);
|
||||
if(sx<0||sx>=cols||sy<0||sy>=rows) continue;
|
||||
const idx = Math.max(0, Math.min(chars.length-1, Math.floor((lum+0.2)*chars.length*0.9)));
|
||||
bufPlanet[sy][sx] = chars[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// 行星轨道与卫星
|
||||
for(let i=0; i<3; i++){
|
||||
const orbR = R*(1.6 + i*0.5);
|
||||
const tilt = 0.35 + i*0.1;
|
||||
for(let a=0; a<2*Math.PI; a+=0.04){
|
||||
const ox = orbR*Math.cos(a + t*(0.4-i*0.1));
|
||||
const oy = orbR*Math.sin(a + t*(0.4-i*0.1))*tilt;
|
||||
const sx = Math.floor(cx + ox/aspect);
|
||||
const sy = Math.floor(cy + oy);
|
||||
if(sx<0||sx>=cols||sy<0||sy>=rows) continue;
|
||||
if(bufPlanet[sy][sx]===' ') bufPlanet[sy][sx] = (i===0?'.':(i===1?'∙':'•'));
|
||||
}
|
||||
const a = t*(0.7-i*0.18);
|
||||
const ox = orbR*Math.cos(a);
|
||||
const oy = orbR*Math.sin(a)*tilt;
|
||||
const sx = Math.floor(cx + ox/aspect);
|
||||
const sy = Math.floor(cy + oy);
|
||||
if(sx>=0&&sx<cols&&sy>=0&&sy<rows) bufPlanet[sy][sx] = (i===0?'◉':(i===1?'◎':'○'));
|
||||
}
|
||||
|
||||
// 彗星拖尾
|
||||
for(const c of comets){
|
||||
c.x += c.vx; c.y += c.vy; c.life++;
|
||||
if(c.x>cols+10 || c.y>rows+5){
|
||||
c.x = -10; c.y = Math.random()*rows*0.5;
|
||||
c.life = 0;
|
||||
}
|
||||
const tail = 16;
|
||||
for(let k=0;k<tail;k++){
|
||||
const tx = Math.floor(c.x - k*c.vx*1.5);
|
||||
const ty = Math.floor(c.y - k*c.vy*1.5);
|
||||
if(tx<0||tx>=cols||ty<0||ty>=rows) continue;
|
||||
const tch = k===0?'☄':(k<3?'*':(k<7?'∙':(k<12?'·':'.')));
|
||||
bufComet[ty][tx] = tch;
|
||||
}
|
||||
}
|
||||
|
||||
// 漂浮的小行星
|
||||
for(const a of asteroids){
|
||||
a.x += a.vx; a.y += a.vy; a.rot += a.rs;
|
||||
if(a.x<-6) a.x = cols+6;
|
||||
if(a.x>cols+6) a.x = -6;
|
||||
if(a.y<-4) a.y = rows+4;
|
||||
if(a.y>rows+4) a.y = -4;
|
||||
const wob = Math.sin(t*0.8 + a.rot)*0.6;
|
||||
drawAsteroid(bufAst, a.x + wob, a.y + Math.cos(t*0.6+a.rot)*0.4, a.shape);
|
||||
}
|
||||
|
||||
elNeb.textContent = flush(bufNeb);
|
||||
elStar.textContent = flush(bufStar);
|
||||
elPlanet.textContent = flush(bufPlanet);
|
||||
elAst.textContent = flush(bufAst);
|
||||
elComet.textContent = flush(bufComet);
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
render();
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
2. INFO 区小型轨道
|
||||
============================================================ */
|
||||
(function(){
|
||||
const el = document.getElementById('info-ascii');
|
||||
if(!el) return;
|
||||
const W=44, H=22;
|
||||
let t=0;
|
||||
// 小行星带的随机位置
|
||||
const belt = [];
|
||||
for(let i=0;i<14;i++){
|
||||
belt.push({
|
||||
a: Math.random()*Math.PI*2,
|
||||
r: 8.5 + Math.random()*1.6,
|
||||
sp: 0.5 + Math.random()*0.4,
|
||||
ch: '✦◆◈*·•'[Math.floor(Math.random()*6)]
|
||||
});
|
||||
}
|
||||
function tick(){
|
||||
t+=0.04;
|
||||
const buf = Array.from({length:H},()=>new Array(W).fill(' '));
|
||||
const cx=W/2, cy=H/2;
|
||||
|
||||
// 散落的星点背景
|
||||
for(let i=0;i<24;i++){
|
||||
const x = Math.floor((Math.sin(i*7.3)+1)*W/2);
|
||||
const y = Math.floor((Math.cos(i*5.1)+1)*H/2);
|
||||
const tw = (Math.sin(t*1.5 + i)+1)*0.5;
|
||||
if(x>=0&&x<W&&y>=0&&y<H && tw>0.6) buf[y][x]='.';
|
||||
}
|
||||
|
||||
// 行星轨道
|
||||
for(let i=0;i<4;i++){
|
||||
const r = 3+i*2.6;
|
||||
for(let a=0;a<Math.PI*2;a+=0.18){
|
||||
const x = Math.floor(cx + r*Math.cos(a)*1.0);
|
||||
const y = Math.floor(cy + r*Math.sin(a)*0.5);
|
||||
if(x>=0&&x<W&&y>=0&&y<H && buf[y][x]===' ') buf[y][x]='.';
|
||||
}
|
||||
const a = t*(1-i*0.18);
|
||||
const x = Math.floor(cx + r*Math.cos(a));
|
||||
const y = Math.floor(cy + r*Math.sin(a)*0.5);
|
||||
if(x>=0&&x<W&&y>=0&&y<H) buf[y][x]='★';
|
||||
}
|
||||
|
||||
// 外侧小行星带
|
||||
for(const b of belt){
|
||||
const ang = b.a + t*b.sp*0.3;
|
||||
const x = Math.floor(cx + b.r*Math.cos(ang));
|
||||
const y = Math.floor(cy + b.r*Math.sin(ang)*0.5);
|
||||
if(x>=0&&x<W&&y>=0&&y<H) buf[y][x]=b.ch;
|
||||
}
|
||||
|
||||
// 中心恒星与日冕
|
||||
if(buf[Math.floor(cy)]) buf[Math.floor(cy)][Math.floor(cx)]='◉';
|
||||
const halo = [[-1,0,'-'],[1,0,'-'],[0,-1,'|'],[0,1,'|']];
|
||||
for(const [dx,dy,ch] of halo){
|
||||
const x = Math.floor(cx)+dx, y = Math.floor(cy)+dy;
|
||||
if(x>=0&&x<W&&y>=0&&y<H && buf[y][x]===' ') buf[y][x]=ch;
|
||||
}
|
||||
|
||||
el.textContent = buf.map(r=>r.join('')).join('\n');
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
tick();
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
3. PROTEXT :文本乱码-解码效果
|
||||
悬停或进入视口时,文字先打乱再逐字"解密"显现
|
||||
============================================================ */
|
||||
(function(){
|
||||
const glyphs = "★☆✶✦✧◉◎○●∙•~≈≋▁▂▃▄▅▆▇█ABCDEFGHJKLMNPQRSTUVWXYZ0123456789@#%&*";
|
||||
const RAND = ()=> glyphs[Math.floor(Math.random()*glyphs.length)];
|
||||
|
||||
function scramble(el){
|
||||
if(el.dataset.scrLock==='1') return;
|
||||
el.dataset.scrLock='1';
|
||||
el.classList.add('scrambling');
|
||||
const target = el.dataset.text || el.textContent;
|
||||
const chars = [...target];
|
||||
const len = chars.length;
|
||||
let frame = 0;
|
||||
const total = 16;
|
||||
const step = ()=>{
|
||||
frame++;
|
||||
const out = chars.map((ch,i)=>{
|
||||
if(ch===' '||ch==='\n') return ch;
|
||||
const reveal = Math.floor((frame/total)*len*1.2);
|
||||
if(i < reveal) return ch;
|
||||
return RAND();
|
||||
}).join('');
|
||||
el.textContent = out;
|
||||
if(frame < total){
|
||||
setTimeout(step, 40);
|
||||
} else {
|
||||
el.textContent = target;
|
||||
el.classList.remove('scrambling');
|
||||
el.dataset.scrLock='0';
|
||||
}
|
||||
};
|
||||
step();
|
||||
}
|
||||
|
||||
document.querySelectorAll('.pt').forEach(el=>{
|
||||
if(!el.dataset.text) el.dataset.text = el.textContent.trim();
|
||||
el.addEventListener('mouseenter', ()=>scramble(el));
|
||||
});
|
||||
|
||||
const io = new IntersectionObserver((entries)=>{
|
||||
entries.forEach(e=>{
|
||||
if(e.isIntersecting){
|
||||
scramble(e.target);
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
});
|
||||
},{threshold:0.3});
|
||||
document.querySelectorAll('.intro-text .pt, .spec-row .pt, .showcase-text .pt').forEach(el=>io.observe(el));
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
4. 标签筛选
|
||||
============================================================ */
|
||||
(function(){
|
||||
const tags = document.querySelectorAll('.tag');
|
||||
const cards = document.querySelectorAll('#grid .card');
|
||||
tags.forEach(tag=>{
|
||||
tag.addEventListener('click', ()=>{
|
||||
tags.forEach(t=>t.classList.remove('active'));
|
||||
tag.classList.add('active');
|
||||
const cat = tag.dataset.cat;
|
||||
cards.forEach(c=>{
|
||||
if(cat==='all' || c.dataset.cat.split(' ').includes(cat)){
|
||||
c.classList.remove('hidden');
|
||||
} else {
|
||||
c.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
5. 平滑滚动
|
||||
============================================================ */
|
||||
document.querySelectorAll('a[href^="#"]').forEach(a=>{
|
||||
a.addEventListener('click', e=>{
|
||||
const id = a.getAttribute('href');
|
||||
if(id.length>1){
|
||||
const tgt = document.querySelector(id);
|
||||
if(tgt){ e.preventDefault(); tgt.scrollIntoView({behavior:'smooth',block:'start'}); }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ============================================================
|
||||
6. 实时时钟
|
||||
============================================================ */
|
||||
(function(){
|
||||
const el = document.querySelector('.here');
|
||||
if(!el) return;
|
||||
function fmt(n){return n<10?'0'+n:n}
|
||||
function tick(){
|
||||
const d = new Date();
|
||||
el.innerHTML = '你好<br><span class="blink">▮</span> 现在是 ' + fmt(d.getHours())+':'+fmt(d.getMinutes())+':'+fmt(d.getSeconds());
|
||||
setTimeout(tick, 1000);
|
||||
}
|
||||
tick();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
# 风格预设库
|
||||
|
||||
每个预设给 8 个 `:root` CSS 变量 + 5 个 ASCII 层颜色 + hero 背景渐变。改其他地方需要相应连带改(图片滤镜、scrambler 字符颜色等)。
|
||||
|
||||
> **配色原则**:5 个 ASCII 层在背景上必须**同时可读 + 互相和谐**。同一冷暖、轻微变奏,不要把"对比 = 好看"。
|
||||
|
||||
---
|
||||
|
||||
## 1. 深空青(默认 · cosmic-cyan)
|
||||
|
||||
最克制的太空感,宝石蓝紫为底,青/粉/琥珀的发光层。适合本地优先 AI、笔记/写作、独立软件、CLI 工具。
|
||||
|
||||
```css
|
||||
:root{
|
||||
--bg:#05060f;
|
||||
--fg:#e6e8ff;
|
||||
--dim:#7a7fa8;
|
||||
--accent:#8be9ff; /* 青 */
|
||||
--accent2:#ff6ad5; /* 粉 */
|
||||
--accent3:#c792ea; /* 紫 */
|
||||
--line:#1a1d3a;
|
||||
--hl:#0d1024;
|
||||
--paper:#0a0c1f;
|
||||
}
|
||||
.hero{ background: radial-gradient(ellipse at 30% 20%, #1a0a2e 0%, #05060f 55%, #000 100%); }
|
||||
#ascii-nebula { color:#a87bd6; } /* 星云 */
|
||||
#ascii-stars { color:#cfe6ff; } /* 星点 */
|
||||
#ascii-planet { color:#8be9ff; } /* 行星 */
|
||||
#ascii-asteroids{ color:#f5b971; } /* 小行星 */
|
||||
#ascii-comets { color:#ff8fd2; } /* 彗星 */
|
||||
```
|
||||
图片滤镜:`hue-rotate(200deg) saturate(.9) contrast(1.1) brightness(.6)`
|
||||
|
||||
---
|
||||
|
||||
## 2. 暖色羊皮纸(letterpress-warm · 原墨思配色)
|
||||
|
||||
宋体凸印 + 米色背景,复古旧报纸感。适合中文写作工具、独立出版、设计工作室、小众杂志。
|
||||
|
||||
```css
|
||||
:root{
|
||||
--bg:#F3E4D4;
|
||||
--fg:#1f1510;
|
||||
--dim:#8a7560;
|
||||
--accent:#660125; /* 酒红 */
|
||||
--accent2:#a83344;
|
||||
--accent3:#7a4a1f;
|
||||
--line:#d8c4af;
|
||||
--hl:#ecd8ca;
|
||||
--paper:#ecd8ca;
|
||||
}
|
||||
.hero{ background: linear-gradient(180deg, #F3E4D4 0%, #e9d2b8 100%); }
|
||||
#ascii-nebula { color:#b88a6a; opacity:.5; mix-blend-mode:multiply; }
|
||||
#ascii-stars { color:#5a3a1e; opacity:.7; }
|
||||
#ascii-planet { color:#660125; }
|
||||
#ascii-asteroids{ color:#7a4a1f; }
|
||||
#ascii-comets { color:#a83344; }
|
||||
```
|
||||
图片滤镜:`sepia(.3) contrast(1.05) saturate(.85) brightness(.95)`
|
||||
注:暖色背景下要把 `mix-blend-mode: screen` 改成 `multiply`,让 ASCII 是"压在纸上"而不是"发光"。
|
||||
|
||||
---
|
||||
|
||||
## 3. 赛博紫(cyber-violet)
|
||||
|
||||
霓虹紫 + 电子粉,赛博朋克招牌色。适合 AI / Web3 / 游戏外设 / 电竞订阅。
|
||||
|
||||
```css
|
||||
:root{
|
||||
--bg:#0a0118;
|
||||
--fg:#f0e6ff;
|
||||
--dim:#7e5cab;
|
||||
--accent:#ff00aa; /* 霓虹品红 */
|
||||
--accent2:#00ffe0; /* 青电流 */
|
||||
--accent3:#b14aff; /* 紫 */
|
||||
--line:#2a0d4a;
|
||||
--hl:#150327;
|
||||
--paper:#10021f;
|
||||
}
|
||||
.hero{ background: radial-gradient(ellipse at 70% 30%, #4a0e6e 0%, #0a0118 60%, #000 100%); }
|
||||
#ascii-nebula { color:#b14aff; opacity:.55; }
|
||||
#ascii-stars { color:#e0d4ff; }
|
||||
#ascii-planet { color:#00ffe0; }
|
||||
#ascii-asteroids{ color:#ff00aa; }
|
||||
#ascii-comets { color:#ffd400; } /* 黄电流彗星 */
|
||||
```
|
||||
图片滤镜:`hue-rotate(280deg) saturate(1.4) contrast(1.1) brightness(.65)`
|
||||
|
||||
---
|
||||
|
||||
## 4. 终端绿(terminal-green)
|
||||
|
||||
黑底 phosphor 绿,复古 CRT 终端味。适合 CLI 工具、开发者订阅、安全产品、复古游戏。
|
||||
|
||||
```css
|
||||
:root{
|
||||
--bg:#000800;
|
||||
--fg:#a8ffb0;
|
||||
--dim:#5fa05f;
|
||||
--accent:#00ff66; /* phosphor 绿 */
|
||||
--accent2:#80ff80;
|
||||
--accent3:#ffaa44;
|
||||
--line:#0a3010;
|
||||
--hl:#001500;
|
||||
--paper:#001a05;
|
||||
}
|
||||
.hero{ background: radial-gradient(ellipse at center, #052010 0%, #000800 70%, #000 100%); }
|
||||
#ascii-nebula { color:#2a8a44; opacity:.45; }
|
||||
#ascii-stars { color:#a8ffb0; }
|
||||
#ascii-planet { color:#00ff66; }
|
||||
#ascii-asteroids{ color:#ffaa44; }
|
||||
#ascii-comets { color:#80ffd4; }
|
||||
```
|
||||
图片滤镜:`grayscale(1) brightness(.6) sepia(1) hue-rotate(70deg) saturate(2)`
|
||||
另外可在 `.hero::after` 加扫描线:`background: repeating-linear-gradient(0deg, transparent 0, transparent 2px, rgba(0,255,102,.05) 3px, rgba(0,255,102,.05) 4px);`
|
||||
|
||||
---
|
||||
|
||||
## 5. 极光(aurora · 默认风格的冷淡变奏)
|
||||
|
||||
灰青冰蓝主色,加一抹粉绿,像北极极光。适合冥想 / 健康 / 旅行 / 北欧风产品。
|
||||
|
||||
```css
|
||||
:root{
|
||||
--bg:#0c1420;
|
||||
--fg:#dceaf0;
|
||||
--dim:#6b8499;
|
||||
--accent:#7fe3c4; /* 极光绿 */
|
||||
--accent2:#b8a4ff; /* 淡紫 */
|
||||
--accent3:#ffb3c1; /* 极光粉 */
|
||||
--line:#1a2638;
|
||||
--hl:#101a28;
|
||||
--paper:#0e1622;
|
||||
}
|
||||
.hero{ background: linear-gradient(160deg, #1a3548 0%, #0c1420 50%, #050810 100%); }
|
||||
#ascii-nebula { color:#b8a4ff; opacity:.5; }
|
||||
#ascii-stars { color:#dceaf0; }
|
||||
#ascii-planet { color:#7fe3c4; }
|
||||
#ascii-asteroids{ color:#ffb3c1; }
|
||||
#ascii-comets { color:#88ddff; }
|
||||
```
|
||||
图片滤镜:`hue-rotate(180deg) saturate(.7) contrast(1.05) brightness(.7)`
|
||||
|
||||
---
|
||||
|
||||
## 6. 沙漠日落(desert-sunset · 暖色 ASCII 系)
|
||||
|
||||
橙红土 + 焦糖 + 杏色,沙丘黄昏感。适合咖啡 / 独立硬件 / 工艺品 / 食品订阅。
|
||||
|
||||
```css
|
||||
:root{
|
||||
--bg:#1a0a05;
|
||||
--fg:#f5e6d0;
|
||||
--dim:#a8765a;
|
||||
--accent:#ff8b3d; /* 烤橙 */
|
||||
--accent2:#d94545; /* 焦红 */
|
||||
--accent3:#ffd47a; /* 杏黄 */
|
||||
--line:#3a1a10;
|
||||
--hl:#1f0d06;
|
||||
--paper:#15080a;
|
||||
}
|
||||
.hero{ background: radial-gradient(ellipse at 50% 80%, #5a1f10 0%, #1a0a05 60%, #000 100%); }
|
||||
#ascii-nebula { color:#a8765a; opacity:.5; }
|
||||
#ascii-stars { color:#ffd47a; }
|
||||
#ascii-planet { color:#ff8b3d; }
|
||||
#ascii-asteroids{ color:#d94545; }
|
||||
#ascii-comets { color:#ffe9c2; }
|
||||
```
|
||||
图片滤镜:`sepia(.5) saturate(1.3) contrast(1.1) brightness(.7) hue-rotate(-10deg)`
|
||||
|
||||
---
|
||||
|
||||
## 怎么自由设计
|
||||
|
||||
如果用户给的风格描述不在以上 6 种里,按这个流程:
|
||||
|
||||
1. **定主色调温度**:冷(蓝/紫/青)/ 暖(橙/红/黄)/ 中性(绿/灰)
|
||||
2. **`--bg` 选最深值**(如 `#05060f`),`--fg` 选最浅值(如 `#e6e8ff`)
|
||||
3. **`--accent` / `--accent2` / `--accent3`** 三色:拉开**色相**而非饱和度,它们在小区域出现,要"互相点亮"
|
||||
4. **5 个 ASCII 层颜色** 排序原则:
|
||||
- `nebula` → 半透紫/灰,做底
|
||||
- `stars` → 高明度白/暖白,最亮
|
||||
- `planet` → 主 `--accent`
|
||||
- `asteroids` → 暖色对比(即使主调冷也加暖一抹)
|
||||
- `comets` → 第二高饱和,做"流过去"的视觉锚
|
||||
5. **图片滤镜** 必须连带调,否则 unsplash 图会"颜色不进环境"
|
||||
|
||||
> 测试方法:把 5 个层颜色填进调色板生成器(Coolors / Realtime Colors),确认它们之间最低对比度 ≥ 2.0,最高色相差 ≥ 90°。
|
||||
+967
@@ -0,0 +1,967 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><!-- SWAP-1 -->{{PRODUCT_CN}} — {{TAGLINE_SHORT}}</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#05060f;
|
||||
--fg:#e6e8ff;
|
||||
--dim:#7a7fa8;
|
||||
--accent:#8be9ff;
|
||||
--accent2:#ff6ad5;
|
||||
--accent3:#c792ea;
|
||||
--line:#1a1d3a;
|
||||
--hl:#0d1024;
|
||||
--paper:#0a0c1f;
|
||||
}
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
html,body{background:var(--bg);color:var(--fg);font-family:"Songti SC","STSong","Source Han Serif SC","Noto Serif CJK SC","SimSun",serif;font-size:14px;line-height:1.8;-webkit-font-smoothing:antialiased}
|
||||
/* 中文段落均使用宋体 */
|
||||
:lang(zh),.intro-text,.showcase-text p,.showcase-text h3,.info-big,.hero-title,.card-title,.price .desc,.cell{font-family:"Songti SC","STSong","Source Han Serif SC","Noto Serif CJK SC","SimSun",serif}
|
||||
/* 数字 / 拉丁字母仍保持等宽,但中文遇到等宽会 fallback 回宋体 */
|
||||
.header-logo,.nav a,.hero-meta,.hero-sub,.sec-head,.scroll-cue,.tag,.card-num,.card-tags,.spec-row b,.price h4,.price .num,.platforms,.footer,.here{font-family:"SF Mono","Menlo","Consolas","Songti SC","STSong","SimSun",serif}
|
||||
a{color:inherit;text-decoration:none;cursor:pointer}
|
||||
::selection{background:var(--accent);color:var(--bg)}
|
||||
|
||||
/* HEADER */
|
||||
.header{position:fixed;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:center;padding:18px 28px;z-index:100;color:var(--fg);background:rgba(5,6,15,.85);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||||
.header-logo{font-weight:600;letter-spacing:0.18em;font-size:13px}
|
||||
.header-logo span{color:var(--accent)}
|
||||
.nav ul{display:flex;gap:26px;list-style:none}
|
||||
.nav a{font-size:12px;letter-spacing:0.2em;position:relative;padding:4px 0;transition:color .3s}
|
||||
.nav a:hover{color:var(--accent)}
|
||||
.nav a::after{content:"";position:absolute;left:0;bottom:0;height:1px;width:0;background:var(--accent);transition:width .3s}
|
||||
.nav a:hover::after{width:100%}
|
||||
|
||||
/* HEY */
|
||||
.here{position:fixed;right:32px;bottom:28px;font-size:12px;letter-spacing:0.2em;z-index:90;text-align:right;color:var(--dim)}
|
||||
.here .blink{display:inline-block;animation:blink 1.2s infinite}
|
||||
@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}
|
||||
|
||||
/* HERO ASCII */
|
||||
.hero{height:100vh;width:100%;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;border-bottom:1px solid var(--line);background:radial-gradient(ellipse at 30% 20%, #1a0a2e 0%, #05060f 55%, #000 100%)}
|
||||
.ascii-layer{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:12px;line-height:12px;letter-spacing:0;white-space:pre;font-family:"SF Mono",monospace;pointer-events:none;font-weight:600;margin:0}
|
||||
#ascii-nebula{color:#a87bd6;opacity:.55;text-shadow:0 0 8px rgba(168,123,214,.45);mix-blend-mode:screen}
|
||||
#ascii-stars{color:#cfe6ff;opacity:.85;text-shadow:0 0 4px rgba(207,230,255,.5)}
|
||||
#ascii-planet{color:var(--accent);opacity:.95;text-shadow:0 0 6px rgba(139,233,255,.45)}
|
||||
#ascii-asteroids{color:#f5b971;opacity:.92;text-shadow:0 0 6px rgba(245,185,113,.45)}
|
||||
#ascii-comets{color:#ff8fd2;opacity:.95;text-shadow:0 0 8px rgba(255,143,210,.55)}
|
||||
.hero-overlay{position:relative;z-index:2;text-align:center;padding:0 24px;color:var(--fg)}
|
||||
.hero-title{font-size:clamp(40px,7vw,98px);font-weight:300;letter-spacing:0.04em;line-height:1;margin-bottom:18px}
|
||||
.hero-title .em{font-style:italic;color:var(--accent)}
|
||||
.hero-sub{font-size:14px;letter-spacing:0.3em;color:var(--dim);margin-bottom:36px}
|
||||
|
||||
/* ASCII 大标题 */
|
||||
.ascii-title{font-family:"SF Mono","Menlo","Consolas",monospace;color:var(--accent);white-space:pre;display:inline-block;text-align:left;line-height:1;font-weight:700;margin:0 auto;letter-spacing:0;text-shadow:0 0 12px rgba(139,233,255,.5),0 0 2px rgba(199,146,234,.4)}
|
||||
.ascii-title.lg{font-size:clamp(8px,1.25vw,14px);line-height:1.05}
|
||||
.ascii-title.md{font-size:clamp(6px,.85vw,10px);color:var(--fg)}
|
||||
.ascii-title.sm{font-size:clamp(5px,.55vw,7px);color:var(--accent);opacity:.85}
|
||||
.brand-wrap-outer{text-align:center}
|
||||
.brand-cn-sub{font-family:"Songti SC","STSong","SimSun",serif;font-size:clamp(28px,3.6vw,46px);font-weight:400;letter-spacing:0.5em;color:var(--fg);margin:24px 0 0;padding-left:0.5em}
|
||||
.ascii-cn{display:none}
|
||||
.sec-ascii{display:block;color:var(--accent);white-space:pre;font-family:"SF Mono",monospace;font-size:14px;line-height:1;margin-bottom:10px;opacity:.85;letter-spacing:0.4em}
|
||||
|
||||
/* 中文真字宋体大标题 + ASCII 网点背景 */
|
||||
.brand-block{text-align:center;line-height:1;display:inline-block;padding:36px 64px;position:relative}
|
||||
.brand-block::before{
|
||||
content:"";position:absolute;inset:0;
|
||||
background-image:radial-gradient(circle, rgba(139,233,255,.22) 1px, transparent 1.4px);
|
||||
background-size:7px 7px;
|
||||
pointer-events:none;
|
||||
opacity:.85;
|
||||
z-index:0;
|
||||
/* 中间淡四角浓的渐隐遮罩,像深空尘埃 */
|
||||
-webkit-mask-image:radial-gradient(ellipse at center, transparent 30%, #000 90%);
|
||||
mask-image:radial-gradient(ellipse at center, transparent 30%, #000 90%);
|
||||
}
|
||||
.brand-deco-top,.brand-deco-bot{font-family:"SF Mono","Menlo",monospace;color:var(--accent);font-size:clamp(12px,1.4vw,18px);letter-spacing:0.6em;opacity:.85;margin:0 0 18px;position:relative;z-index:1}
|
||||
.brand-deco-bot{margin:18px 0 0}
|
||||
.brand-title-cn{
|
||||
font-family:"Songti SC","STSong","Source Han Serif SC","SimSun",serif;
|
||||
font-weight:700;
|
||||
font-size:clamp(140px,20vw,240px);
|
||||
line-height:1;
|
||||
letter-spacing:0.32em;
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
z-index:1;
|
||||
|
||||
/* 字内填充:星云网点,模拟字符画/星尘 */
|
||||
background-image:
|
||||
radial-gradient(circle, var(--accent) 0.95px, transparent 1.3px),
|
||||
radial-gradient(circle, var(--accent2) 0.45px, transparent 0.85px);
|
||||
background-size: 5px 5px, 3px 3px;
|
||||
background-position: 0 0, 1.5px 1.5px;
|
||||
-webkit-background-clip:text;
|
||||
background-clip:text;
|
||||
color:transparent;
|
||||
-webkit-text-fill-color:transparent;
|
||||
|
||||
/* 极细描边 */
|
||||
-webkit-text-stroke:0.4px rgba(139,233,255,.55);
|
||||
text-shadow:0 0 8px rgba(139,233,255,.3);
|
||||
}
|
||||
.brand-title-cn .gap{display:inline-block;width:.2em}
|
||||
.brand-wrap-outer{text-align:center}
|
||||
.sec-head h2{font-size:18px;font-weight:400;letter-spacing:0.4em;color:var(--fg);display:flex;flex-direction:column;gap:6px;font-family:"Songti SC","STSong","SimSun",serif}
|
||||
.hero-meta{display:flex;gap:32px;justify-content:center;font-size:11px;letter-spacing:0.16em;color:var(--dim);flex-wrap:wrap}
|
||||
.hero-meta span b{color:var(--fg);font-weight:400}
|
||||
|
||||
.scroll-cue{position:absolute;left:50%;bottom:32px;transform:translateX(-50%);font-size:11px;letter-spacing:0.3em;color:var(--dim);z-index:3;animation:bob 2.4s ease-in-out infinite}
|
||||
@keyframes bob{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,8px)}}
|
||||
|
||||
/* SECTION */
|
||||
.sec{padding:96px 48px;border-bottom:1px solid var(--line);position:relative}
|
||||
.sec-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:48px;font-size:11px;letter-spacing:0.24em;color:var(--dim);border-bottom:1px solid var(--line);padding-bottom:18px}
|
||||
.sec-head .idx{font-family:monospace}
|
||||
|
||||
/* INTRO */
|
||||
.intro{max-width:980px;margin:0 auto}
|
||||
.intro-text{font-size:clamp(20px,2.4vw,30px);line-height:1.55;font-weight:300;letter-spacing:0.02em}
|
||||
.intro-text .pt{display:inline;cursor:default;transition:color .2s}
|
||||
.intro-text .pt:hover{color:var(--accent)}
|
||||
.intro-text .pt.scrambling{color:var(--accent)}
|
||||
.intro-text .em{color:var(--accent);font-style:italic}
|
||||
.intro-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:32px;margin-top:64px;padding-top:48px;border-top:1px dashed var(--line)}
|
||||
.intro-grid .cell{font-size:12px;color:var(--dim);line-height:1.7}
|
||||
.intro-grid .cell b{display:block;color:var(--fg);font-weight:400;margin-bottom:8px;letter-spacing:0.16em;font-size:11px}
|
||||
|
||||
/* TAGS */
|
||||
.filter{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:36px}
|
||||
.tag{padding:6px 14px;border:1px solid var(--line);font-size:11px;letter-spacing:0.16em;color:var(--dim);transition:all .25s;cursor:pointer;user-select:none}
|
||||
.tag:hover{color:var(--fg);border-color:var(--dim)}
|
||||
.tag.active{background:var(--accent);color:var(--bg);border-color:var(--accent)}
|
||||
.tag .dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:currentColor;margin-right:8px;vertical-align:middle;opacity:.6}
|
||||
|
||||
/* GRID */
|
||||
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
|
||||
.card{border:1px solid var(--line);background:var(--paper);transition:all .3s;position:relative;overflow:hidden;display:flex;flex-direction:column}
|
||||
.card:hover{border-color:var(--accent);transform:translateY(-4px);box-shadow:0 12px 32px -16px rgba(139,233,255,.5)}
|
||||
.card-img{aspect-ratio:4/3;background:#0d1024;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}
|
||||
.card-img img{width:100%;height:100%;object-fit:cover;transition:transform .8s ease,filter .4s;filter:hue-rotate(200deg) saturate(.9) contrast(1.1) brightness(.6)}
|
||||
.card:hover .card-img img{transform:scale(1.06);filter:hue-rotate(220deg) saturate(1.1) contrast(1.05) brightness(.75)}
|
||||
.card-img .ascii-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:9px;line-height:9px;color:var(--accent);white-space:pre;opacity:.75;pointer-events:none;mix-blend-mode:screen;text-shadow:0 0 4px rgba(139,233,255,.6)}
|
||||
.card-meta{padding:18px 20px;display:flex;justify-content:space-between;align-items:flex-start;gap:14px}
|
||||
.card-title{font-size:14px;letter-spacing:0.04em;font-weight:400}
|
||||
.card-title .pt{cursor:default}
|
||||
.card-title small{display:block;color:var(--dim);font-size:11px;margin-top:6px;letter-spacing:0.12em}
|
||||
.card-num{color:var(--dim);font-size:10px;font-family:monospace;letter-spacing:0.1em}
|
||||
.card-tags{display:flex;gap:6px;padding:0 20px 18px;flex-wrap:wrap}
|
||||
.card-tags span{font-size:10px;letter-spacing:0.16em;color:var(--dim);padding:2px 8px;border:1px solid var(--line);text-transform:uppercase}
|
||||
.card.hidden{display:none}
|
||||
|
||||
/* SHOWCASE / UI 截图 */
|
||||
.showcase{display:grid;grid-template-columns:1.2fr 1fr;gap:48px;align-items:center;margin-top:24px}
|
||||
.showcase-shot{border:1px solid var(--line);background:var(--paper);padding:14px;position:relative;box-shadow:0 8px 28px -16px rgba(139,233,255,.3)}
|
||||
.showcase-shot .bar{display:flex;gap:6px;margin-bottom:10px}
|
||||
.showcase-shot .bar i{display:block;width:10px;height:10px;border-radius:50%;background:#3a3f6a}
|
||||
.showcase-shot img{width:100%;height:auto;display:block;filter:hue-rotate(200deg) saturate(.85) contrast(1.05) brightness(.7);border:1px solid var(--line)}
|
||||
.showcase-text h3{font-size:clamp(24px,3vw,36px);font-weight:300;line-height:1.3;margin-bottom:18px;letter-spacing:0.02em;color:var(--fg)}
|
||||
.showcase-text h3 .em{color:var(--accent);font-style:italic}
|
||||
.showcase-text .mini-ascii{font-family:"SF Mono",monospace;color:var(--accent);font-size:10px;line-height:1;white-space:pre;display:block;margin-bottom:18px;opacity:.85}
|
||||
.showcase-text p{color:var(--dim);font-size:13px;line-height:1.8;margin-bottom:14px}
|
||||
.showcase-text p .pt:hover{color:var(--accent)}
|
||||
|
||||
/* SPEC */
|
||||
.spec-wrap{display:grid;grid-template-columns:1.3fr 1fr;gap:64px;align-items:start}
|
||||
.spec-visual{position:relative;aspect-ratio:4/5;background:var(--paper);border:1px solid var(--line);overflow:hidden}
|
||||
.spec-visual img{width:100%;height:100%;object-fit:cover;filter:hue-rotate(220deg) saturate(.9) contrast(1.05) brightness(.7)}
|
||||
.spec-visual::before{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(5,6,15,0) 50%,rgba(5,6,15,.85) 100%);z-index:2}
|
||||
.spec-visual .label{position:absolute;left:20px;bottom:18px;z-index:3;font-size:11px;letter-spacing:0.2em;color:var(--accent)}
|
||||
.spec-table{width:100%}
|
||||
.spec-row{display:grid;grid-template-columns:140px 1fr;padding:14px 0;border-bottom:1px dashed var(--line);font-size:12px;align-items:baseline}
|
||||
.spec-row b{color:var(--dim);font-weight:400;letter-spacing:0.16em;font-size:11px}
|
||||
.spec-row span{color:var(--fg)}
|
||||
.spec-row .pt{cursor:default}
|
||||
|
||||
/* PRICING */
|
||||
.price-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
|
||||
.price{border:1px solid var(--line);padding:32px 26px;background:var(--paper);transition:.3s;display:flex;flex-direction:column}
|
||||
.price:hover{border-color:var(--accent);box-shadow:0 12px 32px -16px rgba(139,233,255,.4)}
|
||||
.price.featured{border-color:var(--accent);background:#0f1233;border-width:1px;box-shadow:0 0 0 1px var(--accent) inset, 0 0 32px -8px rgba(139,233,255,.3)}
|
||||
.price h4{font-size:13px;letter-spacing:0.2em;color:var(--dim);margin-bottom:18px;font-weight:400}
|
||||
.price .num{font-size:42px;font-weight:300;letter-spacing:0.02em;margin-bottom:6px;color:var(--fg)}
|
||||
.price .num small{font-size:13px;color:var(--dim);letter-spacing:0.1em;margin-left:6px}
|
||||
.price .desc{color:var(--dim);font-size:12px;margin-bottom:24px;line-height:1.6}
|
||||
.price ul{list-style:none;font-size:12px;color:var(--fg);line-height:2;margin-bottom:28px;flex:1}
|
||||
.price ul li::before{content:"+ ";color:var(--accent);margin-right:4px}
|
||||
.price .btn{display:block;text-align:center;padding:12px;border:1px solid var(--fg);font-size:11px;letter-spacing:0.2em;transition:.25s;color:var(--fg)}
|
||||
.price .btn:hover{background:var(--fg);color:var(--bg);border-color:var(--fg)}
|
||||
.price.featured .btn{background:var(--accent);color:var(--bg);border-color:var(--accent)}
|
||||
.price.featured .btn:hover{background:var(--accent2);border-color:var(--accent2);color:var(--bg)}
|
||||
|
||||
/* INFO */
|
||||
.info{text-align:center;padding:120px 24px}
|
||||
.info-big{font-size:clamp(36px,6vw,76px);font-weight:300;line-height:1.1;letter-spacing:0.02em;margin-bottom:36px}
|
||||
.info-big .em{font-style:italic;color:var(--accent)}
|
||||
.info-links{display:flex;gap:28px;justify-content:center;flex-wrap:wrap;margin-top:28px;font-size:12px;letter-spacing:0.2em}
|
||||
.info-links a{padding:14px 28px;border:1px solid var(--fg);transition:all .25s;color:var(--fg)}
|
||||
.info-links a:hover{background:var(--accent);color:var(--bg);border-color:var(--accent)}
|
||||
.info-orbit{margin:0 auto 48px;width:280px;height:280px;position:relative;color:var(--dim);font-size:9px;line-height:9px;white-space:pre;font-family:monospace;display:flex;align-items:center;justify-content:center}
|
||||
#info-ascii{color:var(--accent);opacity:1;font-weight:700}
|
||||
|
||||
/* PLATFORMS */
|
||||
.platforms{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-top:24px;font-size:11px;letter-spacing:0.18em;color:var(--dim)}
|
||||
.platforms span{padding:6px 14px;border:1px solid var(--line)}
|
||||
|
||||
/* FOOTER */
|
||||
.footer{padding:24px 32px;display:flex;justify-content:space-between;font-size:10px;letter-spacing:0.18em;color:var(--dim);border-top:1px solid var(--line);flex-wrap:wrap;gap:12px;background:var(--paper)}
|
||||
|
||||
/* HERO 后景柔光遮罩,仅在中心给标题留出可读区,四周让 ASCII 充分显现 */
|
||||
.hero::after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:min(82vw,1100px);height:min(58vh,520px);background:radial-gradient(ellipse at center,rgba(5,6,15,.85) 0%,rgba(5,6,15,.6) 40%,rgba(5,6,15,0) 80%);pointer-events:none;z-index:1;border-radius:50%}
|
||||
|
||||
@media(max-width:900px){
|
||||
.grid{grid-template-columns:1fr 1fr}
|
||||
.intro-grid{grid-template-columns:1fr}
|
||||
.spec-wrap,.showcase{grid-template-columns:1fr;gap:32px}
|
||||
.price-grid{grid-template-columns:1fr}
|
||||
.sec{padding:64px 24px}
|
||||
.header{padding:14px 18px}
|
||||
}
|
||||
@media(max-width:560px){
|
||||
.grid{grid-template-columns:1fr}
|
||||
#ascii-stage{font-size:7px;line-height:7px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- HEADER · SWAP-2: logo + 5 项导航 -->
|
||||
<div class="header">
|
||||
<div class="header-logo"><a href="#top">{{LOGO_TEXT}}</a></div>
|
||||
<nav class="nav">
|
||||
<ul>
|
||||
<li><a href="#features">{{NAV_1}}</a></li>
|
||||
<li><a href="#showcase">{{NAV_2}}</a></li>
|
||||
<li><a href="#spec">{{NAV_3}}</a></li>
|
||||
<li><a href="#price">{{NAV_4}}</a></li>
|
||||
<li><a href="#info">{{NAV_5}}</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- HEY · 角落问候,可改成产品 slogan -->
|
||||
<div class="here">{{GREETING}}<br><span class="blink">▮</span> 现在是 凌晨 03:14</div>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="hero" id="top">
|
||||
<pre id="ascii-nebula" class="ascii-layer"></pre>
|
||||
<pre id="ascii-stars" class="ascii-layer"></pre>
|
||||
<pre id="ascii-planet" class="ascii-layer"></pre>
|
||||
<pre id="ascii-asteroids" class="ascii-layer"></pre>
|
||||
<pre id="ascii-comets" class="ascii-layer"></pre>
|
||||
<div class="hero-overlay">
|
||||
<div class="brand-wrap-outer">
|
||||
<!-- SWAP-3: 产品英文名的 5-6 行 ANSI Shadow ASCII 字模。
|
||||
字模生成参考 patorjk.com/software/taag (字体 ANSI Shadow)。
|
||||
最长支持 ~10 字母;超长名缩写或换更窄的字体。 -->
|
||||
<pre class="ascii-title lg">
|
||||
{{ASCII_TITLE_LINE_1}}
|
||||
{{ASCII_TITLE_LINE_2}}
|
||||
{{ASCII_TITLE_LINE_3}}
|
||||
{{ASCII_TITLE_LINE_4}}
|
||||
{{ASCII_TITLE_LINE_5}}
|
||||
{{ASCII_TITLE_LINE_6}}
|
||||
</pre>
|
||||
<!-- SWAP-4a: 中文 1-2 字大字(会渲染为彩色网点字) -->
|
||||
<div class="brand-cn-sub">{{PRODUCT_CN_BIG}}</div>
|
||||
</div>
|
||||
<!-- SWAP-4b: hero 副标语,30-50 字,每字之间空格让节奏更慢 -->
|
||||
<div class="hero-sub" style="margin-top:28px">{{HERO_TAGLINE}}</div>
|
||||
<!-- SWAP-4c: 3 个 meta — 版本 / 类型 / 平台 -->
|
||||
<div class="hero-meta">
|
||||
<span>{{META_LABEL_1}} <b>{{META_VALUE_1}}</b></span>
|
||||
<span>{{META_LABEL_2}} <b>{{META_VALUE_2}}</b></span>
|
||||
<span>{{META_LABEL_3}} <b>{{META_VALUE_3}}</b></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-cue">↓ 向 下 滚 动 探 索 ↓</div>
|
||||
</section>
|
||||
|
||||
<!-- INTRO · SWAP-5: 关于段落 + 3 个亮点格 -->
|
||||
<section class="sec">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ❖ ═══</pre>关于</h2><div class="idx">[ 01 / 05 ]</div></div>
|
||||
<div class="intro">
|
||||
<p class="intro-text">
|
||||
{{ABOUT_PARAGRAPH_HTML}}
|
||||
<!-- 段落里要撒 3-5 个 <span class="pt" data-text="原文">原文</span>
|
||||
做 hover 字符乱码动效,2-3 个 <span class="em">关键词</span> 做色字强调 -->
|
||||
</p>
|
||||
<div class="intro-grid">
|
||||
<div class="cell"><b>✦ {{HIGHLIGHT_1_TITLE}}</b>{{HIGHLIGHT_1_DESC}}</div>
|
||||
<div class="cell"><b>✦ {{HIGHLIGHT_2_TITLE}}</b>{{HIGHLIGHT_2_DESC}}</div>
|
||||
<div class="cell"><b>✦ {{HIGHLIGHT_3_TITLE}}</b>{{HIGHLIGHT_3_DESC}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES GRID · SWAP-6: 6 张功能卡片 + SWAP-7: 4-5 个分类筛选 -->
|
||||
<section class="sec" id="features">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ❍ ═══</pre>{{FEATURES_TITLE}}</h2><div class="idx">[ 02 / 05 ]</div></div>
|
||||
<div class="filter">
|
||||
<a class="tag active" data-cat="all"><span class="dot"></span>全部</a>
|
||||
<a class="tag" data-cat="{{CAT_1_KEY}}"><span class="dot"></span>{{CAT_1_LABEL}}</a>
|
||||
<a class="tag" data-cat="{{CAT_2_KEY}}"><span class="dot"></span>{{CAT_2_LABEL}}</a>
|
||||
<a class="tag" data-cat="{{CAT_3_KEY}}"><span class="dot"></span>{{CAT_3_LABEL}}</a>
|
||||
<a class="tag" data-cat="{{CAT_4_KEY}}"><span class="dot"></span>{{CAT_4_LABEL}}</a>
|
||||
</div>
|
||||
<div class="grid" id="grid">
|
||||
|
||||
<!-- 卡片模板:data-cat 必须用上面 4 个 key 之一(可空格分隔多个) -->
|
||||
<div class="card" data-cat="{{CARD_1_CATS}}">
|
||||
<div class="card-img">
|
||||
<img src="{{CARD_1_IMG_URL}}" alt="{{CARD_1_TITLE}}">
|
||||
<pre class="ascii-overlay">{{CARD_1_ASCII}}</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="{{CARD_1_TITLE}}">{{CARD_1_TITLE}}</span><small>{{CARD_1_SUB}}</small></div>
|
||||
<div class="card-num">/01</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>{{CARD_1_TAG_1}}</span><span>{{CARD_1_TAG_2}}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="{{CARD_2_CATS}}">
|
||||
<div class="card-img">
|
||||
<img src="{{CARD_2_IMG_URL}}" alt="{{CARD_2_TITLE}}">
|
||||
<pre class="ascii-overlay">{{CARD_2_ASCII}}</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="{{CARD_2_TITLE}}">{{CARD_2_TITLE}}</span><small>{{CARD_2_SUB}}</small></div>
|
||||
<div class="card-num">/02</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>{{CARD_2_TAG_1}}</span><span>{{CARD_2_TAG_2}}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="{{CARD_3_CATS}}">
|
||||
<div class="card-img">
|
||||
<img src="{{CARD_3_IMG_URL}}" alt="{{CARD_3_TITLE}}">
|
||||
<pre class="ascii-overlay">{{CARD_3_ASCII}}</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="{{CARD_3_TITLE}}">{{CARD_3_TITLE}}</span><small>{{CARD_3_SUB}}</small></div>
|
||||
<div class="card-num">/03</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>{{CARD_3_TAG_1}}</span><span>{{CARD_3_TAG_2}}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="{{CARD_4_CATS}}">
|
||||
<div class="card-img">
|
||||
<img src="{{CARD_4_IMG_URL}}" alt="{{CARD_4_TITLE}}">
|
||||
<pre class="ascii-overlay">{{CARD_4_ASCII}}</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="{{CARD_4_TITLE}}">{{CARD_4_TITLE}}</span><small>{{CARD_4_SUB}}</small></div>
|
||||
<div class="card-num">/04</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>{{CARD_4_TAG_1}}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="{{CARD_5_CATS}}">
|
||||
<div class="card-img">
|
||||
<img src="{{CARD_5_IMG_URL}}" alt="{{CARD_5_TITLE}}">
|
||||
<pre class="ascii-overlay">{{CARD_5_ASCII}}</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="{{CARD_5_TITLE}}">{{CARD_5_TITLE}}</span><small>{{CARD_5_SUB}}</small></div>
|
||||
<div class="card-num">/05</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>{{CARD_5_TAG_1}}</span><span>{{CARD_5_TAG_2}}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card" data-cat="{{CARD_6_CATS}}">
|
||||
<div class="card-img">
|
||||
<img src="{{CARD_6_IMG_URL}}" alt="{{CARD_6_TITLE}}">
|
||||
<pre class="ascii-overlay">{{CARD_6_ASCII}}</pre>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div class="card-title"><span class="pt" data-text="{{CARD_6_TITLE}}">{{CARD_6_TITLE}}</span><small>{{CARD_6_SUB}}</small></div>
|
||||
<div class="card-num">/06</div>
|
||||
</div>
|
||||
<div class="card-tags"><span>{{CARD_6_TAG_1}}</span><span>{{CARD_6_TAG_2}}</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SHOWCASE · SWAP-8: 2 块界面截图 + 文案 -->
|
||||
<section class="sec" id="showcase">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ◈ ═══</pre>{{SHOWCASE_TITLE}}</h2><div class="idx">[ 03 / 05 ]</div></div>
|
||||
|
||||
<div class="showcase">
|
||||
<div class="showcase-shot">
|
||||
<div class="bar"><i></i><i></i><i></i></div>
|
||||
<img src="{{SHOWCASE_1_IMG}}" alt="{{SHOWCASE_1_ALT}}">
|
||||
</div>
|
||||
<div class="showcase-text">
|
||||
<pre class="mini-ascii">{{SHOWCASE_1_MINI_ASCII}}</pre>
|
||||
<h3>{{SHOWCASE_1_HEADLINE_HTML}}</h3>
|
||||
<!-- headline 可用 <span class="em">关键词</span> 强调 -->
|
||||
<p>{{SHOWCASE_1_PARA_1_HTML}}</p>
|
||||
<p>{{SHOWCASE_1_PARA_2_HTML}}</p>
|
||||
<!-- 段落里撒 1-2 个 <span class="pt" data-text="...">...</span> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="showcase" style="margin-top:64px">
|
||||
<div class="showcase-text">
|
||||
<pre class="mini-ascii">{{SHOWCASE_2_MINI_ASCII}}</pre>
|
||||
<h3>{{SHOWCASE_2_HEADLINE_HTML}}</h3>
|
||||
<p>{{SHOWCASE_2_PARA_1_HTML}}</p>
|
||||
<p>{{SHOWCASE_2_PARA_2_HTML}}</p>
|
||||
</div>
|
||||
<div class="showcase-shot">
|
||||
<div class="bar"><i></i><i></i><i></i></div>
|
||||
<img src="{{SHOWCASE_2_IMG}}" alt="{{SHOWCASE_2_ALT}}">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SPEC · SWAP-9: 大图 + 10 行规格表 -->
|
||||
<section class="sec" id="spec">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ✦ ═══</pre>{{SPEC_TITLE}}</h2><div class="idx">[ 04 / 05 ]</div></div>
|
||||
<div class="spec-wrap">
|
||||
<div class="spec-visual">
|
||||
<img src="{{SPEC_IMG}}" alt="{{SPEC_ALT}}">
|
||||
<div class="label">✦ {{SPEC_LABEL}}</div>
|
||||
</div>
|
||||
<div class="spec-table">
|
||||
<div class="spec-row"><b>{{SPEC_1_KEY}}</b><span class="pt" data-text="{{SPEC_1_VAL}}">{{SPEC_1_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_2_KEY}}</b><span class="pt" data-text="{{SPEC_2_VAL}}">{{SPEC_2_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_3_KEY}}</b><span class="pt" data-text="{{SPEC_3_VAL}}">{{SPEC_3_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_4_KEY}}</b><span class="pt" data-text="{{SPEC_4_VAL}}">{{SPEC_4_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_5_KEY}}</b><span class="pt" data-text="{{SPEC_5_VAL}}">{{SPEC_5_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_6_KEY}}</b><span class="pt" data-text="{{SPEC_6_VAL}}">{{SPEC_6_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_7_KEY}}</b><span class="pt" data-text="{{SPEC_7_VAL}}">{{SPEC_7_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_8_KEY}}</b><span class="pt" data-text="{{SPEC_8_VAL}}">{{SPEC_8_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_9_KEY}}</b><span class="pt" data-text="{{SPEC_9_VAL}}">{{SPEC_9_VAL}}</span></div>
|
||||
<div class="spec-row"><b>{{SPEC_10_KEY}}</b><span class="pt" data-text="{{SPEC_10_VAL}}">{{SPEC_10_VAL}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PRICING · SWAP-10: 3 档订阅,中间档加 .featured -->
|
||||
<section class="sec" id="price">
|
||||
<div class="sec-head"><h2><pre class="sec-ascii">═══ ❑ ═══</pre>{{PRICING_TITLE}}</h2><div class="idx">[ 05 / 05 ]</div></div>
|
||||
<div class="price-grid">
|
||||
|
||||
<div class="price">
|
||||
<h4>{{PRICE_1_NAME}}</h4>
|
||||
<div class="num">{{PRICE_1_NUM}}<small>{{PRICE_1_UNIT}}</small></div>
|
||||
<p class="desc">{{PRICE_1_DESC}}</p>
|
||||
<ul>
|
||||
<li>{{PRICE_1_FEAT_1}}</li>
|
||||
<li>{{PRICE_1_FEAT_2}}</li>
|
||||
<li>{{PRICE_1_FEAT_3}}</li>
|
||||
<li>{{PRICE_1_FEAT_4}}</li>
|
||||
<li>{{PRICE_1_FEAT_5}}</li>
|
||||
</ul>
|
||||
<a class="btn pt" data-text="{{PRICE_1_BTN}}">{{PRICE_1_BTN}}</a>
|
||||
</div>
|
||||
|
||||
<div class="price featured">
|
||||
<h4>★ {{PRICE_2_NAME}}</h4>
|
||||
<div class="num">{{PRICE_2_NUM}}<small>{{PRICE_2_UNIT}}</small></div>
|
||||
<p class="desc">{{PRICE_2_DESC}}</p>
|
||||
<ul>
|
||||
<li>{{PRICE_2_FEAT_1}}</li>
|
||||
<li>{{PRICE_2_FEAT_2}}</li>
|
||||
<li>{{PRICE_2_FEAT_3}}</li>
|
||||
<li>{{PRICE_2_FEAT_4}}</li>
|
||||
<li>{{PRICE_2_FEAT_5}}</li>
|
||||
<li>{{PRICE_2_FEAT_6}}</li>
|
||||
</ul>
|
||||
<a class="btn pt" data-text="{{PRICE_2_BTN}}">{{PRICE_2_BTN}}</a>
|
||||
</div>
|
||||
|
||||
<div class="price">
|
||||
<h4>{{PRICE_3_NAME}}</h4>
|
||||
<div class="num">{{PRICE_3_NUM}}<small>{{PRICE_3_UNIT}}</small></div>
|
||||
<p class="desc">{{PRICE_3_DESC}}</p>
|
||||
<ul>
|
||||
<li>{{PRICE_3_FEAT_1}}</li>
|
||||
<li>{{PRICE_3_FEAT_2}}</li>
|
||||
<li>{{PRICE_3_FEAT_3}}</li>
|
||||
<li>{{PRICE_3_FEAT_4}}</li>
|
||||
<li>{{PRICE_3_FEAT_5}}</li>
|
||||
<li>{{PRICE_3_FEAT_6}}</li>
|
||||
</ul>
|
||||
<a class="btn pt" data-text="{{PRICE_3_BTN}}">{{PRICE_3_BTN}}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- INFO / CTA · SWAP-11: 大字 CTA + 3 按钮 + 平台标签 -->
|
||||
<section class="sec info" id="info">
|
||||
<div class="info-orbit"><pre id="info-ascii"></pre></div>
|
||||
<div class="info-big">{{CTA_BIG_HTML}}</div>
|
||||
<!-- CTA_BIG 可用 <em>...</em> 强调,<br> 换行 -->
|
||||
<div class="hero-sub">{{CTA_SUB}}</div>
|
||||
<div class="info-links">
|
||||
<a class="pt" data-text="{{CTA_BTN_1}}">{{CTA_BTN_1}}</a>
|
||||
<a class="pt" data-text="{{CTA_BTN_2}}">{{CTA_BTN_2}}</a>
|
||||
<a class="pt" data-text="{{CTA_BTN_3}}">{{CTA_BTN_3}}</a>
|
||||
</div>
|
||||
<div class="platforms">
|
||||
<span>★ {{PLATFORM_1}}</span><span>★ {{PLATFORM_2}}</span><span>★ {{PLATFORM_3}}</span><span>★ {{PLATFORM_4}}</span><span>★ {{PLATFORM_5}}</span><span>★ {{PLATFORM_6}}</span><span>★ {{PLATFORM_7}}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOOTER · SWAP-12 -->
|
||||
<div class="footer">
|
||||
<span>{{FOOTER_LEFT}}</span>
|
||||
<span>{{FOOTER_RIGHT}}</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ============================================================
|
||||
1. HERO ASCII :旋转球体 + 行星轨道 + 星点场
|
||||
============================================================ */
|
||||
(function(){
|
||||
const elNeb = document.getElementById('ascii-nebula');
|
||||
const elStar = document.getElementById('ascii-stars');
|
||||
const elPlanet = document.getElementById('ascii-planet');
|
||||
const elAst = document.getElementById('ascii-asteroids');
|
||||
const elComet = document.getElementById('ascii-comets');
|
||||
if(!elPlanet) return;
|
||||
let cols, rows, t = 0;
|
||||
let stars = [];
|
||||
let asteroids = [];
|
||||
let comets = [];
|
||||
let nebulae = [];
|
||||
|
||||
// ============================================================
|
||||
// SWAP-13 · ASCII 场景资产(motif 可替换处)
|
||||
// 想换主题(咖啡/森林/音乐/代码/齿轮/海洋…),只改这个 SCENE 对象。
|
||||
// 渲染逻辑不要动 — 只要每个键的"形状个数 / 字符个数"保持合理即可。
|
||||
// 详见 SKILL.md「ASCII 场景 motif 库」。
|
||||
// ============================================================
|
||||
const SCENE = {
|
||||
// 漂浮的不规则物体(默认:小行星)。每个元素是多行 string 数组
|
||||
floaters: [
|
||||
[
|
||||
" .-~~-. ",
|
||||
" ( o..o )",
|
||||
" `-~~-' "
|
||||
],
|
||||
[
|
||||
" _.--._ ",
|
||||
" / .o \\ ",
|
||||
" \\ o. / ",
|
||||
" `~--~' "
|
||||
],
|
||||
[
|
||||
" .-=-. ",
|
||||
" /,o o.\\ ",
|
||||
" \\ `-' / ",
|
||||
" `~~-' "
|
||||
],
|
||||
[
|
||||
" _.+~+._ ",
|
||||
"/ . o \\",
|
||||
"\\ o . o/",
|
||||
" `~--~~' "
|
||||
],
|
||||
[
|
||||
" .o0o. ",
|
||||
" 0o..oO ",
|
||||
" `OoO' "
|
||||
],
|
||||
[
|
||||
" ,-~^~-. ",
|
||||
"( :*. * )",
|
||||
" `-,_,-' "
|
||||
]
|
||||
],
|
||||
// 中央旋转球体的明暗梯度字符(暗→亮)。默认:太空岩石球
|
||||
centerShade: " .,:;-~+*=%#@█",
|
||||
// 弥漫底层的"气体云"字符梯度(淡→浓)。默认:星云尘埃
|
||||
diffuseChars: "·∙∶⋮⋯░▒",
|
||||
// 闪烁的散点:稀有 / 中频 / 普通频。默认:星
|
||||
sparkRare: "★", // ~4% 概率出现
|
||||
sparkBright: "✦", // ~15%
|
||||
sparkDim: ".∙", // 其余对半分
|
||||
// 飞掠物:头 + 拖尾从近到远 5 段。默认:彗星
|
||||
streakHead: "☄",
|
||||
streakTailNear:"*",
|
||||
streakTailMid: "∙",
|
||||
streakTailFar: "·",
|
||||
streakTailEnd: ".",
|
||||
// 中央球体周围的 3 颗"卫星" + 轨道线点
|
||||
centerSats: "◉◎○", // 3 颗,由内向外
|
||||
orbitDots: ".∙•" // 3 圈轨道,由内向外
|
||||
};
|
||||
// ============================================================
|
||||
// END SWAP-13
|
||||
// ============================================================
|
||||
|
||||
|
||||
function reseed(){
|
||||
cols = Math.floor(window.innerWidth / 7);
|
||||
rows = Math.floor(window.innerHeight / 11);
|
||||
stars = [];
|
||||
const n = Math.floor(cols*rows*0.05);
|
||||
for(let i=0;i<n;i++){
|
||||
const dimChars = SCENE.sparkDim;
|
||||
stars.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows,
|
||||
z: Math.random()*0.9+0.1,
|
||||
c: Math.random()<.04?SCENE.sparkRare:(Math.random()<.15?SCENE.sparkBright:dimChars[Math.floor(Math.random()*dimChars.length)])
|
||||
});
|
||||
}
|
||||
asteroids = [];
|
||||
const aN = Math.max(4, Math.floor(Math.min(cols,rows)/14));
|
||||
for(let i=0;i<aN;i++){
|
||||
asteroids.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows,
|
||||
vx: (Math.random()-0.5)*0.08,
|
||||
vy: (Math.random()-0.5)*0.04,
|
||||
shape: SCENE.floaters[Math.floor(Math.random()*SCENE.floaters.length)],
|
||||
rot: Math.random()*Math.PI*2,
|
||||
rs: (Math.random()-0.5)*0.01
|
||||
});
|
||||
}
|
||||
comets = [];
|
||||
for(let i=0;i<2;i++){
|
||||
comets.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows*0.6,
|
||||
vx: 0.35 + Math.random()*0.25,
|
||||
vy: 0.12 + Math.random()*0.1,
|
||||
life: Math.random()*200
|
||||
});
|
||||
}
|
||||
nebulae = [];
|
||||
for(let i=0;i<3;i++){
|
||||
nebulae.push({
|
||||
x: Math.random()*cols,
|
||||
y: Math.random()*rows,
|
||||
r: 9 + Math.random()*15,
|
||||
seed: Math.random()*100
|
||||
});
|
||||
}
|
||||
}
|
||||
reseed();
|
||||
window.addEventListener('resize', reseed);
|
||||
|
||||
function newBuf(){
|
||||
const b = new Array(rows);
|
||||
for(let y=0;y<rows;y++) b[y] = new Array(cols).fill(' ');
|
||||
return b;
|
||||
}
|
||||
function flush(buf){ return buf.map(r=>r.join('')).join('\n'); }
|
||||
|
||||
function drawAsteroid(buf, ax, ay, shape){
|
||||
for(let i=0;i<shape.length;i++){
|
||||
const line = shape[i];
|
||||
const yy = Math.floor(ay) + i - Math.floor(shape.length/2);
|
||||
if(yy<0||yy>=rows) continue;
|
||||
for(let j=0;j<line.length;j++){
|
||||
const ch = line[j];
|
||||
if(ch===' ') continue;
|
||||
const xx = Math.floor(ax) + j - Math.floor(line.length/2);
|
||||
if(xx<0||xx>=cols) continue;
|
||||
buf[yy][xx] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chars = SCENE.centerShade;
|
||||
const nebChars = SCENE.diffuseChars;
|
||||
function render(){
|
||||
t += 0.012;
|
||||
const bufNeb = newBuf();
|
||||
const bufStar = newBuf();
|
||||
const bufPlanet = newBuf();
|
||||
const bufAst = newBuf();
|
||||
const bufComet = newBuf();
|
||||
|
||||
// 星云气体
|
||||
for(const n of nebulae){
|
||||
const cx = n.x + Math.sin(t*0.2 + n.seed)*3;
|
||||
const cy = n.y + Math.cos(t*0.15 + n.seed)*2;
|
||||
for(let dy=-n.r; dy<=n.r; dy++){
|
||||
for(let dx=-n.r*1.6; dx<=n.r*1.6; dx++){
|
||||
const xx = Math.floor(cx+dx);
|
||||
const yy = Math.floor(cy+dy);
|
||||
if(xx<0||xx>=cols||yy<0||yy>=rows) continue;
|
||||
const d = Math.sqrt((dx/1.6)*(dx/1.6) + dy*dy);
|
||||
if(d > n.r) continue;
|
||||
const noise = Math.sin(xx*0.3 + n.seed)*Math.cos(yy*0.4 - n.seed*0.7) + Math.sin(t*0.5+n.seed);
|
||||
const intensity = (1 - d/n.r) * (0.5 + 0.5*noise);
|
||||
if(intensity > 0.55){
|
||||
const ci = Math.min(nebChars.length-1, Math.floor(intensity * nebChars.length));
|
||||
bufNeb[yy][xx] = nebChars[ci];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 闪烁星点
|
||||
for(const s of stars){
|
||||
const xi = Math.floor((s.x + Math.sin(t*0.3 + s.y)*0.3 + cols)%cols);
|
||||
const yi = Math.floor(s.y);
|
||||
if(yi>=0 && yi<rows){
|
||||
const tw = (Math.sin(t*2 + s.x*0.7 + s.y*0.3)+1)*0.5;
|
||||
if(tw>0.55 || s.c===SCENE.sparkRare || s.c===SCENE.sparkBright) bufStar[yi][xi] = s.c;
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转行星
|
||||
const cx = cols/2, cy = rows/2;
|
||||
const R = Math.min(cols,rows*2)*0.18;
|
||||
const aspect = 0.5;
|
||||
for(let phi=0; phi<Math.PI; phi+=0.06){
|
||||
for(let theta=0; theta<2*Math.PI; theta+=0.04){
|
||||
const x3 = R*Math.sin(phi)*Math.cos(theta + t);
|
||||
const y3 = R*Math.cos(phi);
|
||||
const z3 = R*Math.sin(phi)*Math.sin(theta + t);
|
||||
const lx = Math.cos(t*0.6), ly = -0.3, lz = Math.sin(t*0.6);
|
||||
const nx = Math.sin(phi)*Math.cos(theta + t);
|
||||
const ny = Math.cos(phi);
|
||||
const nz = Math.sin(phi)*Math.sin(theta + t);
|
||||
const lum = nx*lx + ny*ly + nz*lz;
|
||||
if(z3 < 0) continue;
|
||||
const sx = Math.floor(cx + x3/aspect);
|
||||
const sy = Math.floor(cy + y3);
|
||||
if(sx<0||sx>=cols||sy<0||sy>=rows) continue;
|
||||
const idx = Math.max(0, Math.min(chars.length-1, Math.floor((lum+0.2)*chars.length*0.9)));
|
||||
bufPlanet[sy][sx] = chars[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// 行星轨道与卫星
|
||||
for(let i=0; i<3; i++){
|
||||
const orbR = R*(1.6 + i*0.5);
|
||||
const tilt = 0.35 + i*0.1;
|
||||
for(let a=0; a<2*Math.PI; a+=0.04){
|
||||
const ox = orbR*Math.cos(a + t*(0.4-i*0.1));
|
||||
const oy = orbR*Math.sin(a + t*(0.4-i*0.1))*tilt;
|
||||
const sx = Math.floor(cx + ox/aspect);
|
||||
const sy = Math.floor(cy + oy);
|
||||
if(sx<0||sx>=cols||sy<0||sy>=rows) continue;
|
||||
if(bufPlanet[sy][sx]===' ') bufPlanet[sy][sx] = SCENE.orbitDots[i] || SCENE.orbitDots[SCENE.orbitDots.length-1];
|
||||
}
|
||||
const a = t*(0.7-i*0.18);
|
||||
const ox = orbR*Math.cos(a);
|
||||
const oy = orbR*Math.sin(a)*tilt;
|
||||
const sx = Math.floor(cx + ox/aspect);
|
||||
const sy = Math.floor(cy + oy);
|
||||
if(sx>=0&&sx<cols&&sy>=0&&sy<rows) bufPlanet[sy][sx] = SCENE.centerSats[i] || SCENE.centerSats[SCENE.centerSats.length-1];
|
||||
}
|
||||
|
||||
// 彗星拖尾
|
||||
for(const c of comets){
|
||||
c.x += c.vx; c.y += c.vy; c.life++;
|
||||
if(c.x>cols+10 || c.y>rows+5){
|
||||
c.x = -10; c.y = Math.random()*rows*0.5;
|
||||
c.life = 0;
|
||||
}
|
||||
const tail = 16;
|
||||
for(let k=0;k<tail;k++){
|
||||
const tx = Math.floor(c.x - k*c.vx*1.5);
|
||||
const ty = Math.floor(c.y - k*c.vy*1.5);
|
||||
if(tx<0||tx>=cols||ty<0||ty>=rows) continue;
|
||||
const tch = k===0?SCENE.streakHead:(k<3?SCENE.streakTailNear:(k<7?SCENE.streakTailMid:(k<12?SCENE.streakTailFar:SCENE.streakTailEnd)));
|
||||
bufComet[ty][tx] = tch;
|
||||
}
|
||||
}
|
||||
|
||||
// 漂浮的小行星
|
||||
for(const a of asteroids){
|
||||
a.x += a.vx; a.y += a.vy; a.rot += a.rs;
|
||||
if(a.x<-6) a.x = cols+6;
|
||||
if(a.x>cols+6) a.x = -6;
|
||||
if(a.y<-4) a.y = rows+4;
|
||||
if(a.y>rows+4) a.y = -4;
|
||||
const wob = Math.sin(t*0.8 + a.rot)*0.6;
|
||||
drawAsteroid(bufAst, a.x + wob, a.y + Math.cos(t*0.6+a.rot)*0.4, a.shape);
|
||||
}
|
||||
|
||||
elNeb.textContent = flush(bufNeb);
|
||||
elStar.textContent = flush(bufStar);
|
||||
elPlanet.textContent = flush(bufPlanet);
|
||||
elAst.textContent = flush(bufAst);
|
||||
elComet.textContent = flush(bufComet);
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
render();
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
2. INFO 区小型轨道
|
||||
============================================================ */
|
||||
(function(){
|
||||
const el = document.getElementById('info-ascii');
|
||||
if(!el) return;
|
||||
const W=44, H=22;
|
||||
let t=0;
|
||||
// 小行星带的随机位置
|
||||
const belt = [];
|
||||
for(let i=0;i<14;i++){
|
||||
belt.push({
|
||||
a: Math.random()*Math.PI*2,
|
||||
r: 8.5 + Math.random()*1.6,
|
||||
sp: 0.5 + Math.random()*0.4,
|
||||
ch: '✦◆◈*·•'[Math.floor(Math.random()*6)]
|
||||
});
|
||||
}
|
||||
function tick(){
|
||||
t+=0.04;
|
||||
const buf = Array.from({length:H},()=>new Array(W).fill(' '));
|
||||
const cx=W/2, cy=H/2;
|
||||
|
||||
// 散落的星点背景
|
||||
for(let i=0;i<24;i++){
|
||||
const x = Math.floor((Math.sin(i*7.3)+1)*W/2);
|
||||
const y = Math.floor((Math.cos(i*5.1)+1)*H/2);
|
||||
const tw = (Math.sin(t*1.5 + i)+1)*0.5;
|
||||
if(x>=0&&x<W&&y>=0&&y<H && tw>0.6) buf[y][x]='.';
|
||||
}
|
||||
|
||||
// 行星轨道
|
||||
for(let i=0;i<4;i++){
|
||||
const r = 3+i*2.6;
|
||||
for(let a=0;a<Math.PI*2;a+=0.18){
|
||||
const x = Math.floor(cx + r*Math.cos(a)*1.0);
|
||||
const y = Math.floor(cy + r*Math.sin(a)*0.5);
|
||||
if(x>=0&&x<W&&y>=0&&y<H && buf[y][x]===' ') buf[y][x]='.';
|
||||
}
|
||||
const a = t*(1-i*0.18);
|
||||
const x = Math.floor(cx + r*Math.cos(a));
|
||||
const y = Math.floor(cy + r*Math.sin(a)*0.5);
|
||||
if(x>=0&&x<W&&y>=0&&y<H) buf[y][x]='★';
|
||||
}
|
||||
|
||||
// 外侧小行星带
|
||||
for(const b of belt){
|
||||
const ang = b.a + t*b.sp*0.3;
|
||||
const x = Math.floor(cx + b.r*Math.cos(ang));
|
||||
const y = Math.floor(cy + b.r*Math.sin(ang)*0.5);
|
||||
if(x>=0&&x<W&&y>=0&&y<H) buf[y][x]=b.ch;
|
||||
}
|
||||
|
||||
// 中心恒星与日冕
|
||||
if(buf[Math.floor(cy)]) buf[Math.floor(cy)][Math.floor(cx)]='◉';
|
||||
const halo = [[-1,0,'-'],[1,0,'-'],[0,-1,'|'],[0,1,'|']];
|
||||
for(const [dx,dy,ch] of halo){
|
||||
const x = Math.floor(cx)+dx, y = Math.floor(cy)+dy;
|
||||
if(x>=0&&x<W&&y>=0&&y<H && buf[y][x]===' ') buf[y][x]=ch;
|
||||
}
|
||||
|
||||
el.textContent = buf.map(r=>r.join('')).join('\n');
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
tick();
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
3. PROTEXT :文本乱码-解码效果
|
||||
悬停或进入视口时,文字先打乱再逐字"解密"显现
|
||||
============================================================ */
|
||||
(function(){
|
||||
const glyphs = "★☆✶✦✧◉◎○●∙•~≈≋▁▂▃▄▅▆▇█ABCDEFGHJKLMNPQRSTUVWXYZ0123456789@#%&*";
|
||||
const RAND = ()=> glyphs[Math.floor(Math.random()*glyphs.length)];
|
||||
|
||||
function scramble(el){
|
||||
if(el.dataset.scrLock==='1') return;
|
||||
el.dataset.scrLock='1';
|
||||
el.classList.add('scrambling');
|
||||
const target = el.dataset.text || el.textContent;
|
||||
const chars = [...target];
|
||||
const len = chars.length;
|
||||
let frame = 0;
|
||||
const total = 16;
|
||||
const step = ()=>{
|
||||
frame++;
|
||||
const out = chars.map((ch,i)=>{
|
||||
if(ch===' '||ch==='\n') return ch;
|
||||
const reveal = Math.floor((frame/total)*len*1.2);
|
||||
if(i < reveal) return ch;
|
||||
return RAND();
|
||||
}).join('');
|
||||
el.textContent = out;
|
||||
if(frame < total){
|
||||
setTimeout(step, 40);
|
||||
} else {
|
||||
el.textContent = target;
|
||||
el.classList.remove('scrambling');
|
||||
el.dataset.scrLock='0';
|
||||
}
|
||||
};
|
||||
step();
|
||||
}
|
||||
|
||||
document.querySelectorAll('.pt').forEach(el=>{
|
||||
if(!el.dataset.text) el.dataset.text = el.textContent.trim();
|
||||
el.addEventListener('mouseenter', ()=>scramble(el));
|
||||
});
|
||||
|
||||
const io = new IntersectionObserver((entries)=>{
|
||||
entries.forEach(e=>{
|
||||
if(e.isIntersecting){
|
||||
scramble(e.target);
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
});
|
||||
},{threshold:0.3});
|
||||
document.querySelectorAll('.intro-text .pt, .spec-row .pt, .showcase-text .pt').forEach(el=>io.observe(el));
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
4. 标签筛选
|
||||
============================================================ */
|
||||
(function(){
|
||||
const tags = document.querySelectorAll('.tag');
|
||||
const cards = document.querySelectorAll('#grid .card');
|
||||
tags.forEach(tag=>{
|
||||
tag.addEventListener('click', ()=>{
|
||||
tags.forEach(t=>t.classList.remove('active'));
|
||||
tag.classList.add('active');
|
||||
const cat = tag.dataset.cat;
|
||||
cards.forEach(c=>{
|
||||
if(cat==='all' || c.dataset.cat.split(' ').includes(cat)){
|
||||
c.classList.remove('hidden');
|
||||
} else {
|
||||
c.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
5. 平滑滚动
|
||||
============================================================ */
|
||||
document.querySelectorAll('a[href^="#"]').forEach(a=>{
|
||||
a.addEventListener('click', e=>{
|
||||
const id = a.getAttribute('href');
|
||||
if(id.length>1){
|
||||
const tgt = document.querySelector(id);
|
||||
if(tgt){ e.preventDefault(); tgt.scrollIntoView({behavior:'smooth',block:'start'}); }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ============================================================
|
||||
6. 实时时钟
|
||||
============================================================ */
|
||||
(function(){
|
||||
const el = document.querySelector('.here');
|
||||
if(!el) return;
|
||||
function fmt(n){return n<10?'0'+n:n}
|
||||
function tick(){
|
||||
const d = new Date();
|
||||
el.innerHTML = '{{GREETING}}<br><span class="blink">▮</span> 现在是 ' + fmt(d.getHours())+':'+fmt(d.getMinutes())+':'+fmt(d.getSeconds());
|
||||
setTimeout(tick, 1000);
|
||||
}
|
||||
tick();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user