@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
JavaScript
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);
}
};
}, []);
}
;