Skip to content

Instantly share code, notes, and snippets.

@amw
Created November 3, 2022 15:42
Show Gist options
  • Save amw/842b1087167c9f682e3bd5144c4836df to your computer and use it in GitHub Desktop.
Save amw/842b1087167c9f682e3bd5144c4836df to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>Broken CSS Filters Referencing SVG</title>
<style>
body {
display: grid;
grid-template-columns: max-content 25vh;
grid-template-rows: 2em 25vh 25vh 25vh;
align-items: center;
gap: 0 2em;
}
img, svg {
height: 25vh;
}
</style>
</head>
<body>
<label>Gamma</label>
<input id="slider" type="range" min="0.01" max="2" step="0.01" value="1">
<label>Created #refFilter:</label>
<div id="container"></div>
<label>SVG using #refFilter:</label>
<svg id="gammaTarget1" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="exampleGradient">
<stop offset="5%" stop-color="#333"></stop>
<stop offset="95%" stop-color="#ccc"></stop>
</linearGradient>
</defs>
<circle cx="5" cy="5" r="5" fill="url('#exampleGradient')" />
</svg>
<label>IMG using #refFilter:</label>
<img id="gammaTarget2" src="data:image/svg+xml,%3Csvg viewBox='0 0 10 10' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3ClinearGradient id='g'%3E%3Cstop offset='5%25' stop-color='%23333'%3E%3C/stop%3E%3Cstop offset='95%25' stop-color='%23ccc'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx='5' cy='5' r='5' fill='url(%23g)' /%3E%3C/svg%3E" />
</body>
<script>
class GammaSlider {
constructor(element) {
this.value = '1.00'
this.filterId = 'refFilter'
this.createFilter()
this.updateRequest = null
element.value = this.value
element.addEventListener('input', (event) => this.onslide(event))
}
onslide(event) {
this.value = event.target.value
this.updateFilter()
const elements = Array.from(document.querySelectorAll(`.${this.filterId}`))
for ( const element of elements ) {
element.style.filter = ''
element.style.webkitFilter = ''
}
if ( !this.updateRequest ) {
this.updateRequest = requestAnimationFrame(() => {
for ( const element of elements ) {
element.style.filter = `url(#${this.filterId})`
element.style.webkitFilter = `url(#${this.filterId})`
}
this.updateRequest = null
})
}
}
appliesTo(element) {
element.style.filter = `url(#${this.filterId})`
element.style.webkitFilter = `url(#${this.filterId})`
element.classList.add(this.filterId)
}
createFilter() {
if ( this.svg ) {
this.svg.remove()
}
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svg.viewBox.baseVal.width = 10
svg.viewBox.baseVal.height = 10
svg.innerHTML =
`<filter id="${this.filterId}">
<feComponentTransfer>
<feFuncR type="gamma" exponent="${this.value}"></feFuncR>
<feFuncG type="gamma" exponent="${this.value}"></feFuncG>
<feFuncB type="gamma" exponent="${this.value}"></feFuncB>
</feComponentTransfer>
</filter>
<defs>
<linearGradient id="localGradient">
<stop offset="5%" stop-color="#333" />
<stop offset="95%" stop-color="#ccc" />
</linearGradient>
</defs>
<circle cx="5" cy="5" r="5" fill="url(#localGradient)" filter="url(#${this.filterId})" />`
this.svg = svg
this.filter = svg.querySelector('filter')
document.querySelector('#container').appendChild(svg)
}
updateFilter() {
this.filter.innerHTML =
`<feComponentTransfer>
<feFuncR type="gamma" exponent="${this.value}"></feFuncR>
<feFuncG type="gamma" exponent="${this.value}"></feFuncG>
<feFuncB type="gamma" exponent="${this.value}"></feFuncB>
</feComponentTransfer>`
}
}
const slider = new GammaSlider(document.querySelector('#slider'))
slider.appliesTo(document.querySelector('#gammaTarget1'))
slider.appliesTo(document.querySelector('#gammaTarget2'))
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment