Files
workbuddy_skills/oil-ui-pro/references/ornament.md
T

8.5 KiB
Raw Blame History

点缀与特效:SVG 和着色器

这里讲用代码画出来的视觉效果:小到一条手绘下划线、一层颗粒,大到首屏整片流动的渐变、扫过按钮的一道流光、由点阵组成的主视觉。它们能让方向从“配色对了”变成“有生命”;做得粗糙时,就是又一层模型默认的装饰。

先问要不要

  • 从生成引擎里来。 效果要能说出属于这个方向的理由:方向的关键词是“信号”,点阵和扫描线才成立;是“流体”,流动渐变才成立。删掉它,方向会少一点自己的东西;删掉后没有区别,就不加。
  • 一个主效果,几处回响。 一页选一个主效果,其余地方只用它的颜色、形状或节奏小声呼应,比如首屏是流动渐变,按钮的流光就用同一组颜色。几种特效各自为政,页面就成了效果展示。
  • 不碰内容。 效果不压在正文、数据和控件上,也不画得像按钮;在它上面的文字,按最差的一帧检查对比度。
  • 用代码画。 SVG 和着色器体积小,跟着主题色走,放大也清楚。摄影和复杂插画才用图片,见 素材。
  • 质感先来自材质。 顶尖的产品界面很少用流光、渐变团和着色器,让人记住的多是写实的材质、准确的结果和手感。特效是给少数方向的,不是提升质感的默认手段。
  • 产品界面收着用。 后台和工具的工作区不放持续运动的效果;登录、空状态、完成反馈和品牌页可以放开。

SVG 能做的几类

效果 做法 要注意
颗粒质感 feTurbulence type="fractalNoise" 生成噪声,feColorMatrix 把它染成页面的颜色并控制透明度,写成 data URI 平铺成 background-image。细颗粒 baseFrequency 约 0.8–0.9、透明度 0.05–0.08;再叠一层约 0.004 的大块起伏,表面就不是均匀色块 加 stitchTiles="stitch" 避免拼缝。做成固定尺寸的平铺图,不把 filter 挂在整页或滚动容器上,否则每次重绘都要重算。只在方向本身有实物材质时加,不当作提升质感的通用补丁
手绘框、粗糙边缘、大号数字的毛边 feTurbulence 接 feDisplacementMap,scale 约 1.5–3。同类图形各换一个 seed,不要一模一样 正文不加;scale 太大像坏了。滤镜区域默认只向外留 10%,位移、光晕和投影会被裁掉,给 filter 设足够的 x、y、width、height
沿弧线或环形排字 <textPath> 配 startOffset="50%" 和 text-anchor="middle" 居中 字放在弧的外侧;放在内侧,字距被挤压,字形会相撞。手机换一套 viewBox 重新取景,不只是等比缩小
下划线、轨迹、连接两处内容的引线 path 加 stroke-linecap="round";缩放时线宽不变用 vector-effect="non-scaling-stroke";描线动画设 pathLength="1" 和 stroke-dasharray="1",让 stroke-dashoffset 从 1 走到 0 引线两端要真的指向内容。让光点沿线流动:用一段短虚线加长间隔,动画 stroke-dashoffset,再叠一份模糊的副本当光晕

颜色统一用 currentColor 或设计变量,让效果跟着主题和深色模式变。

着色器能做什么

效果 核心做法
流动渐变 3–5 个颜色点沿各自的慢速轨迹移动,按距离混合;要更像液体,用噪声扭曲坐标再取噪声(domain warping),纹路会像大理石或油彩那样流
极光和光带 在一个方向上拉长的噪声,叠加在深色底上,边缘用渐变遮罩收掉
虹彩和镭射 用余弦调色板 a + b·cos(2π(c·t + d)) 按角度或指针位置取色,四组参数就能调出整条连续的色带
流光 一条两边柔化的亮带,按 20°–30° 斜角扫过形状,只在形状内部可见
点阵和半调 把画面切成网格,每格按下面的亮度画一个圆点;动起来可以是波纹、指针附近的隆起,或者从点阵慢慢显出一张图
像素和抖动 用 Bayer 矩阵做有序抖动,把颜色压成少数几种,得到一位色屏幕和早期游戏的质感
跟随指针 指针附近的亮斑、扭曲或点阵隆起;指针位置先做惯性平滑,再传给着色器

不少效果不需要 WebGL:边框上转圈的流光,用 @property 注册一个角度变量,驱动 conic-gradient,再用 mask 只露出边框;文字上的流光,用 background-clip: text 配移动的渐变;跟随指针的卡片光斑,把指针坐标写进 CSS 变量,驱动 radial-gradient。只有连续变化的噪声、逐像素计算或大量点阵时,才用着色器。

做得高级的关键

渐变

  • 在 OKLab 或 OKLCH 里混色。 在 sRGB 里混两种对比色,中间会经过发灰发脏的一段;在 OKLab 里混,中间段仍然饱满。CSS 可以写 linear-gradient(in oklch, …),着色器里先转到 OKLab 再插值。
  • 加抖动消色带。 大面积平滑渐变在 8 位显示上会出现一圈圈色带,暗部最明显。输出前叠一层正负半个色阶的噪声,色带就消失了;把噪声加大到 3–8%,就是颗粒渐变的质感。
  • 配色从品牌来。 紫蓝色的柔光团是最典型的模型默认。从设计变量里取两三个色相,挑一组不常见的搭配,再加结构:光带、点阵、颗粒,而不是再加一团光。
  • 光晕不要糊成白块。 几层光叠加后会超过 1,直接截断会变成一片死白。用 1 - exp(-x) 这类曲线把亮部压回来,高光才有层次。

流光

  • 亮带要窄、边要软。 宽度约为元素的 15–25%,两侧用 smoothstep 柔化。
  • 扫过要快,间隔要长。 单次 600–900ms、缓入缓出;在悬停、出现时播放,或每隔 4 秒以上扫一次。一刻不停地扫,就显得廉价。
  • 用提亮的混合方式。 screen 或 plus-lighter 让流光提亮底色,而不是刷上一层白。浅色底上几乎看不见流光,所以它更适合深色、饱和色或金属质感的表面。
  • 只在一个地方扫。 通常是主按钮或一张主卡片。每张卡都在闪,就没有重点。

点阵

  • 圆点面积跟明暗走。 半径按亮度的平方根变化,面积才和明暗成正比,画面读起来才像原图。
  • 边缘要抗锯齿。 用 fwidth 和 smoothstep 在一个像素的宽度内过渡(WebGL2 直接可用)。硬边的点动起来会闪烁、爬行。
  • 防摩尔纹。 网格尺寸取设备像素的整数倍,或把网格旋转 15°、45°;彩色半调的每个通道用不同角度。
  • 没亮的点也画出来。 屏幕式的点阵,熄灭的点保留很淡的一层,亮的点加一圈柔光,才像真的屏幕。
  • 格子大小决定读法。 每格 6–12 CSS 像素读起来是质感,16 像素以上读起来是图形。

动起来

  • 速度跟角色走。 背景的循环慢到十秒以上,读正文时注意不到;交互反馈要在 150–300ms 内响应。
  • 指针要有惯性。 每帧让当前位置向目标靠近一小步(例如 current += (target - current) * 0.1),效果才像有重量,不会一抖一抖地跟着鼠标。
  • 可以跟滚动绑定。 把滚动进度作为参数传进去,让效果随阅读推进变化,比独立循环更有叙事感。

成本与兜底

  • 分辨率按效果选。 模糊的颜色场按 1 倍或一半分辨率渲染,再用 CSS 放大;点阵、像素和细线要按实际设备像素渲染,否则会糊。
  • 不引入 3D 引擎。 只画一个全屏四边形时,直接写 WebGL 或用几 KB 的小工具库,不为它加载 three.js 这类完整引擎。
  • 看不见就停。 滚出视口时用 IntersectionObserver 暂停,标签页隐藏时停掉 requestAnimationFrame;prefers-reduced-motion 时只画一帧静止画面。
  • 先写兜底。 拿不到 WebGL、上下文丢失(webglcontextlost)或设备太弱时,显示同样配色的 CSS 渐变或静态图,页面照常可读。

验收

  • 截几个不同时刻的画面,不只看第一帧;效果上的文字按最差的一帧检查对比度。
  • 100% 和 200% 下看点阵、颗粒和边缘;缩略图里看不出颗粒是正常的。
  • 浅色、深色和减少动效三种设置各看一次,再看一次兜底画面。
  • 在目标手机上看滚动是否掉帧、机身是否发热。
  • 装饰性 SVG 和画布加 aria-hidden="true";承担意思的效果,例如由点阵拼出的标题,另给读屏能读到的真实文字。