react-lottie-hook
Version:
React Lottie written with react hooks
472 lines (460 loc) • 19.1 kB
JavaScript
import { __assign, __spreadArray } from 'tslib';
import React, { useCallback, useMemo, useState, useEffect } from 'react';
import lottie from 'lottie-web';
import ReactDOM from 'react-dom';
var Lottie = function (props) {
if (!props.lottieRef) {
throw new Error("Lottie component requires a valid ref but got: " + props.lottieRef);
}
var getSize = useCallback(function (initial) {
if (initial === void 0) { initial = 200; }
return (typeof initial === "number" ? initial + "px" : initial);
}, []);
var styles = useMemo(function () { return (__assign({ width: getSize(props.width), height: getSize(props.height), overflow: "hidden", margin: "0 auto", outline: "none" }, props.style)); }, [props.style, props.width, props.height, getSize]);
return (React.createElement("div", { ref: function (r) {
props.lottieRef.current = r;
}, className: props.className, style: styles, onKeyDown: props.onKeyDown, onClick: props.onClick, title: props.title, role: props.ariaRole, "aria-label": props.ariaLabel, "tab-index": "0" }));
};
var AnimType;
(function (AnimType) {
AnimType[AnimType["svg"] = 0] = "svg";
AnimType[AnimType["canvas"] = 1] = "canvas";
AnimType[AnimType["html"] = 2] = "html";
})(AnimType || (AnimType = {}));
var Renderer;
(function (Renderer) {
Renderer["html"] = "html";
Renderer["svg"] = "svg";
Renderer["canvas"] = "canvas";
})(Renderer || (Renderer = {}));
var array = {
isPopulated: function (item) { return Array.isArray(item) && item.length > 0; }
};
var object = {
isPopulated: function (obj) {
return !!obj && typeof obj === "object" && Object.keys(obj).length > 0;
}
};
var string = {
isEmpty: function (str) { return typeof str === "string" && str.length === 0; },
isPopulated: function (str) { return typeof str === "string" && str.length > 0; }
};
var number = {
is: function (number) { return typeof number === "number"; }
};
var boolean = {
is: function (boolean) {
return typeof boolean === "boolean" || (typeof boolean === "number" && Boolean(boolean));
}
};
var removeProps = function (obj) {
var props = [];
for (var _i = 1; _i < arguments.length; _i++) {
props[_i - 1] = arguments[_i];
}
if (!object.isPopulated(obj))
return;
var list = __spreadArray([], props, true);
return Object.entries(obj).reduce(function (acc, _a) {
var _b;
var key = _a[0], value = _a[1];
if (list.includes(key))
return acc;
return __assign(__assign({}, acc), (_b = {}, _b[key] = value, _b));
}, {});
};
var useLottie = function (_a) {
var _b = _a.renderer, renderer = _b === void 0 ? Renderer.svg : _b, _c = _a.loop, loop = _c === void 0 ? true : _c, _d = _a.autoplay, autoplay = _d === void 0 ? true : _d, _e = _a.rendererSettings, rendererSettings = _e === void 0 ? {} : _e, _f = _a.segments, segments = _f === void 0 ? [] : _f, _g = _a.animationData, animationData = _g === void 0 ? {} : _g, _h = _a.eventListeners, eventListeners = _h === void 0 ? {} : _h;
var _j = useState(undefined), animation = _j[0], setAnimation = _j[1];
var lottieRef = React.useRef(null);
var _k = useState(animationData), internalAnimationData = _k[0], setInternalAnimationData = _k[1];
var _l = useState({
animType: undefined,
animationID: undefined,
assets: [],
assetsPath: undefined,
autoloadSegments: false,
autoplay: false,
currentFrame: 0,
currentRawFrame: 0,
firstFrame: 0,
frameModifier: 0,
frameMult: 0,
frameRate: 0,
initialSegment: undefined,
isPaused: false,
isLoaded: false,
isSubframeEnabled: false,
loop: undefined,
name: undefined,
path: undefined,
playCount: 0,
playDirection: 1,
playSpeed: 0,
segmentPos: 0,
segments: [],
timeCompleted: 0,
totalFrames: 0,
isStopped: false,
animationData: animationData
}), state = _l[0], dispatch = _l[1];
var hasOwnProperty = useCallback(function (anim, prop) { return object.isPopulated(anim) && !!anim[prop]; }, []);
var registerEvents = useCallback(function (anim, eventListeners) {
if (!object.isPopulated(eventListeners))
return;
Object.entries(eventListeners).forEach(function (_a) {
var eventName = _a[0], callback = _a[1];
if (hasOwnProperty(anim, "addEventListener") && callback !== undefined) {
anim === null || anim === void 0 ? void 0 : anim.addEventListener(eventName, callback);
}
});
}, [hasOwnProperty]);
var deRegisterEvents = useCallback(function (anim, eventListeners) {
if (!object.isPopulated(eventListeners))
return;
Object.entries(eventListeners).forEach(function (_a) {
var eventName = _a[0], callback = _a[1];
if (hasOwnProperty(anim, "removeEventListener")) {
anim === null || anim === void 0 ? void 0 : anim.removeEventListener(eventName, callback);
}
});
}, [hasOwnProperty]);
var animationConfig = useCallback(function (container) { return ({
container: container,
renderer: renderer,
loop: loop,
autoplay: autoplay,
rendererSettings: rendererSettings,
animationData: internalAnimationData
}); }, [renderer, loop, autoplay, rendererSettings, internalAnimationData]);
var update = function (update) {
dispatch(function (state) { return (__assign(__assign({}, state), update)); });
};
var play = useCallback(function () {
if (hasOwnProperty(animation, "play")) {
animation === null || animation === void 0 ? void 0 : animation.play();
update({
isPaused: false,
isStopped: false
});
}
}, [animation, hasOwnProperty]);
var playSegments = useCallback(function (newSegments, forceFlag) {
if (forceFlag === void 0) { forceFlag = true; }
if (hasOwnProperty(animation, "playSegments")) {
animation === null || animation === void 0 ? void 0 : animation.playSegments(newSegments || segments, forceFlag);
}
}, [animation, segments, hasOwnProperty]);
var stop = useCallback(function () {
if (hasOwnProperty(animation, "stop")) {
animation === null || animation === void 0 ? void 0 : animation.stop();
update({
isStopped: true,
isPaused: true
});
}
}, [animation, hasOwnProperty]);
var pause = useCallback(function () {
if (hasOwnProperty(animation, "pause")) {
animation === null || animation === void 0 ? void 0 : animation.pause();
update({ isPaused: true });
}
}, [animation, hasOwnProperty]);
var destroy = useCallback(function () {
if (hasOwnProperty(animation, "destroy")) {
animation === null || animation === void 0 ? void 0 : animation.destroy();
}
}, [animation, hasOwnProperty]);
var setDirection = useCallback(function (direction) {
if (direction === void 0) { direction = 1; }
if (hasOwnProperty(animation, "setDirection")) {
animation === null || animation === void 0 ? void 0 : animation.setDirection(direction);
update({ playDirection: direction });
}
}, [animation, hasOwnProperty]);
var selectAnimation = useCallback(function (newAnimation) {
if (object.isPopulated(animation) && object.isPopulated(newAnimation)) {
update({
isStopped: false,
isPaused: false,
animationData: newAnimation
});
}
}, [animation]);
var setSpeed = useCallback(function (speed) {
if (hasOwnProperty(animation, "setSpeed")) {
animation === null || animation === void 0 ? void 0 : animation.setSpeed(speed);
update({ playSpeed: speed });
}
}, [animation, hasOwnProperty]);
var resize = useCallback(function () {
if (hasOwnProperty(animation, "resize")) {
animation === null || animation === void 0 ? void 0 : animation.resize();
}
}, [animation, hasOwnProperty]);
var goToAndPlay = useCallback(function (value, isFrame) {
if (hasOwnProperty(animation, "goToAndPlay")) {
if (state.totalFrames && value > state.totalFrames) {
console.error("[goToAndPlay]: provided value " + value + " exceeds animation total frames which is " + state.totalFrames);
}
animation === null || animation === void 0 ? void 0 : animation.goToAndPlay(value, isFrame);
}
}, [animation, hasOwnProperty, state.totalFrames]);
var goToAndStop = useCallback(function (value, isFrame) {
if (hasOwnProperty(animation, "goToAndStop")) {
if (state.totalFrames && value > state.totalFrames) {
console.error("[goToAndStop]: provided value " + value + " exceeds animation total frames which is " + state.totalFrames);
}
animation === null || animation === void 0 ? void 0 : animation.goToAndStop(value, isFrame);
}
}, [animation, hasOwnProperty, state.totalFrames]);
var setSubframe = useCallback(function (useSubFrames) {
if (hasOwnProperty(animation, "setSubframe")) {
animation === null || animation === void 0 ? void 0 : animation.setSubframe(useSubFrames);
}
}, [animation, hasOwnProperty]);
var getDuration = useCallback(function (inFrames) {
if (hasOwnProperty(animation, "getDuration")) {
return animation === null || animation === void 0 ? void 0 : animation.getDuration(inFrames);
}
return 0;
}, [animation, hasOwnProperty]);
var controls = useMemo(function () { return ({
play: play,
stop: stop,
pause: pause,
resize: resize,
destroy: destroy,
setSpeed: setSpeed,
getDuration: getDuration,
setSubframe: setSubframe,
goToAndPlay: goToAndPlay,
goToAndStop: goToAndStop,
playSegments: playSegments,
setDirection: setDirection,
selectAnimation: selectAnimation
}); }, [
play,
stop,
pause,
resize,
destroy,
setSpeed,
getDuration,
setSubframe,
goToAndPlay,
goToAndStop,
setDirection,
playSegments,
selectAnimation,
]);
var filterLottieState = useCallback(function (anim) {
var props = [];
if (!string.isPopulated(anim.name))
props.push("name");
if (!string.isPopulated(anim.path))
props.push("path");
if (!string.isPopulated(anim.animType))
props.push("animType");
if (!string.isPopulated(anim.assetsPath))
props.push("assetsPath");
if (!string.isPopulated(anim.animationID))
props.push("animationID");
if (!number.is(anim.frameMult))
props.push("frameMult");
if (!number.is(anim.frameRate))
props.push("frameRate");
if (!number.is(anim.playCount))
props.push("playCount");
if (!number.is(anim.playSpeed))
props.push("playSpeed");
if (!number.is(anim.firstFrame))
props.push("firstFrame");
if (!number.is(anim.segmentPos))
props.push("segmentPos");
if (!number.is(anim.totalFrames))
props.push("totalFrames");
if (!number.is(anim.currentFrame))
props.push("currentFrame");
if (!number.is(anim.timeCompleted))
props.push("timeCompleted");
if (!number.is(anim.frameModifier))
props.push("frameModifier");
if (!number.is(anim.playDirection))
props.push("playDirection");
if (!number.is(anim.initialSegment))
props.push("initialSegment");
if (!number.is(anim.currentRawFrame))
props.push("currentRawFrame");
if (!boolean.is(anim.autoloadSegments))
props.push("autoloadSegments");
if (!boolean.is(anim.isSubframeEnabled))
props.push("isSubframeEnabled");
if (!boolean.is(anim.isLoaded))
props.push("isLoaded");
if (!boolean.is(anim.isPaused))
props.push("isPaused");
if (!boolean.is(anim.autoplay))
props.push("autoplay");
if (!boolean.is(anim.loop))
props.push("loop");
if (!array.isPopulated(anim.segments))
props.push("segments");
if (!array.isPopulated(anim.assets))
props.push("assets");
var updates = removeProps.apply(void 0, __spreadArray([anim], props, false));
updates.isStopped = state.isStopped;
return updates;
}, [state.isStopped]);
useEffect(function () {
var anim = lottie.loadAnimation(animationConfig(lottieRef.current));
registerEvents(anim, eventListeners);
var updates = filterLottieState(anim);
update(updates);
setAnimation(anim);
return function () {
if (hasOwnProperty(animation, "destroy"))
animation === null || animation === void 0 ? void 0 : animation.destroy();
deRegisterEvents(animation, eventListeners);
controls.destroy();
setAnimation(undefined);
update({ animationData: {} });
};
}, []);
useEffect(function () {
if (internalAnimationData !== state.animationData) {
deRegisterEvents(animation, eventListeners);
controls.destroy();
var newOptions = __assign(__assign({}, animationConfig(lottieRef.current)), { renderer: renderer, loop: loop, autoplay: autoplay, rendererSettings: rendererSettings, animationData: state.animationData });
var anim = lottie.loadAnimation(newOptions);
registerEvents(anim, eventListeners);
setInternalAnimationData(state.animationData);
setAnimation(anim);
var updates = filterLottieState(anim);
update(updates);
}
}, [
animation,
internalAnimationData,
state.animationData,
eventListeners,
rendererSettings,
deRegisterEvents,
animationConfig,
registerEvents,
filterLottieState,
autoplay,
renderer,
controls,
state,
loop,
]);
var lottieState = filterLottieState(state);
return [lottieRef, lottieState, controls];
};
function ownerDocument(node) {
return (node && node.ownerDocument) || document;
}
var setRef = function (ref, value) {
if (typeof ref === "function") {
ref(value);
}
else if (ref) {
ref.current = value;
}
};
var useForkRef = function (refA, refB) {
return React.useMemo(function () {
if (refA == null && refB == null) {
return null;
}
return function (refValue) {
setRef(refA, refValue);
setRef(refB, refValue);
};
}, [refA, refB]);
};
var useEnhancedEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
function useEventCallback(fn) {
var ref = React.useRef(fn);
useEnhancedEffect(function () {
ref.current = fn;
});
return React.useCallback(function (event) { return (0, ref.current)(event); }, []);
}
function mapEventPropToEvent(eventProp) {
return eventProp.substring(2).toLowerCase();
}
var ClickAway = React.forwardRef(function (props, ref) {
var style = props.style, className = props.className, onClickAway = props.onClickAway, _a = props.mouseEvent, mouseEvent = _a === void 0 ? "onClick" : _a, _b = props.touchEvent, touchEvent = _b === void 0 ? "onTouchEnd" : _b, _c = props.onClickIn, onClickIn = _c === void 0 ? function () { return void 0; } : _c, children = props.children;
var _d = React.useState(false), clicked = _d[0], setClicked = _d[1];
var movedRef = React.useRef(false);
var nodeRef = React.useRef(null);
var mountedRef = React.useRef(false);
React.useEffect(function () {
mountedRef.current = true;
return function () {
mountedRef.current = false;
};
}, []);
var handleNodeRef = useForkRef(nodeRef, ref);
var handleOwnRef = React.useCallback(function (instance) {
setRef(handleNodeRef, ReactDOM.findDOMNode(instance));
}, [handleNodeRef]);
var handleRef = useForkRef(children.ref, handleOwnRef);
var handleClickIn = React.useCallback(function () {
setClicked(function (state) { return !state; });
onClickIn();
}, [onClickIn]);
var handleClickAway = useEventCallback(function (event) {
if (!mountedRef.current) {
return;
}
if (movedRef.current) {
movedRef.current = false;
return;
}
if (!nodeRef.current)
return;
var doc = ownerDocument(nodeRef.current);
if (doc.documentElement &&
doc.documentElement.contains(event.target) &&
!nodeRef.current.contains(event.target)) {
if (clicked) {
setClicked(false);
onClickAway(event);
}
}
});
var handleTouchMove = React.useCallback(function () {
movedRef.current = true;
}, []);
React.useEffect(function () {
if (touchEvent !== false) {
var mappedTouchEvent_1 = mapEventPropToEvent(touchEvent);
var doc_1 = ownerDocument(nodeRef.current);
doc_1.addEventListener(mappedTouchEvent_1, handleClickAway);
doc_1.addEventListener("touchmove", handleTouchMove);
return function () {
doc_1.removeEventListener(mappedTouchEvent_1, handleClickAway);
doc_1.removeEventListener("touchmove", handleTouchMove);
};
}
return undefined;
}, [handleClickAway, handleTouchMove, touchEvent]);
React.useEffect(function () {
if (mouseEvent !== false) {
var mappedMouseEvent_1 = mapEventPropToEvent(mouseEvent);
var doc_2 = ownerDocument(nodeRef.current);
doc_2.addEventListener(mappedMouseEvent_1, handleClickAway);
return function () {
doc_2.removeEventListener(mappedMouseEvent_1, handleClickAway);
};
}
return undefined;
}, [handleClickAway, mouseEvent]);
var Wrapper = (React.createElement("div", { role: "presentation", onClick: handleClickIn, style: style, className: className }, children));
return React.createElement(React.Fragment, null, React.cloneElement(Wrapper, { ref: handleRef }));
});
export { ClickAway, Lottie, Renderer, useLottie };
//# sourceMappingURL=index.esm.js.map