mcpField
P
paper-cinder-transition

paper-cinder-transition

github.com/mengto设计与 UI7.1k369 次安装3 天前更新
代码仓库

关于 paper-cinder-transition

用于制作场景切换火焰过渡的图形技能,采用冻结的旧画面、实时新场景和逐像素火焰前沿合成。适合需要火焰揭示效果的页面作者,并提供渲染、性能与无障碍规则。

主要功能

  1. 01冻结旧帧并实时绘制新场景
  2. 02按像素距离合成火焰前沿
  3. 03生成余烬、焦痕与孔洞效果
  4. 04烘焙噪声并裁剪场景渲染范围
  5. 05支持减少动态效果与页面生命周期

适用场景

  1. 01为章节切换制作火焰揭示效果
  2. 02让火焰边缘呈现撕裂与余烬变化
  3. 03控制全屏过渡的渲染开销
  4. 04为减少动态效果偏好提供静态切换

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mengto/skills --skill paper-cinder-transition

安装后在 Claude Code 中输入/paper-cinder-transition确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

demo.html
PROMPT.md
agents/
openai.yaml

详细介绍

纸烬转场

页面不会淡入下一个页面,而是被一条火线吞噬。当燃烧效果是加载屏幕上的环形动画,且必须在主线程阻塞时仍能运行,请使用 fire-paper-loader;当燃烧效果是你负责的两个场景之间的切换时,请使用此方案。对于通过遮罩写入而非烧出的文本,请使用 masked-reveal;对于没有燃烧效果、由滚动控制进度的序列,请使用 scroll-scrubbed-visual-sequence。

运作机制

一次全屏渲染会根据到嘈杂前沿的有符号距离,逐像素判断该像素属于冻结的旧帧(火线前方)、燃烧区域(火线处),还是实时的新场景(火线后方)。 其他一切都是准备工作。移除这三个图层中的任何一个,效果就不再像燃烧:没有冻结帧,旧页面就会在火焰下继续移动;没有实时场景,揭示的就只是一张静止画面;没有逐像素精确的距离,边缘就会变成模糊的色块。

xc  = signed distance to the front in CSS px (+ ahead, - burnt)
reveal = clamp(0.5 - xc/aa, 0, 1)      // new frame from the front back, cut to the pixel
col = mix(fromFrozen, toLive, reveal)
col = mix(col, charcoal,  bed * pores) // 26 px band of torn, porous char (alpha-masked)
col += flame(xc) + charGlow(xc)        // additive: thread, ember zone, tight glow

分工准备

属于此技能准备工作(可自行修改)
合成顺序、距离场、余烬色阶、焦痕规则、噪声烘焙两个场景的内容、场景配色、排版、页面文案
两个场景缓冲区(冻结的来源场景、实时的目标场景)和裁剪区火线从哪一侧进入(见下文)
时序、缓动、DPR、减少动态效果时的静态画面控制界面

规则及其防止的问题

绘制冻结的旧帧和实时的新场景,绝不要让两者相互淡入。 转场开始时,只将离场场景快照一次,存入全分辨率目标缓冲区(时钟冻结),并持续实时渲染进入场景。交叉淡化,或在仍在动画的旧场景上叠加燃烧效果,会让同一屏幕区域同时显示两个章节,火焰也就不再是改变页面的主体。

以像素为单位测量前沿,而不是以场场单位测量。 xc = (field - T)/grad * pxHeight / pxScale。 单独使用等值线时,噪声顺着扫描方向倾斜的位置会形成细如发丝的接缝,逆着扫描方向倾斜的位置则会形成宽缝,因此火焰宽度会无故不均匀。

用相隔数个像素的有限差分求斜率,绝不要对烘焙纹理使用 dFdx。 烘焙噪声在每个纹素内的斜率都恒定;逐像素采样会让色带横穿火线形成条纹。以 +4 px 的间距在 x 和 y 方向采样宽场(hStep = 4*uPxS/uPxH),并将梯度限制在 0.35/aspect .. 3/aspect。

余烬区域应是平滑的发光渐变,而不是分段色带。 其症状是看起来像不真实的海报。根据热度变量连续调色:火线前沿为黄色,然后过渡到橙色,再到边缘清晰、参差不齐的深红色焦边。高热区域会让黄色向后延伸得更远。色阶常量(线性空间,色调映射前):

色阶rgb位置
深色0.34, 0.022, 0.003焦边,tau 0.05
橙色0.86, 0.20, 0.024tau 0.42
金色1.02, 0.38, 0.055tau 0.76
金黄1.10, 0.64, 0.17 x (0.30 + 0.70*hot)tau 高于 0.86
火丝1.40, 0.56, 0.09 至 1.85, 1.02, 0.30,按热度变化火线前沿
辉光0.95, 0.20, 0.022 x 0.60火丝周围 2 到 5 px

高热颜色应保持金黄,绝不要发奶油色。 浅黄色在高热时会呈现奶油色,变暗时则会呈现卡其色。色阶中没有灰色或白色;低热区域应沿色阶向下变化,而不只是单纯变暗。

火线前沿应是一条明亮的火丝,带有珠状起伏和收窄处,宽度为 0.7 到 5 px,而不是均匀的笔画。 均匀的笔画看起来像霓虹灯。宽度为 (0.9 + 2.6*heat) * bead,其中 bead 根据沿边缘的噪声在 0.5 到 1.5 之间变化。只添加紧贴火丝的辉光(exp(-|xc+0.5wc| / (2 + 2.8*heat))),不要添加宽阔的光晕。 必须明显移动,即使扫掠静止时也是如此。 症状:“太僵硬、静止”。用沿边缘双向移动的脉冲带动热流(sA*0.018 - t*1.30、sA*0.027 + t*1.70),以 t*7 闪烁,让一股热浪以 -t*2.6 沿区域回卷,让火星以 -t*0.8 飘移,并让火舌在前沿舔动(t*1.1、t*2.8,屏幕占比最多为 0.022 和 0.010)。静帧会掩盖所有这些效果,因此请在实时播放的片段中判断燃烧效果。

用 Alpha 遮罩处理黑色焦痕;它不是一整块平板。 焦痕是一条宽 26 px(前沿另留 12 px 缓冲)的带状区域,会沿着撕裂的远端边缘从实时画面上剥落,随着老化逐渐出现孔隙,露出下方的内容。实心黑条看起来像擦除。覆盖率 = bed * pores,边缘以 8*noise(0.08) + 3*noise(0.25) px 撕裂,这比前沿自身的坡度更平缓,因此会形成毛边,但不会切出孤岛。

绝不要给孔隙围上发光边缘。 边缘会沿焦痕边缘连成一条描绘出的轮廓。孔隙只是洞。

焦痕的边缘取自宽阔的前沿,而不是精细前沿。 独立的二维噪声会产生漂浮在新画面上的焦痕孤岛。只沿边缘使用噪声(kbEdgeNLine),并令 edge = zoneW + fs + bedW*(0.35 + 1.10*n),其中 fs 最大为 12。

在火焰前方连续地熏黑旧画面,延伸 20 到 40 px,通过 tk^2 从棕褐色 0.80, 0.62, 0.42 过渡到深棕色 0.20, 0.10, 0.045,延伸距离随噪声变化。分成三个独立步骤会画出跟随火焰轮廓的线条。

预先生成噪声;不要逐像素叠加 fbm。 在 2880x1800 分辨率下,逐像素 fbm 每次处理耗时 1.5 到 3 ms;使用一张可平铺纹理和一张哈希表,在视觉效果相同的情况下耗时 0.3 到 0.6 ms。预先生成一次:一张周期为 16 个单元的 1024^2 RGBA16F 纹理(r 为 3 个倍频程的 fbm,g 为 3 个倍频程的脊状噪声,b 为梯度,a 为 2 个倍频程的脊状噪声,全部以 x*0.0625 采样),以及一个存储在 2048^2 R16F 表中的整数哈希格点;在平滑步进的分数部分处进行滤波,使结果与值噪声完全一致。

将实时画面裁剪到已显露的一侧,并以较小尺寸渲染。 只渲染线条后方的内容,另外加上屏幕尺寸的 0.16,用于海湾区和薄雾。燃烧期间以 0.75 倍比例绘制画面(合成画面仍保持全尺寸,因为人眼会注意边缘);过渡静止 150 ms 后,在 0.3 s 内淡入全尺寸画面,同时绘制两者,这样既不会看到清晰度变化,也不会影响动态帧的性能。将文字以全尺寸放在合成画面的实时一侧;如果在 0.75 倍缓冲区中绘制,文字会变模糊。

在噪声无法影响到的区域跳过噪声计算。 按 hx 判断:如果位置超出前沿前方 0.215 + 0.15,返回冻结画面;如果位于后方超过 0.25 - 0.215,则返回实时画面。在中间带状区域内,若位置超出前沿前方 62 px(加上缓冲),或位于后方 125 px,也执行相同操作。这样才能承担全设备分辨率的计算开销。

从新文字所在的一侧开始扫掠。 hx = dir>0 ? uv.x : 1-uv.x;章节文字在右侧,就从右侧烧入。向后播放时也使用目标章节所在的一侧。始终从左向右移动的线条会最后才扫过自己的新标题。

为每次燃烧设置种子。 seed = (chapter+1)*17.3 用于确定沿画面分布的 2.0 到 3.6 个宽阔海湾、它们的深度(0.055 到 0.115)及倾斜度((sv.x-0.5)*0.07),从而确保每次燃烧的轮廓都不相同。

常量

项目值
带状区域半宽0.215 个屏幕高度
前沿,线性-0.26 + 1.62 * rc,rc = clamp((f-0.01)/0.97),进入和离开时均在画面外
时长默认 3.0 s;1.5 到 5 s 均可完整读取
线条噪声尺度宽尺度 q*1.10(0.055-0.115)、q*2.60(0.050-0.030);细尺度 0.014、0.006、0.0025
余烬区域宽度12 到 66 px,(18 + 20*min(w,2.6)) * (0.55 + 0.45*life)
炭痕基底 26 px,表层 3 到 6 px,水墨 0.0085, 0.0062, 0.0048
热浪约 uPxH 的 1.8 px,仅限前沿 18 px 范围内
泛光亮部通道阈值 1.15,半分辨率和四分之一分辨率模糊,权重 0.26*4 和 0.34*4
色调暗角 0.42 + 0.58v、ACES、sRGB、颗粒 0.003
实时场景过渡期间缩放为 0.75,停顿 150 ms,交叉淡化 0.3 s
预算dt 限制为 1/30 s,DPR 上限为 2

常见问题,按症状排列

  • 火焰是一条黑炭上的细红线,没有余烬区域。 烘焙噪声和哈希采样器未绑定,因此 life 读取的是帧纹理的值。绑定 uKbN 和 uKbH。
  • 线条后面出现杂乱的空白,或发生硬切换。 实时场景只在裁剪区域内绘制(front + 0.215 + 0.16);边距过窄会让合成过程读取未绘制的纹理像素。
  • 炭痕边缘出现阶梯或 2x2 块。 在跳过的分支之后计算了导数。根据场自身的尺度计算像素变化(kbAA(v, w))。
  • 新画面上漂浮着零散的炭痕。 远侧边缘是从细噪声中裁切出来的;应从宽尺度前沿取得。
  • 余烬边缘内侧出现乳白色带状区域。 用曲线提亮黑色会让纸面上的区域泛白;应改用增益来照亮暗区。
  • 燃烧结束后,线条后面的场景看起来模糊。 0.75 尺寸的缓冲区仍在使用;停顿间隔结束后运行全尺寸交叉淡化。
  • 耗时看起来很长。 隐藏的 Browser 窗格仍在渲染并占用 GPU;使用带 GPU 标志(--use-angle=metal --enable-gpu)的无头浏览器测量,并将窗格停放。噪声烘焙并对场景进行裁剪后,Kibori 的过渡在 120 Hz 上限下保持 p50 8.3 ms;以相同方式测量,本演示在 1440x900 下燃烧效果的 p50 为 8.3 ms,p90 为 8.8 ms。
  • 前沿经过时,颗粒和余烬在游动。 将它们固定在纸面上(使用纸面坐标中的 g,再加上每次燃烧的偏移),不要放在屏幕空间中;只有前沿会移动。

无障碍与生命周期

  • prefers-reduced-motion: reduce:不播放燃烧效果。将每个场景在固定时间渲染为设计好的静态画面,并在 0.35 s 内(smoothstep)交叉淡化冻结帧和实时帧。控件仍可使用。
  • 当 document.hidden 为真或画布离开视口时暂停;恢复时重置时间基准,避免第一帧累积暂停期间的时间。
  • 将 dt 限制为 1/30 s,DPR 上限设为 2,根据 ResizeObserver 调整尺寸,并且在视口尺寸为零时不创建任何内容。
  • 燃烧期间按下控件会将目标加入队列;前沿离开后执行最后一个目标。
  • 使用真实按钮、方向键和 1 到 3 切换章节,并通过实时区域播报到达的章节。

成本

在源页面中测得,成本来自逐像素噪声,而非火焰。烘焙噪声,并在远离前沿处恢复纯场景,是关键优化手段。其次是 0.75 尺寸和裁剪。不要在合成阶段追求 MSAA:边缘由 kbAA 绘制到像素级。

来源

内容摘自 KIBORI,这是一个 WebGL 木工工坊页面,各章节以燃烧效果相互过渡,页脚的纸张燃烧效果塑造了视觉风格。demo.html 中的 EDGE GLSL(火焰、炭痕、孔隙、表层、焦痕)和燃烧通道均来自该页面,经过移植,并将场景替换为三个由着色器绘制的房间。

评论

支持 Markdown