22 * LiqGlass - Beautiful Glass Morphism Library
33 * A comprehensive JavaScript library for creating stunning glass effects
44 *
5- * @version 1.0.0
6- * @author LiqGlass Library
7- * @license MIT
5+ * @version 1.2.0
86 */
97
108( function ( global , factory ) {
3634 * @property {boolean } [hoverEffect=false] - Enable hover effects
3735 * @property {string } [element='div'] - HTML element type to create
3836 * @property {string } [className=''] - CSS class name to apply
37+ * @property {string } [svgFilter] - SVG filter markup to inject
38+ * @property {string } [svgFilterId] - ID of the SVG filter to apply
3939 */
4040
4141 /**
6262 this . colorPresets = new Map ( ) ;
6363 this . generatedStyles = new Set ( ) ;
6464 this . styleElement = null ;
65+ this . svgFilters = new Map ( ) ;
66+ this . elementFilterMap = new WeakMap ( ) ;
6567
6668 this . initializeDefaultPresets ( ) ;
6769 this . initializeColorPresets ( ) ;
150152 gradientFrom : 'rgba(255,255,255,0.3)' ,
151153 gradientTo : 'rgba(255,255,255,0.1)'
152154 } ) ;
155+
156+ this . presets . set ( 'refraction' , {
157+ blur : 10 ,
158+ opacity : 0.08 ,
159+ borderRadius : 20 ,
160+ borderWidth : 1 ,
161+ borderOpacity : 0.15 ,
162+ shadowIntensity : 'medium' ,
163+ gradient : true ,
164+ gradientFrom : 'rgba(255,255,255,0.15)' ,
165+ gradientTo : 'rgba(255,255,255,0.05)' ,
166+ svgFilter : '<svg style="display: none"><filter id="lg-dist" x="0%" y="0%" width="100%" height="100%"><feTurbulence type="fractalNoise" baseFrequency="0.008 0.008" numOctaves="2" seed="92" result="noise" /><feGaussianBlur in="noise" stdDeviation="2" result="blurred" /><feDisplacementMap in="SourceGraphic" in2="blurred" scale="70" xChannelSelector="R" yChannelSelector="G" /></filter></svg>' ,
167+ svgFilterId : 'lg-dist'
168+ } ) ;
169+
170+ this . presets . set ( 'distortion' , {
171+ blur : 8 ,
172+ opacity : 0.12 ,
173+ borderRadius : 16 ,
174+ borderWidth : 1 ,
175+ borderOpacity : 0.2 ,
176+ shadowIntensity : 'heavy' ,
177+ gradient : true ,
178+ gradientFrom : 'rgba(255,255,255,0.2)' ,
179+ gradientTo : 'rgba(255,255,255,0.08)' ,
180+ svgFilter : '<svg style="display: none"><filter id="glass-distortion" x="0%" y="0%" width="100%" height="100%" filterUnits="objectBoundingBox"><feTurbulence type="fractalNoise" baseFrequency="0.01 0.01" numOctaves="1" seed="5" result="turbulence" /><feComponentTransfer in="turbulence" result="mapped"><feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5" /><feFuncG type="gamma" amplitude="0" exponent="1" offset="0" /><feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5" /></feComponentTransfer><feGaussianBlur in="turbulence" stdDeviation="3" result="softMap" /><feSpecularLighting in="softMap" surfaceScale="5" specularConstant="1" specularExponent="100" lighting-color="white" result="specLight"><fePointLight x="-200" y="-200" z="300" /></feSpecularLighting><feComposite in="specLight" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litImage" /><feDisplacementMap in="SourceGraphic" in2="softMap" scale="150" xChannelSelector="R" yChannelSelector="G" /></filter></svg>' ,
181+ svgFilterId : 'glass-distortion'
182+ } ) ;
153183 }
154184
155185 /**
278308
279309 Object . assign ( element . style , styles ) ;
280310
311+ // Handle SVG filters
312+ if ( config . svgFilter && config . svgFilterId ) {
313+ this . applySVGFilter ( element , config . svgFilter , config . svgFilterId ) ;
314+ }
315+
281316 // Add base class
282317 element . classList . add ( 'liqglass-base' ) ;
283318
291326 }
292327 }
293328
329+ /**
330+ * Apply SVG filter to an element
331+ * @param {HTMLElement } element - Target element
332+ * @param {string } svgFilter - SVG filter markup
333+ * @param {string } filterId - Filter ID
334+ * @private
335+ */
336+ applySVGFilter ( element , svgFilter , filterId ) {
337+ if ( typeof document === 'undefined' ) return ;
338+
339+ // Check if filter already exists
340+ if ( ! this . svgFilters . has ( filterId ) ) {
341+ // Create SVG element
342+ const svgContainer = document . createElement ( 'div' ) ;
343+ svgContainer . innerHTML = svgFilter ;
344+ const svgElement = svgContainer . firstElementChild ;
345+
346+ if ( svgElement ) {
347+ document . body . appendChild ( svgElement ) ;
348+ this . svgFilters . set ( filterId , svgElement ) ;
349+ }
350+ }
351+
352+ // Track element for cleanup
353+ this . elementFilterMap . set ( element , filterId ) ;
354+
355+ // Set up cleanup observer
356+ this . setupElementCleanup ( element , filterId ) ;
357+ }
358+
359+ /**
360+ * Setup cleanup observer for element removal
361+ * @param {HTMLElement } element - Element to observe
362+ * @param {string } filterId - Filter ID
363+ * @private
364+ */
365+ setupElementCleanup ( element , filterId ) {
366+ if ( typeof window === 'undefined' || ! window . MutationObserver ) return ;
367+
368+ const observer = new MutationObserver ( ( mutations ) => {
369+ for ( const mutation of mutations ) {
370+ if ( mutation . type === 'childList' ) {
371+ for ( const removedNode of Array . from ( mutation . removedNodes ) ) {
372+ if ( removedNode === element || ( removedNode . contains && removedNode . contains ( element ) ) ) {
373+ this . cleanupSVGFilter ( filterId ) ;
374+ observer . disconnect ( ) ;
375+ return ;
376+ }
377+ }
378+ }
379+ }
380+ } ) ;
381+
382+ observer . observe ( document . body , {
383+ childList : true ,
384+ subtree : true
385+ } ) ;
386+ }
387+
388+ /**
389+ * Cleanup SVG filter when no longer needed
390+ * @param {string } filterId - Filter ID
391+ * @private
392+ */
393+ cleanupSVGFilter ( filterId ) {
394+ const svgElement = this . svgFilters . get ( filterId ) ;
395+ if ( svgElement && svgElement . parentNode ) {
396+ // Check if any other elements are using this filter
397+ const elementsWithFilter = document . querySelectorAll ( `[style*="url(#${ filterId } )"]` ) ;
398+ if ( elementsWithFilter . length === 0 ) {
399+ svgElement . parentNode . removeChild ( svgElement ) ;
400+ this . svgFilters . delete ( filterId ) ;
401+ }
402+ }
403+ }
404+
405+ /**
406+ * Remove SVG filter from an element
407+ * @param {HTMLElement } element - Element to remove filter from
408+ */
409+ removeSVGFilter ( element ) {
410+ const filterId = this . elementFilterMap . get ( element ) ;
411+ if ( filterId ) {
412+ // Remove filter style
413+ if ( element . style . filter ) {
414+ element . style . filter = element . style . filter . replace ( `url(#${ filterId } )` , '' ) . trim ( ) ;
415+ if ( ! element . style . filter ) {
416+ element . style . removeProperty ( 'filter' ) ;
417+ }
418+ }
419+
420+ this . elementFilterMap . delete ( element ) ;
421+ this . cleanupSVGFilter ( filterId ) ;
422+ }
423+ }
424+
294425 /**
295426 * Generate inline styles for a glass configuration
296427 * @param {GlassConfig } [config={}] - Glass configuration options
307438 gradient = false ,
308439 gradientFrom = 'rgba(255,255,255,0.1)' ,
309440 gradientTo = 'rgba(255,255,255,0.05)' ,
441+ svgFilterId
310442 } = config ;
311443
312444 const shadowMap = {
316448 heavy : '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)' ,
317449 } ;
318450
319- return {
451+ const styles = {
320452 backdropFilter : `blur(${ blur } px)` ,
321453 WebkitBackdropFilter : `blur(${ blur } px)` ,
322454 background : gradient
328460 position : 'relative' ,
329461 overflow : 'hidden' ,
330462 } ;
463+
464+ // Add SVG filter if specified
465+ if ( svgFilterId ) {
466+ styles . filter = `url(#${ svgFilterId } )` ;
467+ }
468+
469+ return styles ;
331470 }
332471
333472 /**
@@ -505,6 +644,16 @@ ${cssProperties}
505644 <h3>Colored Glass</h3>
506645 <p>Beautiful gradients with color presets.</p>
507646 </div>
647+
648+ <div class="liqglass-base liqglass-refraction liqglass-card liqglass-animated liqglass-hover">
649+ <h3>Refraction Glass</h3>
650+ <p>Advanced glass with refraction effects.</p>
651+ </div>
652+
653+ <div class="liqglass-base liqglass-distortion liqglass-card liqglass-animated liqglass-hover">
654+ <h3>Distortion Glass</h3>
655+ <p>Complex distortion with lighting effects.</p>
656+ </div>
508657 </div>
509658 </div>
510659
@@ -513,22 +662,28 @@ ${cssProperties}
513662 const liqGlass = new LiqGlass();
514663 console.log('LiqGlass v' + liqGlass.getVersion() + ' initialized');
515664
516- // Example: Create a dynamic glass element
517- const dynamicGlass = liqGlass.createGlassElement({
518- blur: 18,
519- opacity: 0.2,
520- borderRadius: 25,
521- gradient: true,
522- gradientFrom: 'rgba(147, 51, 234, 0.3)',
523- gradientTo: 'rgba(79, 70, 229, 0.1)',
665+ // Example: Create dynamic glass elements with new presets
666+ const refractionGlass = liqGlass.createGlassElement({
667+ ...liqGlass.getPreset('refraction'),
668+ className: 'liqglass-card',
524669 animated: true,
525- hoverEffect: true,
526- className: 'liqglass-card'
670+ hoverEffect: true
527671 });
672+ refractionGlass.innerHTML = '<h3>Dynamic Refraction</h3><p>Created with JavaScript!</p>';
673+ refractionGlass.style.marginTop = '2rem';
528674
529- dynamicGlass.innerHTML = '<h3>Dynamic Glass</h3><p>Created with JavaScript!</p>';
530- dynamicGlass.style.marginTop = '2rem';
531- document.querySelector('.container').appendChild(dynamicGlass);
675+ const distortionGlass = liqGlass.createGlassElement({
676+ ...liqGlass.getPreset('distortion'),
677+ className: 'liqglass-card',
678+ animated: true,
679+ hoverEffect: true
680+ });
681+ distortionGlass.innerHTML = '<h3>Dynamic Distortion</h3><p>Complex SVG filters in action!</p>';
682+ distortionGlass.style.marginTop = '1rem';
683+
684+ const container = document.querySelector('.container');
685+ container.appendChild(refractionGlass);
686+ container.appendChild(distortionGlass);
532687 </script>
533688</body>
534689</html>` ;
@@ -564,9 +719,18 @@ ${cssProperties}
564719 document . head . removeChild ( this . styleElement ) ;
565720 this . styleElement = null ;
566721 }
722+
723+ // Clean up all SVG filters
724+ for ( const [ filterId , svgElement ] of this . svgFilters ) {
725+ if ( svgElement . parentNode ) {
726+ svgElement . parentNode . removeChild ( svgElement ) ;
727+ }
728+ }
729+
567730 this . presets . clear ( ) ;
568731 this . colorPresets . clear ( ) ;
569732 this . generatedStyles . clear ( ) ;
733+ this . svgFilters . clear ( ) ;
570734 }
571735 }
572736
@@ -604,21 +768,25 @@ ${cssProperties}
604768 * borderRadius: 20
605769 * });
606770 *
607- * // Using singleton
608- * const glassSingleton = LiqGlass.getInstance();
609- * glassSingleton.applyGlassStyles(document.getElementById('myDiv '), {
610- * preset : 'frosted '
771+ * // Using new SVG filter presets
772+ * const refractionElement = glass.createGlassElement({
773+ * ...glass.getPreset('refraction '),
774+ * className : 'my-refraction-glass '
611775 * });
612776 *
613- * // Generate CSS
614- * const css = glass.generateVanillaCSS();
777+ * const distortionElement = glass.createGlassElement({
778+ * ...glass.getPreset('distortion'),
779+ * className: 'my-distortion-glass'
780+ * });
615781 *
616- * // Create custom presets
617- * glass.addPreset('custom', {
618- * blur: 25,
619- * opacity: 0.3,
620- * gradient: true,
621- * gradientFrom: 'rgba(255, 0, 0, 0.2)',
622- * gradientTo: 'rgba(255, 100, 100, 0.1)'
782+ * // Custom SVG filter
783+ * const customElement = glass.createGlassElement({
784+ * blur: 10,
785+ * opacity: 0.15,
786+ * svgFilter: '<svg style="display: none"><filter id="custom-filter">...</filter></svg>',
787+ * svgFilterId: 'custom-filter'
623788 * });
789+ *
790+ * // Remove SVG filter when needed
791+ * glass.removeSVGFilter(customElement);
624792 */
0 commit comments