枯白啃设计又发新技术了!

图标设计师 / 设计工程师

新栏目《999 个小技巧,让你 Vibe 的产品更有质感》 ✨ 003 Dither 与 ASCII 参数化效果 001–003 都在我主页,关注我,每天一个让产品更有质感的小技巧 为你的登录页面/卡片添加可控的 Dither 与 ASCII 效果,并附带遮罩、色调、采样强度、大小、间距等可调参数。 两种效果怎么拆: ASCII:把画面切成网格,每格按亮度换成一个字符,从 “.” 到 “@” 越来越密。大小是字号,间距是格子疏密,采样强度是明暗对比,取样区域决定字画的是暗部还是亮部。 Dither:把画面切成像素块,用 4×4 的 Bayer 矩阵当阈值,把每个颜色压成几级。大小是像素块,间距是纹理的尺度,采样强度越高,颜色级数越少,颗粒感越重。 两者都可以加一层遮罩,让画面退后,效果更像纹理而不是噪点。 推荐参数: ASCII:遮罩 90、取样暗部、采样强度 100、大小 37、间距 0 Dither:遮罩 88、采样强度 100、大小 15、间距 0 Prompt 如下,可以直接抄 ⬇️ Add an ASCII / ordered-dither effect to one specific image or video in this project, with a live control panel next to it. Use the asset and the place I name (an , a , or a file path or URL, and the page or section it sits in); ask me if I have not named them. Apply the effect to that asset only, in place: keep its size, position, rounded corners and anything layered on top of it. If the project already has an image or canvas effect pipeline, or slider, segmented-control and panel components, build on them; otherwise create a reusable EffectLayer and EffectPanel. Settings, one object shared by the layer and the panel: { filter: 'ascii' | 'dither', mask, palette, inkTone, strength, size, spacing } Keep separate values per filter, so switching filters brings back that filter's own settings. What each control does in each filter, its range, and the recommended value (ASCII / Dither): Mask effect, 0-100 (90 / 88): a vertical veil over the picture so the effect reads as texture rather than noise. Same in both filters. Color, Original or one of five palettes (Original): gradient-maps the asset's luminance into one color family before filtering. Sample tones, Shadows | Highlights (Shadows; ASCII only, hidden for Dither): which end of the tonal range becomes dense characters. Sampling strength, 0-100 (100 / 100). ASCII: contrast of the tone curve and how bright and opaque the characters are. Dither: how few levels each color channel keeps (8 at 0, 2 at 100) and how fully the quantized color replaces the original. Size, 0-100 (37 / 15). ASCII: font size, 7-24px. Dither: pixel block, 2-9px. Spacing, 0-100 (0 / 0). ASCII: grid step, 8-24px across and 9-26px down, never tighter than the glyphs. Dither: scale of the Bayer pattern, 1-4 blocks per matrix cell. Helpers used throughout: range(v, a, b) = a + (b - a) * v / 100 luminance L = (0.299 R + 0.587 G + 0.114 B) / 255 s = strength / 100 "The box" is the asset's rendered size on the page. Sampling: Cover-fit the asset into the box and draw it into a small sampling canvas (about 480px on the long side), then read it with getImageData. A sample is the pixel under a point in the box, composited over white. With a palette, recolor that canvas first: map each pixel's L through a 256-entry lookup built from the palette's four stops. Per image or video frame, take about 12,000 evenly spaced samples: low = 8th percentile of L, high = 92nd percentile. Shadows means dark ink, Highlights light ink. ASCII: Characters, from empty to full: " .,:;-=+xX08S#&@" (starts with two spaces). Font: round(range(size, 7, 24))px monospace (22 instead of 24 when the box is under 720px wide), centered in each cell. Grid: x step = max(range(spacing, 8, 24), widest glyph * 1.08); line height = max(range(spacing, 9, 26), height of "@" * 1.12, font * 1.04). The first cell sits half a step in from the edge. Tone of a cell: raw = dark ink ? 1 - L : L; normalized = dark ink ? (high - L) / (high - low) : (L - low) / (high - low), clamped 0-1 (use raw alone when high - low < 0.025); mix = 0.72 + 0.14 s; t = raw (1 - mix) + normalized mix. Then a tone curve: black = 0.22 s, white = 1 - 0.08 s, lev = clamp((t - black) / (white - black), 0, 1), tone = lev (1 - s) + smoothstep(lev) s. Skip cells with tone <= 0.02. Character = chars[round(tone * 16)]; skip spaces. Ink: the sampled color lifted in HSV: value += (1 - value) * (0.24 + 0.18 s + 0.24 tone); saturation *= 1 + 0.16 + 0.2 s (clamped). Alpha = clamp((0.12 + 0.7 tone) (0.72 + 0.28 s), 0.06, 0.94). Dither (ordered, Bayer 4x4): pixel size = max(2, round(range(size, 2, 9))) (7 instead of 9 under 720px); pattern scale = max(1, round(range(spacing, 1, 4))); levels = max(2, round(range(strength, 8, 2))) per channel; blend = range(strength, 0.32, 1). Matrix [[0,8,2,10],[12,4,14,6],[3,11,1,9],[15,7,13,5]]; threshold = (m + 0.5) / 16, indexed by floor(x / pattern scale), floor(y / pattern scale). For each block sample its center, push saturation away from luma by 1 + 0.06 + 0.18 s, then per channel: scaled = c / 255 * (levels - 1); q = floor(scaled) + (fraction > threshold ? 1 : 0); out = c + (q / (levels - 1) * 255 - c) * blend. Write the blocks into a canvas of box / pixel size and draw it scaled up with image smoothing off. Mask and layering: The mask is a vertical gradient with stops at the top, 42% and the bottom. Base opacities: on a light page white 54/20/38%, on a dark page black 44/20/50%. For a mask value v: stop = v <= 50 ? base * (v / 50)^1.08 : base + (100 - base) * ((
登录并发表评论

评论 · 0