react-liquid-glass-view
Version:
A beautiful glass morphism effect component for React applications
343 lines (338 loc) • 14.5 kB
JavaScript
"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
});