UNPKG

@trap_stevo/legendarybuilderproreact-ui

Version:

The legendary UI & utility API that makes your application a legendary application. ~ Created by Steven Compton

561 lines 19.6 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _typeof from "@babel/runtime/helpers/typeof"; import { useState, useEffect, useRef, useMemo } from "react"; import { useDeepMemoizeCompare } from "./HUDUniversalHUDDynamicsUtilityManager.js"; var defaultOrigin = typeof window !== "undefined" ? { x: window.innerWidth / 2, y: window.innerHeight / 2 } : { x: 0, y: 0 }; function clamp01(v) { return Math.max(0, Math.min(1, v)); } ; function getVector(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; var length = Math.hypot(dx, dy); return length === 0 ? { x: 0, y: 0, magnitude: 0 } : { x: dx / length, y: dy / length, magnitude: length }; } ; function getPolarCoords(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; var radius = Math.hypot(dx, dy); var thetaRadians = Math.atan2(dy, dx); var thetaDegrees = thetaRadians * 180 / Math.PI; var theta360 = (thetaDegrees + 360) % 360; return { radius: radius, thetaRadians: thetaRadians, thetaDegrees: thetaDegrees, theta360: theta360 }; } ; function smoothLerp(a, b, factor) { return a + (b - a) * factor; } ; function smoothVector(from, to, factor) { return { x: smoothLerp(from.x, to.x, factor), y: smoothLerp(from.y, to.y, factor) }; } ; function parseGranularity(input) { if (typeof input === "number") { return 360 / input; } if (typeof input === "string") { if (input.endsWith("dir")) { return parseInt(input) || 16; } if (input.endsWith("deg")) { return Math.round(360 / parseFloat(input)); } } return 16; } ; function defaultLabelForSegment(segment, totalSegments) { var arrows = ["→", "↘", "↓", "↙", "←", "↖", "↑", "↗"]; return totalSegments === 8 ? arrows[segment % 8] : "".concat((segment * 360 / totalSegments).toFixed(1), "\xB0"); } ; function classifyByGranularity(angle360, divisions) { var customLabels = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var segment = Math.round(angle360 / (360 / divisions)) % divisions; return Array.isArray(customLabels) && customLabels.length === divisions ? customLabels[segment] : defaultLabelForSegment(segment, divisions); } ; function areVectorsEqual(v1, v2) { var threshold = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.001; return Math.abs(v1.x - v2.x) < threshold && Math.abs(v1.y - v2.y) < threshold; } ; function areAnglesEqual(a1, a2) { var threshold = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.1; return Math.abs(a1 - a2) < threshold; } ; function normalizeCapture(options) { if (typeof options === "boolean") { return options; } if (_typeof(options) === "object" && options !== null) { return !!options.capture; } return false; } ; export function maxRadiusFromOrigin(origin) { if (typeof window === "undefined") { return 1; } var h = window.innerHeight; var w = window.innerWidth; return Math.min(origin.x, origin.y, w - origin.x, h - origin.y); } ; export function distanceToUnit(polarRadius, origin) { var minDistance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 5; var maxR = Math.max(1, maxRadiusFromOrigin(origin)); return clamp01((polarRadius - minDistance) / (maxR - minDistance)); } ; export function ArrivedAtCoordinate(from, to) { var threshold = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var dx = Math.abs(from.x - to.x); var dy = Math.abs(from.y - to.y); return dx <= threshold && dy <= threshold; } ; export function GetDirectionToTarget(from, to) { var dx = to.x - from.x; var dy = from.y - to.y; var radians = Math.atan2(dy, dx); var degrees = radians * 180 / Math.PI; return (degrees + 360) % 360; } ; export function useCursorDirection() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _options$origin = options.origin, origin = _options$origin === void 0 ? defaultOrigin : _options$origin, _options$minDistance = options.minDistance, minDistance = _options$minDistance === void 0 ? 5 : _options$minDistance, _options$smooth = options.smooth, smooth = _options$smooth === void 0 ? true : _options$smooth, _options$lerpFactor = options.lerpFactor, lerpFactor = _options$lerpFactor === void 0 ? 0.15 : _options$lerpFactor, _options$directionGra = options.directionGranularity, directionGranularity = _options$directionGra === void 0 ? "16dir" : _options$directionGra, _options$customLabels = options.customLabels, customLabels = _options$customLabels === void 0 ? null : _options$customLabels, _options$eventOptions = options.eventOptions, eventOptions = _options$eventOptions === void 0 ? { touchstart: { passive: true }, touchmove: { passive: true }, touchend: { passive: true }, mousemove: false } : _options$eventOptions; var _useState = useState({ radius: 0, thetaRadians: 0, thetaDegrees: 0, theta360: 0 }), _useState2 = _slicedToArray(_useState, 2), polar = _useState2[0], setPolar = _useState2[1]; var _useState3 = useState({ x: 0, y: 0 }), _useState4 = _slicedToArray(_useState3, 2), mousePosition = _useState4[0], setMousePosition = _useState4[1]; var _useState5 = useState({ x: 0, y: 0 }), _useState6 = _slicedToArray(_useState5, 2), cartesian = _useState6[0], setCartesian = _useState6[1]; var _useState7 = useState(null), _useState8 = _slicedToArray(_useState7, 2), directionLabel = _useState8[0], setDirectionLabel = _useState8[1]; var vectorRef = useRef({ x: 0, y: 0 }); var touchingRef = useRef(false); var originRef = useRef(origin); var lockRef = useRef(false); var labelRef = useRef(null); var angleRef = useRef(0); var eventOptionsUpdated = useDeepMemoizeCompare(eventOptions); var opts = useMemo(function () { var _eventOptions$touchst, _eventOptions$mousemo, _eventOptions$touchmo, _eventOptions$touchen; return { touchstart: (_eventOptions$touchst = eventOptions.touchstart) !== null && _eventOptions$touchst !== void 0 ? _eventOptions$touchst : false, mousemove: (_eventOptions$mousemo = eventOptions.mousemove) !== null && _eventOptions$mousemo !== void 0 ? _eventOptions$mousemo : false, touchmove: (_eventOptions$touchmo = eventOptions.touchmove) !== null && _eventOptions$touchmo !== void 0 ? _eventOptions$touchmo : false, touchend: (_eventOptions$touchen = eventOptions.touchend) !== null && _eventOptions$touchen !== void 0 ? _eventOptions$touchen : false }; }, [eventOptionsUpdated]); useEffect(function () { originRef.current = origin; }, [origin]); useEffect(function () { var mounted = true; var frameId = 0; function safeUpdate(x, y) { if (lockRef.current || !mounted) { return; } lockRef.current = true; if (frameId) { cancelAnimationFrame(frameId); } frameId = requestAnimationFrame(function () { lockRef.current = false; if (!mounted) { return; } setMousePosition({ x: x, y: y }); var _getPolarCoords = getPolarCoords(originRef.current.x, originRef.current.y, x, y), radius = _getPolarCoords.radius, thetaRadians = _getPolarCoords.thetaRadians, thetaDegrees = _getPolarCoords.thetaDegrees, theta360 = _getPolarCoords.theta360; if (radius < minDistance) { if (polar.radius !== 0) { setPolar({ radius: 0, thetaRadians: 0, thetaDegrees: 0, theta360: 0 }); setCartesian({ x: 0, y: 0 }); setDirectionLabel(null); } return; } var rawVec = getVector(originRef.current.x, originRef.current.y, x, y); var vec = smooth ? smoothVector(vectorRef.current, rawVec, lerpFactor) : rawVec; if (!areVectorsEqual(vec, vectorRef.current)) { vectorRef.current = vec; setCartesian(vec); } if (!areAnglesEqual(theta360, angleRef.current)) { angleRef.current = theta360; setPolar({ radius: radius, thetaRadians: thetaRadians, thetaDegrees: thetaDegrees, theta360: theta360 }); var divisions = parseGranularity(directionGranularity); var label = classifyByGranularity(theta360, divisions, customLabels); if (label !== labelRef.current) { labelRef.current = label; setDirectionLabel(label); } } }); } function handleMouseMove(e) { if (!touchingRef.current) { safeUpdate(e.clientX, e.clientY); } } function handleTouchStart() { touchingRef.current = true; } function handleTouchMove(e) { var touch = e.touches[0]; if (touch) { safeUpdate(touch.clientX, touch.clientY); } } function handleTouchEnd() { touchingRef.current = false; setPolar({ radius: 0, thetaRadians: 0, thetaDegrees: 0, theta360: 0 }); setMousePosition({ x: 0, y: 0 }); setCartesian({ x: 0, y: 0 }); setDirectionLabel(null); } window.addEventListener("touchstart", handleTouchStart, opts.touchstart); window.addEventListener("mousemove", handleMouseMove, opts.mousemove); window.addEventListener("touchmove", handleTouchMove, opts.touchmove); window.addEventListener("touchend", handleTouchEnd, opts.touchend); return function () { mounted = false; if (frameId) { cancelAnimationFrame(frameId); } window.removeEventListener("touchstart", handleTouchStart, normalizeCapture(opts.touchstart)); window.removeEventListener("mousemove", handleMouseMove, normalizeCapture(opts.mousemove)); window.removeEventListener("touchmove", handleTouchMove, normalizeCapture(opts.touchmove)); window.removeEventListener("touchend", handleTouchEnd, normalizeCapture(opts.touchend)); }; }, [eventOptionsUpdated, minDistance, smooth, lerpFactor, directionGranularity, customLabels]); return { directionLabel: directionLabel, mousePosition: mousePosition, cartesian: cartesian, polar: polar }; } ; export function useDirectionalMovement() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, id = _ref.id, origin = _ref.origin, sendMovement = _ref.sendMovement, _ref$directionGranula = _ref.directionGranularity, directionGranularity = _ref$directionGranula === void 0 ? "16dir" : _ref$directionGranula, _ref$radialBezier = _ref.radialBezier, radialBezier = _ref$radialBezier === void 0 ? [0.25, 0.1, 0.25, 1.0] : _ref$radialBezier, _ref$radialLogisticMi = _ref.radialLogisticMid, radialLogisticMid = _ref$radialLogisticMi === void 0 ? 0.5 : _ref$radialLogisticMi, _ref$radialLogisticK = _ref.radialLogisticK, radialLogisticK = _ref$radialLogisticK === void 0 ? 6.0 : _ref$radialLogisticK, _ref$radialMode = _ref.radialMode, radialMode = _ref$radialMode === void 0 ? "easeInOut" : _ref$radialMode, _ref$radialPower = _ref.radialPower, radialPower = _ref$radialPower === void 0 ? 1.0 : _ref$radialPower, _ref$shapeOnClient = _ref.shapeOnClient, shapeOnClient = _ref$shapeOnClient === void 0 ? false : _ref$shapeOnClient, _ref$angleEpsilon = _ref.angleEpsilon, angleEpsilon = _ref$angleEpsilon === void 0 ? 0.002 : _ref$angleEpsilon, _ref$onlyOnChange = _ref.onlyOnChange, onlyOnChange = _ref$onlyOnChange === void 0 ? true : _ref$onlyOnChange, _ref$distEpsilon = _ref.distEpsilon, distEpsilon = _ref$distEpsilon === void 0 ? 0.005 : _ref$distEpsilon, _ref$throttleHz = _ref.throttleHz, throttleHz = _ref$throttleHz === void 0 ? 60 : _ref$throttleHz, _ref$lerpFactor = _ref.lerpFactor, lerpFactor = _ref$lerpFactor === void 0 ? 0.069 : _ref$lerpFactor, _ref$minDistance = _ref.minDistance, minDistance = _ref$minDistance === void 0 ? 5 : _ref$minDistance, _ref$deadZone = _ref.deadZone, deadZone = _ref$deadZone === void 0 ? 0.08 : _ref$deadZone, _ref$exponent = _ref.exponent, exponent = _ref$exponent === void 0 ? 1.20 : _ref$exponent, _ref$smooth = _ref.smooth, smooth = _ref$smooth === void 0 ? true : _ref$smooth; var _useCursorDirection = useCursorDirection({ directionGranularity: directionGranularity, minDistance: minDistance, lerpFactor: lerpFactor, origin: origin, smooth: smooth }), polar = _useCursorDirection.polar; var lastPayloadRef = useRef({ theta: 0, dist: 0 }); var lastEmitTimeRef = useRef(0); var polarRef = useRef(polar); var rafIdRef = useRef(0); var cfgRef = useRef({ id: id, origin: origin, sendMovement: sendMovement, directionGranularity: directionGranularity, radialBezier: radialBezier, radialLogisticMid: radialLogisticMid, radialLogisticK: radialLogisticK, radialMode: radialMode, radialPower: radialPower, shapeOnClient: shapeOnClient, angleEpsilon: angleEpsilon, onlyOnChange: onlyOnChange, distEpsilon: distEpsilon, throttleHz: throttleHz, lerpFactor: lerpFactor, minDistance: minDistance, deadZone: deadZone, exponent: exponent, smooth: smooth }); useEffect(function () { polarRef.current = polar; }, [polar]); useEffect(function () { cfgRef.current = { id: id, origin: origin, sendMovement: sendMovement, directionGranularity: directionGranularity, radialBezier: radialBezier, radialLogisticMid: radialLogisticMid, radialLogisticK: radialLogisticK, radialMode: radialMode, radialPower: radialPower, shapeOnClient: shapeOnClient, angleEpsilon: angleEpsilon, onlyOnChange: onlyOnChange, distEpsilon: distEpsilon, throttleHz: throttleHz, lerpFactor: lerpFactor, minDistance: minDistance, deadZone: deadZone, exponent: exponent, smooth: smooth }; }, [id, origin, sendMovement, directionGranularity, radialBezier, radialLogisticMid, radialLogisticK, radialMode, radialPower, shapeOnClient, angleEpsilon, onlyOnChange, distEpsilon, throttleHz, lerpFactor, minDistance, deadZone, exponent, smooth]); useEffect(function () { var twoPI = Math.PI * 2; var wrapDeltaToPi = function wrapDeltaToPi(d) { return ((d + Math.PI) % twoPI + twoPI) % twoPI - Math.PI; }; var clamp01Local = function clamp01Local(v) { return v < 0 ? 0 : v > 1 ? 1 : v; }; var dzCurve = function dzCurve(raw, dz, exp) { if (raw <= dz) { return 0; } var scaled = (raw - dz) / (1 - dz); var curved = Math.pow(scaled, Math.max(0.0001, exp || 1)); return clamp01Local(curved); }; var cubic = function cubic(t, a, b, c, d) { return Math.pow(1 - t, 3) * a + 3 * Math.pow(1 - t, 2) * t * b + 3 * (1 - t) * Math.pow(t, 2) * c + Math.pow(t, 3) * d; }; var applyRadialExpansion = function applyRadialExpansion(raw, mode, power, k, mid, bezier) { var r = clamp01Local(raw); switch (mode) { case "pow": { return clamp01Local(Math.pow(r, Math.max(0.0001, power || 1))); } case "easeIn": { return clamp01Local(Math.pow(r, 2.2)); } case "easeOut": { var inv = 1 - r; return clamp01Local(1 - Math.pow(inv, 2.2)); } case "easeInOut": { if (r < 0.5) { return clamp01Local(0.5 * Math.pow(r * 2, 2.2)); } else { var x = (1 - r) * 2; return clamp01Local(1 - 0.5 * Math.pow(x, 2.2)); } } case "logistic": { var kk = Number.isFinite(k) ? k : 6.0; var xm = clamp01Local(mid !== null && mid !== void 0 ? mid : 0.5); var y = 1 / (1 + Math.exp(-kk * (r - xm))); var y0 = 1 / (1 + Math.exp(-kk * (0 - xm))); var y1 = 1 / (1 + Math.exp(-kk * (1 - xm))); return clamp01Local((y - y0) / Math.max(1e-6, y1 - y0)); } case "bezier": { var _ref2 = Array.isArray(bezier) && bezier.length === 4 ? bezier : [0.25, 0.1, 0.25, 1.0], _ref3 = _slicedToArray(_ref2, 4), x1 = _ref3[0], _y = _ref3[1], x2 = _ref3[2], y2 = _ref3[3]; var _y2 = cubic(r, 0, _y, y2, 1); return clamp01Local(_y2); } default: { return r; } } }; var loop = function loop() { var _cfg$origin; var tNow = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var p = polarRef.current || { radius: 0, thetaRadians: 0 }; var cfg = cfgRef.current; var theta = p.thetaRadians || 0; var revTheta = -theta; var center = (_cfg$origin = cfg.origin) !== null && _cfg$origin !== void 0 ? _cfg$origin : { x: window.innerWidth / 2, y: window.innerHeight / 2 }; var distUnit = clamp01Local(distanceToUnit(p.radius, center, cfg.minDistance)); var distExp = applyRadialExpansion(distUnit, cfg.radialMode, cfg.radialPower, cfg.radialLogisticK, cfg.radialLogisticMid, cfg.radialBezier); var distanceFromCenter = cfg.shapeOnClient ? dzCurve(distExp, clamp01Local(cfg.deadZone || 0), cfg.exponent || 1) : distExp; if (cfg.throttleHz && cfg.throttleHz > 0) { var minDeltaMs = 1000 / cfg.throttleHz; if (tNow - lastEmitTimeRef.current < minDeltaMs) { rafIdRef.current = requestAnimationFrame(loop); return; } } if (cfg.onlyOnChange) { var prev = lastPayloadRef.current; var dTheta = wrapDeltaToPi(revTheta - prev.theta); var dDist = distanceFromCenter - prev.dist; if (Math.abs(dTheta) > cfg.angleEpsilon || Math.abs(dDist) > cfg.distEpsilon) { var _cfg$sendMovement; lastPayloadRef.current = { theta: revTheta, dist: distanceFromCenter }; lastEmitTimeRef.current = tNow; (_cfg$sendMovement = cfg.sendMovement) === null || _cfg$sendMovement === void 0 || _cfg$sendMovement.call(cfg, { id: cfg.id, direction: revTheta, distanceFromCenter: distanceFromCenter }); } } else { var _cfg$sendMovement2; lastPayloadRef.current = { theta: revTheta, dist: distanceFromCenter }; lastEmitTimeRef.current = tNow; (_cfg$sendMovement2 = cfg.sendMovement) === null || _cfg$sendMovement2 === void 0 || _cfg$sendMovement2.call(cfg, { id: cfg.id, direction: revTheta, distanceFromCenter: distanceFromCenter }); } rafIdRef.current = requestAnimationFrame(loop); }; rafIdRef.current = requestAnimationFrame(loop); return function () { if (rafIdRef.current) { cancelAnimationFrame(rafIdRef.current); } }; }, []); } ;