react-dvd-screensaver
Version:
DVD-era nostalgia in React
152 lines (147 loc) • 5.6 kB
JavaScript
import React, { useRef, useState, useEffect, useLayoutEffect, cloneElement } from 'react';
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
function useWindowSize() {
const getSize = () => ({
width: window.innerWidth,
height: window.innerHeight
});
const [windowSize, setWindowSize] = React.useState(getSize);
React.useLayoutEffect(() => {
const onResize = () => setWindowSize(getSize);
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return windowSize;
}
// src/useDvdScreensaver.ts
function useDvdScreensaver(options) {
const { width: windowWidth } = useWindowSize();
const animationRef = useRef({
animationFrameId: 0,
containerHeight: 0,
containerWidth: 0,
impactCount: 0,
isPosXIncrement: true,
isPosYIncrement: true,
positionX: Math.random() * windowWidth,
positionY: Math.random() * windowWidth
});
const elementRef = useRef(null);
const containerRef = useRef(null);
const [impactCount, setImpactCount] = useState(0);
const [hovered, setHovered] = useState(false);
function updatePosition(containerSpan, delta, elementSpan, prevPos, toggleRefKey) {
const parentBoundary = containerSpan - elementSpan;
let newPos = prevPos + (animationRef.current[toggleRefKey] ? delta : -delta);
if (newPos <= 0 || newPos >= parentBoundary) {
animationRef.current[toggleRefKey] = !animationRef.current[toggleRefKey];
animationRef.current.impactCount += 1;
setImpactCount(animationRef.current.impactCount);
newPos = newPos <= 0 ? 0 : parentBoundary;
}
return newPos;
}
function animate() {
if (elementRef.current && elementRef.current.parentElement) {
const containerHeight = elementRef.current.parentElement.clientHeight;
const containerWidth = elementRef.current.parentElement.clientWidth;
const elementHeight = elementRef.current.clientHeight;
const elementWidth = elementRef.current.clientWidth;
const delta = (options == null ? void 0 : options.speed) || 2;
const posX = updatePosition(
containerWidth,
delta,
elementWidth,
animationRef.current.positionX,
"isPosXIncrement"
);
const posY = updatePosition(
containerHeight,
delta,
elementHeight,
animationRef.current.positionY,
"isPosYIncrement"
);
elementRef.current.style.transform = `translate3d(${posX}px, ${posY}px, 0)`;
animationRef.current.positionX = posX;
animationRef.current.positionY = posY;
}
animationRef.current.animationFrameId = requestAnimationFrame(animate);
}
useEffect(() => {
if (options == null ? void 0 : options.freezeOnHover) {
if (hovered) {
cancelAnimationFrame(animationRef.current.animationFrameId);
animationRef.current.animationFrameId = 0;
}
if (!hovered && !animationRef.current.animationFrameId) {
animationRef.current.animationFrameId = requestAnimationFrame(animate);
}
}
if (options == null ? void 0 : options.hoverCallback) {
options.hoverCallback();
}
}, [hovered, options]);
useLayoutEffect(() => {
const element = elementRef.current;
const handleMouseOver = () => setHovered(true);
const handleMouseOut = () => setHovered(false);
if (element) {
element.style.willChange = "transform";
element.addEventListener("mouseover", handleMouseOver);
element.addEventListener("mouseout", handleMouseOut);
animationRef.current.animationFrameId = requestAnimationFrame(animate);
}
return () => {
if (element) {
element.removeEventListener("mouseover", handleMouseOver);
element.removeEventListener("mouseout", handleMouseOut);
}
cancelAnimationFrame(animationRef.current.animationFrameId);
};
}, []);
return {
containerRef,
elementRef,
hovered,
impactCount
};
}
// src/DvdScreensaver.tsx
function DvdScreensaver(props) {
const { elementRef, impactCount } = useDvdScreensaver({
freezeOnHover: props.freezeOnHover,
hoverCallback: props.hoverCallback,
speed: props.speed
});
useEffect(() => {
if (props.impactCallback) {
props.impactCallback(impactCount);
}
}, [impactCount, props.impactCallback]);
const enhancedStyle = __spreadProps(__spreadValues({}, props.style), {
width: (props == null ? void 0 : props.width) || "100%",
height: (props == null ? void 0 : props.height) || "100%"
});
const childWithRef = cloneElement(props.children, { ref: elementRef });
return /* @__PURE__ */ React.createElement("div", { className: props.className, style: enhancedStyle }, childWithRef);
}
export { DvdScreensaver, useDvdScreensaver };