UNPKG

react-liquid-glass-view

Version:

A beautiful glass morphism effect component for React applications

343 lines (338 loc) 14.5 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.tsx var src_exports = {}; __export(src_exports, { LiquidGlassView: () => LiquidGlassView }); module.exports = __toCommonJS(src_exports); // src/liquid-glass-view.tsx var import_react = __toESM(require("react")); var import_react2 = require("react"); var import_framer_motion = require("framer-motion"); function LiquidGlassView({ children, width = 100, height = 100, cornerRadius = 25, darknessOpacity = 17, darknessBlur = 5, lightnessOpacity = 17, lightnessBlur = 15, centerDistortion = 68, centerSize = 15, preBlur = 7, postBlur = 0, iridescence = 20, className = "", style = {}, draggable = false, dragAnimation = true, transitionAnimationSpring = false }) { const id = (0, import_react2.useId)(); const [filterId] = (0, import_react2.useState)(`liquid-glass-filter-${id.replace(/:/g, "")}`); const [dragDirection, setDragDirection] = (0, import_react2.useState)("none"); const lastDragPos = (0, import_react2.useRef)({ x: 0, y: 0 }); const containerWidth = width; const containerHeight = height; const containerRef = (0, import_react2.useRef)(null); const svgRef = (0, import_react2.useRef)(null); const thing9Ref = (0, import_react2.useRef)(null); const thing0Ref = (0, import_react2.useRef)(null); const thing1Ref = (0, import_react2.useRef)(null); const thing2Ref = (0, import_react2.useRef)(null); const preblurRef = (0, import_react2.useRef)(null); const postblurRef = (0, import_react2.useRef)(null); const dispRRef = (0, import_react2.useRef)(null); const dispGRef = (0, import_react2.useRef)(null); const dispBRef = (0, import_react2.useRef)(null); (0, import_react2.useEffect)(() => { if (!svgRef.current) return; svgRef.current.setAttribute("width", `${width}`); svgRef.current.setAttribute("height", `${height}`); svgRef.current.setAttribute("viewBox", `0 0 ${width} ${height}`); const glassWidth = width; const glassHeight = height; const glassX = 0; const glassY = 0; if (thing9Ref.current) { thing9Ref.current.setAttributeNS( "http://www.w3.org/1999/xlink", "href", `data:image/svg+xml,%3Csvg width='${width}' height='${height}' viewBox='0 0 ${width} ${height}' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='rgb%280 0 0 %2F${darknessOpacity / 2.55}%25%29' /%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='%23FFF' style='filter:blur(${darknessBlur}px)' /%3E%3C/svg%3E` ); } if (thing0Ref.current) { thing0Ref.current.setAttributeNS( "http://www.w3.org/1999/xlink", "href", `data:image/svg+xml,%3Csvg width='${width}' height='${height}' viewBox='0 0 ${width} ${height}' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='rgb%28255 255 255 %2F${lightnessOpacity / 2.55}%25%29' style='filter:blur(${lightnessBlur}px)' /%3E%3C/svg%3E` ); } if (thing1Ref.current) { thing1Ref.current.setAttributeNS( "http://www.w3.org/1999/xlink", "href", `data:image/svg+xml,%3Csvg width='${width}' height='${height}' viewBox='0 0 ${width} ${height}' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='%23000' /%3E%3C/svg%3E` ); } if (thing2Ref.current) { thing2Ref.current.setAttributeNS( "http://www.w3.org/1999/xlink", "href", `data:image/svg+xml,%3Csvg width='${width}' height='${height}' viewBox='0 0 ${width} ${height}' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3ClinearGradient id='gradient1' x1='0%25' y1='0%25' x2='100%25' y2='0%25'%3E%3Cstop offset='0%25' stop-color='%23000'/%3E%3Cstop offset='100%25' stop-color='%2300F'/%3E%3C/linearGradient%3E%3ClinearGradient id='gradient2' x1='0%25' y1='0%25' x2='0%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%23000'/%3E%3Cstop offset='100%25' stop-color='%230F0'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect x='0' y='0' width='${width}' height='${height}' rx='${cornerRadius}' fill='%237F7F7F' /%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='%23000' /%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='url(%23gradient1)' style='mix-blend-mode: screen' /%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='url(%23gradient2)' style='mix-blend-mode: screen' /%3E%3Crect x='${glassX}' y='${glassY}' width='${glassWidth}' height='${glassHeight}' rx='${cornerRadius}' fill='rgb%28127 127 127 %2F${(255 - centerDistortion) / 2.55}%25%29' style='filter:blur(${20 - centerSize}px)' /%3E%3C/svg%3E` ); } if (preblurRef.current) { preblurRef.current.setAttribute("stdDeviation", `${preBlur / 10}`); } if (postblurRef.current) { postblurRef.current.setAttribute("stdDeviation", `${postBlur / 10}`); } if (dispRRef.current) { dispRRef.current.setAttribute("scale", `${-150 + iridescence / 10}`); } if (dispGRef.current) { dispGRef.current.setAttribute("scale", `-150`); } if (dispBRef.current) { dispBRef.current.setAttribute("scale", `${-150 - iridescence / 10}`); } }, [ width, height, cornerRadius, darknessOpacity, darknessBlur, lightnessOpacity, lightnessBlur, centerDistortion, centerSize, preBlur, postBlur, iridescence ]); const handleDragStart = () => { lastDragPos.current = { x: 0, y: 0 }; setDragDirection("none"); }; const handleDrag = (event, info) => { const xDiff = Math.abs(info.offset.x - lastDragPos.current.x); const yDiff = Math.abs(info.offset.y - lastDragPos.current.y); if (dragDirection === "none" && (xDiff > 5 || yDiff > 5)) { setDragDirection(xDiff > yDiff ? "horizontal" : "vertical"); } lastDragPos.current = { x: info.offset.x, y: info.offset.y }; }; const handleDragEnd = () => { setDragDirection("none"); }; return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement( import_framer_motion.motion.div, { ref: containerRef, drag: draggable, dragMomentum: false, dragElastic: 0.1, initial: false, animate: { width: containerWidth, height: containerHeight, opacity: 1, scale: 1 }, transition: transitionAnimationSpring ? { width: { type: "spring", damping: 15, stiffness: 300, mass: 0.8 }, height: { type: "spring", damping: 15, stiffness: 300, mass: 0.8 }, scale: { type: "spring", damping: 20, stiffness: 250 } } : { duration: 0 }, exit: { opacity: 0, scale: 0.9, transition: { type: "spring", damping: 25, stiffness: 300, duration: 0.3 } }, whileHover: draggable ? { scale: 1.05, transition: { type: "spring", damping: 20, stiffness: 300 } } : {}, whileDrag: dragAnimation ? dragDirection === "horizontal" ? { scaleX: 1.15, scaleY: 1.15, transition: { type: "spring", damping: 2, stiffness: 20, mass: 0.1, restSpeed: 0.2, restDelta: 0.01, bounce: 2 } } : dragDirection === "vertical" ? { scaleX: 1.15, scaleY: 1.15, transition: { type: "spring", damping: 2, stiffness: 20, mass: 0.1, restSpeed: 0.2, restDelta: 0.01, bounce: 2 } } : { scale: 1 } : void 0, onDragStart: handleDragStart, onDrag: handleDrag, onDragEnd: handleDragEnd, className: `liquid-glass-container relative ${className} ${draggable ? "cursor-move" : ""}`, style: { userSelect: "none", zIndex: draggable ? 100 : 10, ...style } }, /* @__PURE__ */ import_react.default.createElement("div", { className: "liquid-glass-content" }, children), /* @__PURE__ */ import_react.default.createElement( "div", { className: "liquid-glass-overlay", style: { position: "absolute", top: "-43px", left: "-43px", width: "calc(100% + 86px)", height: "calc(100% + 86px)", backdropFilter: `url(#${filterId})`, pointerEvents: "none", zIndex: 1 } } ) ), /* @__PURE__ */ import_react.default.createElement( "svg", { ref: svgRef, width, height, viewBox: `0 0 ${width} ${height}`, xmlns: "http://www.w3.org/2000/svg", style: { position: "absolute", visibility: "hidden", pointerEvents: "none" } }, /* @__PURE__ */ import_react.default.createElement("filter", { id: filterId }, /* @__PURE__ */ import_react.default.createElement("feImage", { ref: thing9Ref, xlinkHref: "", x: "0%", y: "0%", width: "100%", height: "100%", result: "thing9" }), /* @__PURE__ */ import_react.default.createElement("feImage", { ref: thing0Ref, xlinkHref: "", x: "0%", y: "0%", width: "100%", height: "100%", result: "thing0" }), /* @__PURE__ */ import_react.default.createElement("feImage", { ref: thing1Ref, xlinkHref: "", x: "0%", y: "0%", width: "100%", height: "100%", result: "thing1" }), /* @__PURE__ */ import_react.default.createElement("feImage", { ref: thing2Ref, xlinkHref: "", x: "0%", y: "0%", width: "100%", height: "100%", result: "thing2" }), /* @__PURE__ */ import_react.default.createElement("feGaussianBlur", { ref: preblurRef, stdDeviation: preBlur / 10, in: "SourceGraphic", result: "preblur" }), /* @__PURE__ */ import_react.default.createElement( "feDisplacementMap", { ref: dispRRef, in2: "thing2", in: "preblur", scale: -150 + iridescence / 10, xChannelSelector: "B", yChannelSelector: "G" } ), /* @__PURE__ */ import_react.default.createElement( "feColorMatrix", { type: "matrix", values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0", result: "disp1" } ), /* @__PURE__ */ import_react.default.createElement( "feDisplacementMap", { ref: dispGRef, in2: "thing2", in: "preblur", scale: "-150", xChannelSelector: "B", yChannelSelector: "G" } ), /* @__PURE__ */ import_react.default.createElement( "feColorMatrix", { type: "matrix", values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0", result: "disp2" } ), /* @__PURE__ */ import_react.default.createElement( "feDisplacementMap", { ref: dispBRef, in2: "thing2", in: "preblur", scale: -150 - iridescence / 10, xChannelSelector: "B", yChannelSelector: "G" } ), /* @__PURE__ */ import_react.default.createElement( "feColorMatrix", { type: "matrix", values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0", result: "disp3" } ), /* @__PURE__ */ import_react.default.createElement("feBlend", { in2: "disp2", mode: "screen" }), /* @__PURE__ */ import_react.default.createElement("feBlend", { in2: "disp1", mode: "screen" }), /* @__PURE__ */ import_react.default.createElement("feGaussianBlur", { ref: postblurRef, stdDeviation: postBlur / 10 }), /* @__PURE__ */ import_react.default.createElement("feBlend", { in2: "thing0", mode: "screen" }), /* @__PURE__ */ import_react.default.createElement("feBlend", { in2: "thing9", mode: "multiply" }), /* @__PURE__ */ import_react.default.createElement("feComposite", { in2: "thing1", operator: "in" }), /* @__PURE__ */ import_react.default.createElement("feOffset", { dx: "43", dy: "43" })) ), /* @__PURE__ */ import_react.default.createElement("style", null, ` .liquid-glass-container { overflow: hidden; border-radius: ${cornerRadius}px; } .liquid-glass-content { position: relative; z-index: 20; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; padding: 20px; } .liquid-glass-overlay { border-radius: ${cornerRadius}px; } `)); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { LiquidGlassView });