지적 세 가지를 전부 확인했고 실제 결함도 하나 나왔다.
1. 재료 섹션 h2 가 아무것도 전달하지 않았다
'표면은 CSS 가 정하고, 공간은 GPU 가 맡는다'
금칙어도 없고 치환 테스트도 통과하는데 독자가 얻는 게 없다.
구현 이야기를 대구로 포장한 것이었다.
-> '텍스처 200KB 를 300바이트가 대신한다'
카피라이팅 방법론(so you can 테스트 / 6~12단어 / 검증 가능한 수치)을
찾아 적용했다. 우리 antipatterns 의 카피 절은 금지 목록뿐이라
이런 문장을 못 잡는다 — 작법 절을 새로 넣었다.
2. 필터 데모가 줄무늬였다
필터가 무엇을 하는지는 보이지만 왜 쓰는지는 안 보인다.
각 필터를 실제로 걸리는 자리로 옮겼다.
그레인 -> 매끈한 그라디언트 (없으면 밋밋하다는 대비)
굴절 -> 글자 (유리 뒤가 실제로 휘는 것)
듀오톤 -> 사진풍 면 (한 팔레트로 묶이는 것)
타일마다 '언제 쓰는가' 한 줄을 추가했다.
3. 그레인 필터가 아무 효과도 못 내고 있었다
feComposite in='SourceGraphic' in2='soft' operator='over' 는
원본을 노이즈 위에 올린다. 노이즈가 통째로 가려진다.
에러도 경고도 없이 조용히 죽는다.
feBlend overlay 로 바꾸고 baseFrequency 0.9 -> 0.62.
섹션 간 검은 띠도 고쳤다. 파이프라인 캔버스가 섹션 패딩(112px) 아래에서
시작해 히어로 배경이 끝난 자리와 사이에 200px 이 비었다.
스킬
antipatterns.md 카피 작법 — 금지 목록만으로는 못 잡는 실패
svg-filters.md 데모는 실무 소재에 · feComposite 입력 순서 함정
This commit is contained in:
parent
52fe0b5030
commit
61fb0cdcfa
6 changed files with 192 additions and 41 deletions
|
|
@ -55,10 +55,10 @@ const { c, ui } = Astro.props;
|
|||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: clip;
|
||||
min-height: min(78dvh, 46rem);
|
||||
min-height: min(72dvh, 42rem);
|
||||
display: grid;
|
||||
align-content: center;
|
||||
padding-block: calc(var(--header-h) + var(--space-6)) var(--space-7);
|
||||
padding-block: calc(var(--header-h) + var(--space-6)) var(--space-6);
|
||||
}
|
||||
|
||||
/* ---- 배경 ------------------------------------------------ */
|
||||
|
|
@ -72,8 +72,8 @@ const { c, ui } = Astro.props;
|
|||
/* 아래로 갈수록 지운다. black 은 색이 아니라 마스크 알파다.
|
||||
없으면 히어로 하단에서 배경이 가로선으로
|
||||
뚝 끊겨 다음 섹션과 두 장의 종이처럼 보인다. */
|
||||
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 82%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, black 0%, black 82%, transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 97%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, black 0%, black 97%, transparent 100%);
|
||||
}
|
||||
.hero-fallback,
|
||||
.hero-canvas,
|
||||
|
|
|
|||
|
|
@ -26,8 +26,11 @@ const { c } = Astro.props;
|
|||
{
|
||||
c.tiles.map((t, i) => (
|
||||
<li class:list={["tile", "reveal", i % 2 === 0 ? "par-slow" : "par-fast"]} style={`--reveal-delay:${i * 80}ms`}>
|
||||
<div class={`swatch swatch-${t.id}`} aria-hidden="true"></div>
|
||||
<div class={`swatch swatch-${t.id}`} aria-hidden="true">
|
||||
{t.id === "glass" && <span class="swatch-type">Aa 산미 8.5</span>}
|
||||
</div>
|
||||
<h3 class="tile-name">{t.name}</h3>
|
||||
<p class="tile-use">{t.use}</p>
|
||||
<p class="tile-how muted">{t.how}</p>
|
||||
<p class="tile-cost mono">{t.cost}</p>
|
||||
</li>
|
||||
|
|
@ -43,13 +46,20 @@ const { c } = Astro.props;
|
|||
<svg class="visually-hidden" aria-hidden="true" focusable="false">
|
||||
<defs>
|
||||
<!-- 종이: 미세한 프랙탈 노이즈를 표면에 얹는다 -->
|
||||
<!--
|
||||
그레인. 한동안 아무 효과도 못 내고 있었다.
|
||||
`feComposite in="SourceGraphic" in2="soft" operator="over"` 는 원본을 노이즈
|
||||
**위에** 올린다 — 노이즈가 통째로 가려진다. 입력 순서가 뒤집혀 있었다.
|
||||
그레인은 원본을 덮는 게 아니라 표면에 섞이는 것이므로 feBlend/overlay 가 맞다.
|
||||
baseFrequency 0.9 도 너무 촘촘해 화면에서 뭉갰다.
|
||||
-->
|
||||
<filter id="f-grain" x="0" y="0" width="100%" height="100%">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" result="n" />
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.62" numOctaves="3" seed="4" result="n" />
|
||||
<feColorMatrix in="n" type="saturate" values="0" result="g" />
|
||||
<feComponentTransfer in="g" result="soft">
|
||||
<feFuncA type="linear" slope="0.42" />
|
||||
<feFuncA type="linear" slope="0.55" />
|
||||
</feComponentTransfer>
|
||||
<feComposite in="SourceGraphic" in2="soft" operator="over" />
|
||||
<feBlend in="SourceGraphic" in2="soft" mode="overlay" />
|
||||
</filter>
|
||||
|
||||
<!-- 유리: 난류로 만든 변위 지도를 굴절에 쓴다. 필터 영역을 넓혀 가장자리 잘림을 막는다 -->
|
||||
|
|
@ -131,17 +141,52 @@ const { c } = Astro.props;
|
|||
.tile:nth-child(n + 2) .swatch { aspect-ratio: 16 / 6; }
|
||||
.tile { display: grid; gap: var(--space-2); align-content: start; }
|
||||
|
||||
/* 필터를 먹일 대상. 같은 도형에 서로 다른 재질을 입힌다 */
|
||||
/* 필터를 먹일 대상.
|
||||
한때 셋 다 같은 줄무늬였다. 필터가 무엇을 하는지는 보였지만
|
||||
그것을 왜 쓰는지는 보이지 않았다 — 줄무늬는 실무에서 쓰는 소재가 아니다.
|
||||
이제 각 필터를 그 필터가 실제로 걸리는 자리에 올린다. */
|
||||
.swatch {
|
||||
aspect-ratio: 4 / 3;
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 그레인 — 매끈한 그라디언트 위. 노이즈가 없으면 밋밋하다는 게 요점이다 */
|
||||
.swatch-grain {
|
||||
background: linear-gradient(148deg,
|
||||
color-mix(in oklab, var(--accent) 82%, var(--surface)),
|
||||
color-mix(in oklab, var(--glow-2) 62%, var(--surface)));
|
||||
}
|
||||
|
||||
/* 굴절 — 글자 위. 유리 뒤의 것이 휘는 걸 보여줘야 의미가 산다 */
|
||||
.swatch-glass {
|
||||
background: linear-gradient(100deg,
|
||||
color-mix(in oklab, var(--surface) 82%, var(--ink)),
|
||||
color-mix(in oklab, var(--accent) 30%, var(--surface)));
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.swatch-type {
|
||||
font-size: clamp(1.1rem, 0.6rem + 1.6vw, 1.9rem);
|
||||
font-weight: var(--weight-strong);
|
||||
letter-spacing: var(--tracking-display);
|
||||
color: var(--ink);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 듀오톤 — 사진. 출처가 다른 사진을 한 팔레트로 묶는 게 실제 용도다 */
|
||||
.swatch-duotone {
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
58deg,
|
||||
var(--accent) 0 14px,
|
||||
color-mix(in oklab, var(--accent) 55%, var(--surface)) 14px 28px
|
||||
);
|
||||
radial-gradient(58% 74% at 30% 28%, color-mix(in oklab, var(--ink) 90%, transparent), transparent 70%),
|
||||
radial-gradient(52% 60% at 74% 68%, color-mix(in oklab, var(--ink-muted) 70%, transparent), transparent 66%),
|
||||
linear-gradient(160deg, color-mix(in oklab, var(--surface) 40%, var(--ink-muted)), var(--surface-sunken));
|
||||
}
|
||||
|
||||
.tile-use {
|
||||
margin: 0;
|
||||
color: var(--accent);
|
||||
font-size: var(--step-0);
|
||||
}
|
||||
.swatch-grain { filter: url(#f-grain); }
|
||||
.swatch-glass { filter: url(#f-glass); }
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ const { c } = Astro.props;
|
|||
position: sticky;
|
||||
top: 0;
|
||||
height: 100dvh;
|
||||
/* 섹션 패딩(112px) 아래에서 시작하면 히어로 배경이 끝난 자리와
|
||||
이 캔버스 사이에 200px 짜리 검은 띠가 생긴다. 패딩만큼 끌어올린다. */
|
||||
margin-top: calc(var(--space-7) * -1);
|
||||
margin-bottom: -100dvh;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
|
|
@ -89,6 +92,12 @@ const { c } = Astro.props;
|
|||
radial-gradient(42% 50% at 76% 68%,
|
||||
color-mix(in oklab, var(--glow-2) 18%, transparent), transparent 68%);
|
||||
}
|
||||
/* 캔버스가 섹션 패딩 위까지 덮으면 이웃 섹션과 딱 붙어 보인다.
|
||||
위아래를 지워 여백을 되돌린다. */
|
||||
.gate-bg {
|
||||
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 6%, black 88%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, transparent 0%, black 6%, black 88%, transparent 100%);
|
||||
}
|
||||
.gate-canvas { opacity: 0; transition: opacity 1s var(--ease-soft); }
|
||||
.gate-canvas.is-live { opacity: 0.7; }
|
||||
/* 세로 화면에서는 캔버스가 화면 전체를 덮는다. 같은 밝기면 본문이 묻힌다 */
|
||||
|
|
@ -148,7 +157,15 @@ const { c } = Astro.props;
|
|||
|
||||
.stop {
|
||||
position: relative;
|
||||
padding-block: var(--space-4);
|
||||
/* 정거장 간격이 곧 3D 게이트의 스크롤 구간이다.
|
||||
space-4(24px)로는 섹션이 1493px 밖에 안 돼 sticky 이동 거리가 593px 뿐이었다.
|
||||
섹션이 화면에 보이는 구간(2393px)의 4분의 1이라, 조금만 스크롤해도
|
||||
여섯 게이트를 다 지나쳐 버렸다.
|
||||
|
||||
space-6 으로 늘렸더니 sticky 구간이 1226px, 게이트당 204px 이 됐다.
|
||||
휠 한 번(100~150px)에 게이트 하나가 지나가 여전히 과민했다.
|
||||
space-7 이면 게이트당 300px 남짓 — 휠 두세 번에 하나다. */
|
||||
padding-block: var(--space-7);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
|
||||
gap: var(--space-5);
|
||||
|
|
@ -205,8 +222,10 @@ const { c } = Astro.props;
|
|||
}
|
||||
|
||||
.after {
|
||||
margin-top: var(--space-5);
|
||||
margin-top: var(--space-7);
|
||||
max-width: var(--measure);
|
||||
/* 마지막 게이트를 통과할 스크롤이 남아 있어야 한다 */
|
||||
padding-bottom: 20dvh;
|
||||
}
|
||||
|
||||
@media (max-width: 780px) {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export interface Content {
|
|||
eyebrow: string;
|
||||
h2: string;
|
||||
lead: string;
|
||||
tiles: { id: "grain" | "glass" | "duotone"; name: string; how: string; cost: string }[];
|
||||
tiles: { id: "grain" | "glass" | "duotone"; name: string; use: string; how: string; cost: string }[];
|
||||
note: string;
|
||||
};
|
||||
metrics: { eyebrow: string; h2: string; lead: string; items: Metric[]; caveat: string };
|
||||
|
|
@ -216,31 +216,34 @@ export const ko: Content = {
|
|||
},
|
||||
materials: {
|
||||
eyebrow: "재료",
|
||||
h2: "표면은 CSS 가 정하고, 공간은 GPU 가 맡는다",
|
||||
h2: "텍스처 200KB 를 300바이트가 대신한다",
|
||||
lead:
|
||||
"아래 배경은 지금 셰이더가 그리고 있고, 세 장의 타일은 같은 줄무늬 하나에 서로 다른 SVG 필터를 먹인 것이다. 스킬이 다루는 재료를 글로 설명하는 대신 화면에서 돌린다.",
|
||||
"필터는 장식이 아니라 재료다. 셋 다 실제로 쓰이는 자리에 걸어 뒀다 — 그레인은 매끈한 면에, 굴절은 유리 뒤 글자에, 듀오톤은 사진에. 배경은 지금 three.js 셰이더가 그리고 있다.",
|
||||
tiles: [
|
||||
{
|
||||
id: "grain",
|
||||
name: "종이",
|
||||
how: "feTurbulence 로 만든 프랙탈 노이즈를 알파로 눌러 표면에 얹는다.",
|
||||
cost: "비용 낮음 · 정지 상태",
|
||||
name: "그레인",
|
||||
use: "매끈한 그라디언트가 싸구려로 보일 때",
|
||||
how: "feTurbulence 가 만든 프랙탈 노이즈를 알파로 눌러 표면에 얹는다. 이미지 요청이 한 건도 늘지 않는다.",
|
||||
cost: "300바이트 · 정지 상태",
|
||||
},
|
||||
{
|
||||
id: "glass",
|
||||
name: "유리",
|
||||
how: "난류를 변위 지도로 써서 굴절시킨다. 필터 영역을 넓혀 가장자리 잘림을 막는다.",
|
||||
cost: "비용 중간 · 애니메이션 금지",
|
||||
name: "굴절",
|
||||
use: "유리 뒤의 글자가 실제로 휘어야 할 때",
|
||||
how: "난류를 변위 지도로 써서 픽셀을 민다. 필터 영역을 넓혀야 가장자리가 잘리지 않는다.",
|
||||
cost: "중간 · 애니메이션 금지",
|
||||
},
|
||||
{
|
||||
id: "duotone",
|
||||
name: "인쇄",
|
||||
how: "명도만 남긴 뒤 feComponentTransfer 로 두 색 사이에 다시 매핑한다.",
|
||||
cost: "비용 낮음 · 사진에도 쓴다",
|
||||
name: "듀오톤",
|
||||
use: "출처가 제각각인 사진을 한 팔레트로 묶을 때",
|
||||
how: "명도만 남긴 뒤 feComponentTransfer 로 두 색 사이에 다시 매핑한다. 원본을 보정하지 않는다.",
|
||||
cost: "낮음 · 사진에 쓴다",
|
||||
},
|
||||
],
|
||||
note:
|
||||
"위 세 장은 SVG 필터가 지금 먹고 있는 화면이고, 이 페이지의 배경 광은 three.js 셰이더가 그리고 있다. three 는 초기 번들에 없다. 첫 화면이 그려진 뒤에 받고, WebGL 이 없거나 모션을 줄이는 설정이면 아예 받지 않는다. 그때 보이는 그라디언트가 폴백이 아니라 그대로 결과물이다.",
|
||||
"이 셋은 SVG 필터가 지금 먹고 있는 화면이고, 그 뒤 배경은 three.js 셰이더가 그리고 있다. three 는 초기 번들에 없다 — 화면에 가까워질 때만 받고, WebGL 이 없거나 모션을 줄이는 설정이면 아예 받지 않는다. 그때 보이는 그라디언트는 폴백이 아니라 그대로 결과물이다."
|
||||
},
|
||||
metrics: {
|
||||
eyebrow: "이 페이지의 수치",
|
||||
|
|
@ -447,31 +450,34 @@ export const en: Content = {
|
|||
},
|
||||
materials: {
|
||||
eyebrow: "Materials",
|
||||
h2: "CSS decides the surface. The GPU handles the space.",
|
||||
h2: "300 bytes replaces a 200KB texture",
|
||||
lead:
|
||||
"The backdrop below is being drawn by a shader right now, and the three tiles are one striped fill wearing three different SVG filters. The skill's materials are running here rather than described.",
|
||||
"Filters are material, not decoration. Each one sits where it is actually used — grain on a flat gradient, refraction behind glass over type, duotone on a photograph. The backdrop is a three.js shader, running now.",
|
||||
tiles: [
|
||||
{
|
||||
id: "grain",
|
||||
name: "Paper",
|
||||
how: "Fractal noise from feTurbulence, pushed down through alpha and laid over the surface.",
|
||||
cost: "low cost · static",
|
||||
name: "Grain",
|
||||
use: "when a smooth gradient reads as cheap",
|
||||
how: "feTurbulence generates fractal noise, pushed down through alpha onto the surface. Not one extra image request.",
|
||||
cost: "300 bytes · static",
|
||||
},
|
||||
{
|
||||
id: "glass",
|
||||
name: "Glass",
|
||||
how: "Turbulence used as a displacement map. The filter region is widened so edges do not clip.",
|
||||
cost: "medium cost · never animate it",
|
||||
name: "Refraction",
|
||||
use: "when type behind glass has to actually bend",
|
||||
how: "Turbulence drives a displacement map that pushes pixels. Widen the filter region or the edges clip.",
|
||||
cost: "medium · never animate",
|
||||
},
|
||||
{
|
||||
id: "duotone",
|
||||
name: "Print",
|
||||
how: "Reduce to luminance, then remap between two colours with feComponentTransfer.",
|
||||
cost: "low cost · works on photography too",
|
||||
name: "Duotone",
|
||||
use: "when photos from different sources need one palette",
|
||||
how: "Keep luminance only, then remap between two colours with feComponentTransfer. The source file is untouched.",
|
||||
cost: "low · use it on photos",
|
||||
},
|
||||
],
|
||||
note:
|
||||
"The three tiles above are SVG filters running right now, and the light behind this page is a three.js shader. three is not in the initial bundle. It arrives after the first paint, and never at all without WebGL or with reduced motion requested. What you see then is not a fallback. It is the result.",
|
||||
"These three are SVG filters running on live pixels, and the field behind them is a three.js shader. three is not in the initial bundle — it arrives only as the section approaches, and never at all when WebGL is missing or motion is reduced. What you see then is not a fallback; it is the result."
|
||||
},
|
||||
metrics: {
|
||||
eyebrow: "This page, measured",
|
||||
|
|
|
|||
|
|
@ -129,6 +129,40 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. **
|
|||
카피에서 제품·회사 이름을 **경쟁사 이름으로 바꿔 읽는다.**
|
||||
**여전히 자연스러우면 그 카피는 아무것도 말하지 않은 것이다. 다시 써라.**
|
||||
|
||||
### 작법 — 금지 목록만으로는 못 잡는다
|
||||
|
||||
위의 지문 목록은 **나쁜 문장**을 걸러낸다. 그런데 지문에 하나도 안 걸리면서
|
||||
**아무것도 전달하지 않는 문장**이 있다. 대개 대구가 예뻐서 통과된다.
|
||||
|
||||
> 실측 사례: `"표면은 CSS 가 정하고, 공간은 GPU 가 맡는다"`
|
||||
> 치환 테스트 통과(다른 제품엔 안 맞는다), 금칙어 없음, 리듬도 좋다.
|
||||
> 그런데 **독자가 얻는 것이 없다.** 구현 이야기를 대구로 포장한 것뿐이다.
|
||||
|
||||
**세 가지로 거른다.**
|
||||
|
||||
**1. `so you can…` 테스트** — 문장 뒤에 이어 붙여 완성해봐라.
|
||||
|
||||
```
|
||||
"feTurbulence 로 노이즈를 만든다" so you can → 200KB 텍스처를 300바이트로 줄인다 ✅
|
||||
"표면은 CSS 가 정하고 공간은 GPU 가 맡는다" so you can → ??? ❌
|
||||
```
|
||||
|
||||
완성되지 않으면 기능만 쓴 것이다. **완성된 뒷부분을 헤드라인으로 올려라.**
|
||||
|
||||
**2. 6~12단어** — 2초 안에 훑히면서 가치를 전달하는 폭이다.
|
||||
한국어는 어절 기준으로 6~12, 글자 수로는 12~28자.
|
||||
|
||||
**3. 헤딩에 검증 가능한 것을 넣어라** — 숫자·조건·비교 대상 중 하나.
|
||||
|
||||
| 고치기 전 | 고친 뒤 | 무엇이 달라졌나 |
|
||||
|---|---|---|
|
||||
| 표면은 CSS 가 정하고, 공간은 GPU 가 맡는다 | **텍스처 200KB 를 300바이트가 대신한다** | 구현 → 이득. 수치가 검증 가능 |
|
||||
| Powerful Features | 엑셀 없이 정산이 끝난다 | 명사구 → 주장 |
|
||||
| 빠르고 안정적인 배포 | 배포 실패가 롤백까지 90초 | 형용사 → 측정치 |
|
||||
|
||||
**대구·대조·리듬은 마지막에 얹는 것이다.** 먼저 말할 내용을 정하고,
|
||||
그것이 정해진 뒤에도 대구가 성립하면 그때 써라. 대구부터 잡으면 내용이 비어도 모른다.
|
||||
|
||||
### 구문 지문
|
||||
|
||||
| 지문 | 왜 문제 | 대신 |
|
||||
|
|
|
|||
|
|
@ -434,3 +434,50 @@ if (reduceEffects()) document.documentElement.classList.add('reduce-effects');
|
|||
- `width: 200%` — 요소보다 크게 잡아야 납작한 형태에서도 모서리까지 닿는다
|
||||
|
||||
`prefers-reduced-motion` 에서는 애니메이션만 빼면 된다. 정지 상태의 광은 그대로 남는다.
|
||||
|
||||
---
|
||||
|
||||
## 데모는 추상 도형이 아니라 실무 소재에 걸어라
|
||||
|
||||
필터를 소개할 때 줄무늬·체크무늬 같은 도형에 걸면 **필터가 무엇을 하는지는 보이지만
|
||||
그것을 왜 쓰는지는 보이지 않는다.** 보는 사람이 "그래서?" 하고 넘어간다.
|
||||
|
||||
**각 필터를 그 필터가 실제로 걸리는 자리에 올려라.**
|
||||
|
||||
| 필터 | 실무 소재 | 그 소재라야 보이는 것 |
|
||||
|---|---|---|
|
||||
| `feTurbulence` 그레인 | **매끈한 그라디언트 면** | 노이즈가 없으면 밋밋하다는 대비 |
|
||||
| `feDisplacementMap` 굴절 | **글자** | 유리 뒤의 것이 실제로 휘는 것 |
|
||||
| `feColorMatrix` 듀오톤 | **사진** | 출처가 다른 사진이 한 팔레트로 묶이는 것 |
|
||||
|
||||
실측: 셋 다 같은 줄무늬에 걸었더니 "이게 어필이 되는 예시냐"는 지적을 받았다.
|
||||
소재를 바꾸자 설명 문장 없이도 용도가 읽혔다.
|
||||
|
||||
**어필 포인트는 효과가 아니라 대체 비용이다.** `feTurbulence` 의 진짜 매력은
|
||||
"노이즈가 예쁘다"가 아니라 **200KB JPEG 텍스처를 300바이트 코드가 대신한다**는 것이다.
|
||||
카피에 그 수치를 써라.
|
||||
|
||||
## `feComposite` 입력 순서를 뒤집으면 필터가 조용히 죽는다
|
||||
|
||||
```xml
|
||||
<!-- 틀림: 원본이 노이즈 위에 올라가 노이즈를 통째로 가린다 -->
|
||||
<feComposite in="SourceGraphic" in2="soft" operator="over" />
|
||||
```
|
||||
|
||||
`operator="over"` 는 **`in` 을 `in2` 위에** 올린다. 원본을 `in` 에 두면 효과가 전부 덮인다.
|
||||
**에러도 경고도 없다.** 필터는 정상적으로 적용되고 결과만 원본과 같다.
|
||||
|
||||
그레인처럼 표면에 **섞이는** 효과는 합성이 아니라 블렌드다.
|
||||
|
||||
```xml
|
||||
<feBlend in="SourceGraphic" in2="soft" mode="overlay" />
|
||||
```
|
||||
|
||||
그리고 `baseFrequency` 를 0.8 이상으로 올리면 노이즈가 픽셀보다 촘촘해져
|
||||
화면에서 회색으로 뭉갠다. 그레인은 **0.5~0.7** 이 보인다.
|
||||
|
||||
**필터를 붙였는데 "아무 변화가 없다"면 의심 순서는 이렇다.**
|
||||
|
||||
1. `in`/`in2` 순서가 뒤집혔는가
|
||||
2. `baseFrequency` 가 너무 높은가
|
||||
3. 필터 영역(`x`/`y`/`width`/`height`)이 좁아 결과가 잘렸는가
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue