283 lines
8.9 KiB
CSS
283 lines
8.9 KiB
CSS
/* 修改记录动画 —— 智能体持笔在 3D 倾斜纸上改写内容,
|
||
纸摊平后绿行抽出展开为红绿 diff。无边框无底色,融于星空;无 glow。 */
|
||
|
||
:root {
|
||
--bg-0: #0c1120;
|
||
--bg-1: #111830;
|
||
--bg-panel: #151c31;
|
||
--text-1: #eef1f8;
|
||
--text-2: #a7b0c4;
|
||
--text-3: #6b7488;
|
||
--line: rgba(168, 180, 210, 0.14);
|
||
--accent: #a9c2f0;
|
||
--accent-strong: #c1d4f6;
|
||
--danger: #d47b6b;
|
||
--ok: #7ba889;
|
||
/* 收盒文件:与编辑中的 3D 纸同款底色,边框用更亮的白边(叠放分层清晰) */
|
||
--file-paper: var(--bg-panel);
|
||
--file-line: rgba(238, 240, 246, 0.35);
|
||
--font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC",
|
||
"Noto Sans SC", "Helvetica Neue", "Microsoft YaHei", sans-serif;
|
||
--font-mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, monospace;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: var(--font-sans);
|
||
background: linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 60%);
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.stage { display: flex; flex-direction: column; align-items: center; gap: 18px; }
|
||
.hint { font-size: 13px; color: var(--text-3); }
|
||
|
||
/* ───────── 动画容器 ───────── */
|
||
|
||
.anim3-window {
|
||
width: 540px;
|
||
height: 380px;
|
||
}
|
||
|
||
.anim3-stage {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
user-select: none; /* 演示区禁止选中文本,避免鼠标划过时出现系统选中高亮(黑光) */
|
||
-webkit-user-select: none;
|
||
}
|
||
|
||
/* ───────── 智能体(右上) ───────── */
|
||
|
||
.writer {
|
||
position: absolute;
|
||
right: 58px;
|
||
top: 26px;
|
||
width: 84px;
|
||
height: 84px;
|
||
}
|
||
.writer-avatar {
|
||
display: block;
|
||
width: 84px;
|
||
height: 84px;
|
||
color: var(--text-1);
|
||
}
|
||
.writer-avatar .astrion-avatar { width: 100%; height: 100%; }
|
||
|
||
/* ───────── 笔(双层结构) ─────────
|
||
外层 pen-pos:JS 逐帧/过渡定位,使笔尖精确落在文字进度点上;
|
||
内层 svg:以笔尖为圆心旋转摇摆(transform-origin 在笔尖),
|
||
既有晃动,笔尖又不会脱离位置。 */
|
||
|
||
.pen-pos {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
z-index: 40; /* 最高层:高于 3D 纸、盒子(30)与堆叠纸(21~23),笔不能被挡 */
|
||
width: 40px;
|
||
height: 40px;
|
||
color: var(--accent);
|
||
will-change: transform;
|
||
transition: transform 0.45s cubic-bezier(.4,0,.2,1); /* 飞行/移动用过渡 */
|
||
}
|
||
.pen-pos.is-live {
|
||
transition: none; /* 写字逐帧驱动时关闭过渡 */
|
||
}
|
||
.writer-pen {
|
||
width: 40px;
|
||
height: 40px;
|
||
/* origin = 笔尖视觉触点(SVG (2.65,21.02) → 40px 盒内 (4.4,35.0)),与 nibRef 同点:
|
||
摇摆时笔尖钉住不动,只有笔身晃 */
|
||
transform-origin: 11% 87.6%;
|
||
}
|
||
.writer-pen.is-writing {
|
||
animation: pen-write 0.42s ease-in-out infinite;
|
||
}
|
||
/* 擦除:origin 切到笔尾橡皮触点(SVG (19.2,4.8) → 盒内 (32,8)),与 eraserRef 同点 */
|
||
.writer-pen.is-erasing {
|
||
transform-origin: 80% 20%;
|
||
}
|
||
@keyframes pen-write {
|
||
0%, 100% { transform: rotate(-5deg); }
|
||
50% { transform: rotate(6deg); }
|
||
}
|
||
|
||
/* ───────── 3D 倾斜的纸 ───────── */
|
||
/* perspective 让纸呈平行四边形(不倾斜就只是一条线) */
|
||
|
||
.paper3d {
|
||
position: absolute;
|
||
left: 280px;
|
||
top: 130px;
|
||
width: 192px;
|
||
height: 122px;
|
||
transform: perspective(760px) rotateX(42deg) rotateZ(-7deg);
|
||
transform-style: preserve-3d;
|
||
}
|
||
|
||
.paper-sheet {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: var(--bg-panel);
|
||
border: 1px solid var(--line);
|
||
border-radius: 6px;
|
||
padding: 18px 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
}
|
||
|
||
/* 内容行:横线代表一行内容(行宽由 JS 逐帧驱动,不用 transition;opacity 过渡供轮间换内容淡出) */
|
||
.pline {
|
||
position: relative;
|
||
height: 4px;
|
||
border-radius: 2px;
|
||
background: var(--text-3);
|
||
opacity: 0.75;
|
||
transition: opacity 0.28s ease;
|
||
}
|
||
.paper3d.is-refilling .pline { opacity: 0; }
|
||
/* 线右端锚点:0 尺寸,随线的 3D/倾斜变换,供 JS 实测真实右端点 */
|
||
.pline-end {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 50%;
|
||
width: 0;
|
||
height: 0;
|
||
margin-top: -0px; /* 点本身无尺寸,top:50% 即线厚中心 */
|
||
}
|
||
/* 写入行不再用绿色:与其他行同色(修改记录靠的是 diff 区,不是纸面变色) */
|
||
|
||
/* ───────── diff 区 ───────── */
|
||
|
||
.diff-view {
|
||
position: absolute;
|
||
left: 134px; /* 盒子水平中线(86+48),diff 中心对齐盒子中线 */
|
||
transform: translateX(-50%); /* 克隆切片内 transform 已被覆盖,不受影响 */
|
||
top: 0;
|
||
font-family: var(--font-mono);
|
||
font-size: 12.5px;
|
||
line-height: 1.5;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
z-index: 20; /* 盒内堆叠纸(21~23)之下——genie 时已隐藏,由切片接管 */
|
||
border-radius: 6px;
|
||
transition: background 0.25s ease;
|
||
}
|
||
/* genie 前:四行 diff 合为一张纸的底板(不加 outline:shorthand 不可过渡,会闪白框) */
|
||
.diff-view.is-sheet {
|
||
background: rgba(24, 30, 52, 0.92);
|
||
}
|
||
/* 轮间重置:禁用过渡,红绿底色/符号瞬时消失(否则恢复显示时会闪一下空白红绿块) */
|
||
.diff-view.no-fade { transition: none; }
|
||
.diff-view.no-fade .dline::before { transition: none; }
|
||
.diff-view.no-fade .dsign { transition: none; }
|
||
|
||
/* ───────── 收盒文件卡片(单元素裁切式 genie) ─────────
|
||
卡片全程同一个 DOM 元素:clip-path 多边形裁切(不压缩内容),
|
||
body 每帧定位到轮廓包围盒,文字矢量缩小——全程清晰无损 */
|
||
.file-flight {
|
||
position: absolute;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
transition: opacity 0.45s ease;
|
||
/* SVG 视口默认 hidden 会把描边外半侧(0.5px)裁掉——visible 让边缘描边完整 */
|
||
overflow: visible;
|
||
}
|
||
.file-flight.is-on { opacity: 1; }
|
||
/* SVG 卡片:path 轮廓即卡片外形——fill 纸色 + stroke 描边跟随轮廓(S 曲线斜边也有描边) */
|
||
.file-flight-shape {
|
||
fill: var(--file-paper);
|
||
stroke: var(--file-line);
|
||
stroke-width: 1;
|
||
}
|
||
.file-cover-name {
|
||
font-family: var(--font-mono);
|
||
font-size: 15px;
|
||
letter-spacing: 0.02em;
|
||
fill: var(--text-3); /* 和纸上线条同色 */
|
||
}
|
||
.file-cover-stat {
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
}
|
||
.file-cover-stat .add { fill: var(--ok); }
|
||
.file-cover-stat .del { fill: var(--danger); }
|
||
|
||
.dline {
|
||
position: relative;
|
||
display: flex;
|
||
gap: 12px;
|
||
padding: 3px 10px;
|
||
border-radius: 4px;
|
||
/* 容器本身始终不透明:转正落位的字符立即可见,不经历消失→淡入;
|
||
红/绿底色由 ::before 独立淡入,+/- 符号独立淡入 */
|
||
white-space: nowrap;
|
||
}
|
||
.dline::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: 4px;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
pointer-events: none;
|
||
}
|
||
.dline.is-del::before { background: rgba(212, 123, 107, 0.08); }
|
||
.dline.is-add::before { background: rgba(123, 168, 137, 0.10); }
|
||
.dline.is-shown::before { opacity: 1; }
|
||
.dline .dsign { flex: none; width: 10px; opacity: 0; transition: opacity 0.3s ease; }
|
||
.dline.is-shown .dsign { opacity: 1; }
|
||
.dline.is-del { color: var(--danger); }
|
||
.dline.is-add { color: var(--ok); }
|
||
|
||
/* diff 行内单字符:占位时透明;落位转正后 is-lit 实色显示 */
|
||
.dch { opacity: 0; }
|
||
.dch.is-lit { opacity: 1; }
|
||
|
||
/* 飘行的字母:从笔的擦/写位置飞向 diff 行字符位,落位后转正为流内字符 */
|
||
.dch-ghost {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
font-family: var(--font-mono);
|
||
font-size: 12.5px;
|
||
line-height: 1.5;
|
||
pointer-events: none;
|
||
/* 不设 will-change:合成层位图与转正后的矢量重绘之间会出现大小/清晰度跳变 */
|
||
transition: transform 0.34s cubic-bezier(.35,.1,.25,1);
|
||
z-index: 35; /* 高于盒前壁(30),飞行字母不被盒子挡 */
|
||
}
|
||
|
||
/* 参考点(校准期红色可见,校准后改回透明) */
|
||
/* 笔尖/橡皮定位参考点:visibility 隐藏(保留几何盒供 getBoundingClientRect 实测),
|
||
同时清掉 fill/stroke 防止从 SVG 根继承描边显形 */
|
||
.pen-ref { visibility: hidden; fill: none; stroke: none; pointer-events: none; }
|
||
|
||
/* ───────── 收件箱(genie 收盒) ─────────
|
||
后壁 z1(纸挡住它)、前壁 z4(它挡住纸)——纸从前后壁之间沉入 */
|
||
.inbox-part {
|
||
position: absolute;
|
||
left: 86px; /* 中心线 x=134 */
|
||
top: 166px;
|
||
width: 96px;
|
||
height: 96px;
|
||
color: var(--text-1);
|
||
opacity: 0;
|
||
transition: opacity 0.35s ease;
|
||
pointer-events: none;
|
||
}
|
||
.inbox-part.is-shown { opacity: 1; }
|
||
.box-back { z-index: 10; }
|
||
.box-front { z-index: 30; }
|
||
/* 前壁盒身填实色:不能透出后面收进去的纸(凹口线保持描边不填) */
|
||
.box-front path { fill: var(--bg-1); }
|
||
/* 盒内纸叠:genie 终态的文件卡片(.file-flight)本身就是插在盒子里的纸 */
|