SVG 모폴로지

SVG morphology

알파 채널을 부풀리거나(dilate) 깎아내는(erode) 필터. 팽창은 획을 굵게 뭉치고, 침식은 얇은 획을 갈라지게 만듭니다.

다른 이름: feMorphologyDilate/erode filter
···
html
<svg width="0" height="0"><filter id="morph" x="-20%" y="-20%" width="140%" height="140%">
  <feMorphology id="morphOp" operator="dilate" radius="0"/>
</filter></svg>
<div class="panel"><h1 class="outline" id="txt">FLOW</h1></div>
<div class="label" id="l">dilate(0)</div>
css
.panel{position:relative;width:min(80%,320px);height:120px;border-radius:16px;background:#181822;display:grid;place-items:center}
.outline{margin:0;color:#fff;font:500 13vmin/1 sans-serif;letter-spacing:.02em;filter:url(#morph)}
.label{position:absolute;left:50%;bottom:8%;transform:translateX(-50%);padding:4px 10px;border-radius:999px;
  background:rgba(0,0,0,.55);color:#fff;font:600 12px/1.4 monospace}
js
const op = document.getElementById('morphOp');
const l = document.getElementById('l');
const STEPS = [['dilate',0],['dilate',2],['dilate',4],['dilate',6],['erode',0],['erode',1],['erode',2],['erode',3]];
let i = 0;
function apply(){
  const [operator, radius] = STEPS[i];
  op.setAttribute('operator', operator);
  op.setAttribute('radius', String(radius));
  l.textContent = operator + '(' + radius + ')';
  i = (i + 1) % STEPS.length;
}
apply();
setInterval(apply, 900);

feMorphology 는 각 픽셀 주변 radius 범위 안에서 알파값의 최댓값(dilate) 또는 최솟값(erode) 을 취하는 필터입니다 — 이미지 처리에서 말하는 팽창·침식 연산 그대로입니다. x·y 로 반지름을 따로 줄 수도 있습니다.

dilate 는 글자·아이콘의 획을 굵게 만들다가 결국 인접한 획끼리 들러붙게 하고, erode 는 반대로 획을 얇게 깎다가 가느다란 부분부터 끊어놓습니다 — 데모에서 얇은 사슬 서체가 erode 단계에서 조각조각 갈라지는 걸 볼 수 있습니다.

계산량은 반지름의 제곱에 가깝게 늘어나는 min/max 컨볼루션이라 radius 가 크면 비쌉니다. dilate 로 만든 윤곽선은 픽셀 격자를 그대로 반영해 각져 보이는데, 뒤에 feGaussianBlur 를 살짝 얹으면 모서리가 둥글어져 자연스러워집니다. Safari 포함 주요 브라우저 모두 잘 지원합니다.

언제 쓰나

아이콘 윤곽선을 두껍게/얇게 만들거나, 마스크 가장자리의 노이즈를 정리할 때. radius 는 작게 유지하고, 각진 결과가 싫으면 뒤에 blur 를 살짝 더하세요.