| name | dither-background |
| description | 创建一个暗色单色的程序化背景,带有放大的方形像素与可见的 Bayer 有序抖动。当页面需要一个氛围感的近黑抖动场、宽阔的有机波纹或云团、以及克制的灰白高光衬托在框选 UI、主视觉内容或数据叠层之后时使用。 |
Dither Background
何时使用
- 暗色界面需要一个氛围感的单色背景层。
- 画面应展现放大的方形像素与可见的有序抖动。
- 设计需要有机波纹、云状团块或程序化的纵深感,而不使用彩色渐变。
- 背景应衬托框选 UI、主视觉内容或数据叠层。
视觉目标
- 近黑基底配以炭灰中间调、柔和的灰度堆积,以及偶尔出现的白色高光。
- 方形像素单元清晰可见,而非细小的胶片颗粒。
- 4x4 Bayer 风格抖动图案或等价的有序阈值化。
- 宽阔的有机波纹或云状团块,而非随机的电视雪花噪点。
- 边缘做暗角处理,使较亮的团块居中或偏轴分布。
HTML 与 CSS
<canvas class="dither-background" data-dither-background></canvas>
.dither-background {
position: fixed;
inset: 0;
z-index: 0;
width: 100vw;
height: 100vh;
background: #030303;
pointer-events: none;
}
.page-content {
position: relative;
z-index: 1;
}
Canvas 配方
当需要运动或程序化纵深时,使用真正的 canvas。
const BAYER_4X4 = [
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5,
].map((value) => (value + 0.5) / 16);
function smoothstep(edge0, edge1, value) {
const t = Math.max(0, Math.min(1, (value - edge0) / (edge1 - edge0)));
return t * t * (3 - 2 * t);
}
function noise2(x, y) {
const value = Math.sin(x * 127.1 + y * 311.7) * 43758.5453123;
return value - Math.floor(value);
}
function valueNoise(x, y) {
const ix = Math.floor(x);
const iy = .(y);
fx = x - ix;
fy = y - iy;
ux = fx * fx * ( - * fx);
uy = fy * fy * ( - * fy);
a = (ix, iy);
b = (ix + , iy);
c = (ix, iy + );
d = (ix + , iy + );
(
a * ( - ux) * ( - uy) +
b * ux * ( - uy) +
c * ( - ux) * uy +
d * ux * uy
);
}
() {
value = ;
amplitude = ;
frequency = ;
( octave = ; octave < ; octave++) {
value += (x * frequency, y * frequency) * amplitude;
frequency *= ;
amplitude *= ;
}
value;
}
() {
(!canvas) {};
ctx = canvas.(, { : });
(!ctx) {};
reduceMotion = .().;
cell = options. || ;
maxDpr = options. || ;
width = ;
height = ;
cols = ;
rows = ;
rafId = ;
palette = options. || [
[, , ],
[, , ],
[, , ],
[, , ],
[, , ],
[, , ],
];
() {
dpr = .(. || , maxDpr);
width = .(, .);
height = .(, .);
canvas. = .(width * dpr);
canvas. = .(height * dpr);
canvas.. = ;
canvas.. = ;
ctx.(dpr, , , dpr, , );
cols = .(width / cell);
rows = .(height / cell);
}
() {
nx = (x / cols - ) * ;
ny = (y / rows - ) * ;
distance = .(nx * nx * + ny * ny * );
vignette = - (, , distance);
drift = reduceMotion ? : time * ;
wave =
.(nx * + ny * + drift) * +
.(nx * - + ny * - drift * ) * ;
cloud = (nx * + drift * , ny * - drift * );
ridge = (, , cloud + wave);
offAxisMass = (, , .(nx + , ny - ));
.(, .(, ridge * vignette * + offAxisMass * ));
}
() {
seconds = time * ;
ctx. = ;
ctx.(, , width, height);
( y = ; y < rows; y++) {
( x = ; x < cols; x++) {
threshold = [(y % ) * + (x % )];
brightness = (x, y, seconds);
stepped = .(.(, .(, brightness + threshold * )) * palette.);
color = palette[.(palette. - , stepped)];
ctx. = ;
ctx.(x * cell, y * cell, cell, cell);
}
}
(!reduceMotion) rafId = (render);
}
() {
(rafId);
();
();
}
();
();
.(, handleResize);
{
(rafId);
.(, handleResize);
};
}
cleanupDither = (
.(),
{
: ,
: ,
}
);
调节旋钮
- 单元尺寸:
5px-10px;单元越大,Bayer 矩阵越清晰易辨。
- 调色板:仅用近黑、炭灰、柔和灰,罕见的白色高光。
- 形状:调节
wave、cloud、ridge 和 offAxisMass 以塑造宽阔的团块。
- 暗角:当前景可读性需要更多留白时,加强边缘衰减。
- 运动:让漂移保持缓慢;使用较小的时间乘数并避免闪烁。
- 性能:在简化场之前,先增大
cellSize 或限制 maxDpr。
构图笔记
- 用
pointer-events: none 把 canvas 置于界面之后。
- 将其作为框选 UI、主视觉文案或数据叠层背后的氛围层。
- 保持前景对比可控、排版干净。
- 让一个主亮团块定义整体构图;避免全屏均匀发亮。
避免
- 彩虹渐变、彩色噪点,或没有抖动结构的柔和模糊团块。
- 细小颗粒或雪花状斑点,导致方形矩阵消失不见。
- 与前景文字争夺注意力的全屏明亮白噪声。
- 逐帧随机变化导致闪烁而非漂移的噪点。
- 用同样明亮的单元铺满整个视口。
快速检查
- 在正常观看距离下能看清方形 Bayer 图案。
- 场形成宽阔的有机波纹或云团。
- 调色板保持单色且克制。
- 边缘隐入近黑。
- 前景 UI 无需厚重叠层即可保持可读。