Ink bleed effect
Credit https://andyjakubowski.com/tutorial/ink-bleed-effect-with-svg-filters
<label>
Blur
<input
id="blur-slider"
type="range"
name="blur"
min="0"
max="5"
step="0.1"
value="2"
/>
<span id="blur-label"></span>
</label>
<label
>Threshold
<input
id="threshold-slider"
type="range"
name="threshold"
min="10"
max="90"
step="10"
value="50"
/>
<span id="threshold-label"></span>
</label>
<h1></h1>
<h1 class="blur"></h1>
<h1 class="blur-and-threshold"></h1>
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="svgThreshold">
<feComponentTransfer>
<feFuncG class="transfer-func" type="discrete" tableValues="0 1 1" />
<feFuncB class="transfer-func" type="discrete" tableValues="0 1 1" />
<feFuncA class="transfer-func" type="discrete" tableValues="0 1 1" />
<feFuncR class="transfer-func" type="discrete" tableValues="0 1 1" />
</feComponentTransfer>
</filter>
</svg>
<style>
body {
display: flex;
flex-direction: column;
}
h1 {
font-size: 64px;
margin: 0;
}
h1::before {
content: "Friendliness";
}
.blur {
filter: blur(var(--blur));
}
.blur-and-threshold {
filter: blur(var(--blur)) url(#svgThreshold);
}
</style>
<script>
const table = {
0: "0",
10: "0 1 1 1 1 1 1 1 1 1",
20: "0 0 1 1 1 1 1 1 1 1",
30: "0 0 0 1 1 1 1 1 1 1",
40: "0 0 0 0 1 1 1 1 1 1",
50: "0 1",
60: "0 0 0 0 0 0 1 1 1 1",
70: "0 0 0 0 0 0 0 1 1 1",
80: "0 0 0 0 0 0 0 0 1 1",
90: "0 0 0 0 0 0 0 0 0 1",
100: "1",
}
function handleDOMContentLoaded() {
const transferFuncs = Array.from(
document.getElementsByClassName("transfer-func"),
)
const blurSlider = document.getElementById("blur-slider")
const thresholdSlider = document.getElementById("threshold-slider")
const blurLabel = document.getElementById("blur-label")
const thresholdLabel = document.getElementById("threshold-label")
document.documentElement.style.setProperty(
"--blur",
`${blurSlider.getAttribute("value")}px`,
)
transferFuncs.forEach((func) =>
func.setAttribute(
"tableValues",
table[thresholdSlider.getAttribute("value")],
),
)
blurLabel.textContent = `${blurSlider.getAttribute("value")}px`
thresholdLabel.textContent = thresholdSlider.getAttribute("value")
blurSlider.addEventListener("input", () => {
blurLabel.textContent = `${blurSlider.value}px`
document.documentElement.style.setProperty(
"--blur",
`${blurSlider.value}px`,
)
})
thresholdSlider.addEventListener("input", () => {
thresholdLabel.textContent = thresholdSlider.value
transferFuncs.forEach((func) => {
func.setAttribute("tableValues", table[thresholdSlider.value])
})
})
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", handleDOMContentLoaded)
} else {
handleDOMContentLoaded()
}
</script>