chromatic-blur
Version:
A lightweight, zero-dependency JavaScript plugin for creating stunning chromatic aberration blur effects
17 lines • 6.59 kB
JavaScript
/**
* ChromaticBlur - A vanilla JavaScript plugin for creating chromatic aberration blur effects
*
* Modern best practices implemented:
* - ES6+ module pattern with class-based architecture
* - No external dependencies
* - Declarative API with sensible defaults
* - Method chaining support
* - Automatic cleanup and memory management
* - Accessible (doesn't interfere with screen readers)
* - Performance optimized (reuses SVG filters)
* - TypeScript-friendly JSDoc comments
*
* @version 1.0.0
* @license MIT
*/
class e{static defaults={redOffset:5,blueOffset:-5,blurAmount:3,turbulenceFrequency:.001,displacementScale:50,borderColor:"rgba(156, 156, 156, 0.2)",addOverlay:!0,addNoise:!0};static instances=new Set;constructor(t,n={}){if(this.element="string"==typeof t?document.querySelector(t):t,!this.element)throw new Error("ChromaticBlur: Invalid element provided");this.options={...e.defaults,...n},this.id=`chromatic-blur-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,this.originalStyles={backdropFilter:this.element.style.backdropFilter,WebkitBackdropFilter:this.element.style.WebkitBackdropFilter,position:this.element.style.position,overflow:this.element.style.overflow},this._init(),e.instances.add(this)}_init(){this._ensureSVGContainer(),this._createFilter(),this._applyStyles(),(this.options.addOverlay||this.options.addNoise)&&this._addOverlays()}_ensureSVGContainer(){let e=document.getElementById("chromatic-blur-filters");if(!e){e=document.createElementNS("http://www.w3.org/2000/svg","svg"),e.id="chromatic-blur-filters",e.style.cssText="position:absolute;width:0;height:0;pointer-events:none",e.setAttribute("aria-hidden","true");const t=document.createElementNS("http://www.w3.org/2000/svg","defs");e.appendChild(t),document.body.insertBefore(e,document.body.firstChild)}this.svgContainer=e,this.defs=e.querySelector("defs")}_createFilter(){const e=document.createElementNS("http://www.w3.org/2000/svg","filter");e.id=this.id,e.setAttribute("x","-50%"),e.setAttribute("y","-50%"),e.setAttribute("width","200%"),e.setAttribute("height","200%"),e.innerHTML=`\n \x3c!-- Turbulence for organic noise --\x3e\n <feTurbulence\n baseFrequency="${this.options.turbulenceFrequency}"\n numOctaves="1"\n type="turbulence"\n result="turbulence" />\n\n \x3c!-- RED CHANNEL: Offset to the right --\x3e\n <feOffset\n in="SourceGraphic"\n dx="${this.options.redOffset}"\n dy="0"\n result="redOffset" />\n <feColorMatrix\n in="redOffset"\n type="matrix"\n values="1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0"\n result="red" />\n\n \x3c!-- GREEN CHANNEL: No offset --\x3e\n <feColorMatrix\n in="SourceGraphic"\n type="matrix"\n values="0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0"\n result="green" />\n\n \x3c!-- BLUE CHANNEL: Offset to the left --\x3e\n <feOffset\n in="SourceGraphic"\n dx="${this.options.blueOffset}"\n dy="0"\n result="blueOffset" />\n <feColorMatrix\n in="blueOffset"\n type="matrix"\n values="0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0"\n result="blue" />\n\n \x3c!-- Combine red and green --\x3e\n <feComposite\n in="red"\n in2="green"\n operator="arithmetic"\n k2="1"\n k3="1"\n result="redGreen" />\n\n \x3c!-- Combine with blue --\x3e\n <feComposite\n in="redGreen"\n in2="blue"\n operator="arithmetic"\n k2="1"\n k3="1"\n result="colorSplit" />\n\n \x3c!-- Apply displacement for wavy distortion --\x3e\n <feDisplacementMap\n in="colorSplit"\n in2="turbulence"\n scale="${this.options.displacementScale}"\n result="displacement" />\n\n \x3c!-- Final blur --\x3e\n <feGaussianBlur\n in="displacement"\n stdDeviation="${this.options.blurAmount}"\n result="blurred" />\n `,this.defs.appendChild(e),this.filter=e}_applyStyles(){const e=`url(#${this.id})`,t="static"===window.getComputedStyle(this.element).position;Object.assign(this.element.style,{backdropFilter:e,WebkitBackdropFilter:e,overflow:"hidden",boxShadow:`0 0 0 1px ${this.options.borderColor} inset`}),t&&(this.element.style.position="relative")}_addOverlays(){const e=document.createElement("div");if(e.className="chromatic-blur-overlays",e.style.cssText="\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n border-radius: inherit;\n ",this.options.addNoise){const t=document.createElement("div");t.className="chromatic-blur-noise",t.style.cssText="\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n mix-blend-mode: overlay;\n background-color: rgba(255, 255, 255, 0);\n opacity: 0.05;\n border-radius: inherit;\n ",e.appendChild(t)}if(this.options.addOverlay){const t=document.createElement("div");t.className="chromatic-blur-gradient",t.style.cssText="\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-image: linear-gradient(225deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%);\n mix-blend-mode: screen;\n pointer-events: none;\n border-radius: inherit;\n ",e.appendChild(t)}this.element.insertBefore(e,this.element.firstChild),this.overlayContainer=e}update(e){return this.options={...this.options,...e},this.filter&&this.filter.remove(),this._createFilter(),this._applyStyles(),this}destroy(){if(this.filter&&this.filter.remove(),this.overlayContainer&&this.overlayContainer.remove(),Object.assign(this.element.style,this.originalStyles),e.instances.delete(this),0===e.instances.size){const e=document.getElementById("chromatic-blur-filters");e&&e.remove()}}enable(){return this.element.style.backdropFilter=`url(#${this.id})`,this.element.style.WebkitBackdropFilter=`url(#${this.id})`,this}disable(){return this.element.style.backdropFilter="none",this.element.style.WebkitBackdropFilter="none",this}static destroyAll(){e.instances.forEach(e=>e.destroy())}}"undefined"!=typeof module&&module.exports&&(module.exports=e),"function"==typeof define&&define.amd&&define([],()=>e),"undefined"!=typeof window&&(window.ChromaticBlur=e);export default e;