UNPKG

react-sprite-animator

Version:

A react component that loads sprite image and the runs it as an animation

214 lines (176 loc) 7.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSprite = exports.loadImage = void 0; var _react = require("react"); function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var noop = function noop() {}; var loadImage = function loadImage(url) { var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop; var img = new Image(); img.onload = function () { callback(null, img); }; img.onerror = function (err) { callback(err); }; img.src = url; }; exports.loadImage = loadImage; var useSprite = function useSprite(_ref) { var _ref$startFrame = _ref.startFrame, startFrame = _ref$startFrame === void 0 ? 0 : _ref$startFrame, sprite = _ref.sprite, width = _ref.width, height = _ref.height, _ref$direction = _ref.direction, direction = _ref$direction === void 0 ? 'horizontal' : _ref$direction, _ref$onError = _ref.onError, onError = _ref$onError === void 0 ? noop : _ref$onError, _ref$onLoad = _ref.onLoad, onLoad = _ref$onLoad === void 0 ? noop : _ref$onLoad, _ref$onEnd = _ref.onEnd, onEnd = _ref$onEnd === void 0 ? noop : _ref$onEnd, frameCount = _ref.frameCount, _ref$fps = _ref.fps, fps = _ref$fps === void 0 ? 60 : _ref$fps, _ref$shouldAnimate = _ref.shouldAnimate, shouldAnimate = _ref$shouldAnimate === void 0 ? true : _ref$shouldAnimate, stopLastFrame = _ref.stopLastFrame, reset = _ref.reset, _ref$scale = _ref.scale, scale = _ref$scale === void 0 ? 1 : _ref$scale, wrapAfter = _ref.wrapAfter, frame = _ref.frame; var prevTime = (0, _react.useRef)(); var _useState = (0, _react.useState)(startFrame), _useState2 = _slicedToArray(_useState, 2), currentFrame = _useState2[0], setCurrentFrame = _useState2[1]; var _useState3 = (0, _react.useState)(0), _useState4 = _slicedToArray(_useState3, 2), spriteWidth = _useState4[0], setSpriteWidth = _useState4[1]; var _useState5 = (0, _react.useState)(0), _useState6 = _slicedToArray(_useState5, 2), spriteHeight = _useState6[0], setSpriteHeight = _useState6[1]; var _useState7 = (0, _react.useState)(false), _useState8 = _slicedToArray(_useState7, 2), isLoaded = _useState8[0], setIsLoaded = _useState8[1]; var _useState9 = (0, _react.useState)(false), _useState10 = _slicedToArray(_useState9, 2), isLoading = _useState10[0], setIsLoading = _useState10[1]; var _useState11 = (0, _react.useState)(false), _useState12 = _slicedToArray(_useState11, 2), hasErrored = _useState12[0], setHasErrored = _useState12[1]; var _useState13 = (0, _react.useState)(0), _useState14 = _slicedToArray(_useState13, 2), maxFrames = _useState14[0], setMaxFrames = _useState14[1]; var interval = 1000 / fps; var loadSprite = (0, _react.useCallback)(function (url) { var unmounted = false; if (!isLoading && (!isLoaded || !hasErrored)) { setIsLoading(true); loadImage(url, function (err, image) { if (unmounted) { return; } if (err) { onError(err); setHasErrored(true); return; } onLoad(); setIsLoaded(true); setIsLoading(false); setMaxFrames(frameCount || Math.floor(direction === 'horizontal' ? image.width / width : image.height / height)); setSpriteWidth(image.width); setSpriteHeight(image.height); }); } return function () { return unmounted = true; }; }, [sprite, isLoaded, hasErrored]); var animate = (0, _react.useCallback)(function (nextFrame, time) { if (!prevTime.current) { prevTime.current = time; } if (shouldAnimate) { var delta = time - prevTime.current; if (delta < interval) { return requestAnimationFrame(function (time) { return animate(nextFrame, time); }); } prevTime.current = time - delta % interval; setCurrentFrame(nextFrame); } else { prevTime.current = 0; } }, [shouldAnimate]); var getSpritePosition = (0, _react.useCallback)(function () { var frame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var isHorizontal = direction === 'horizontal'; var row, col; if (typeof wrapAfter === 'undefined') { row = isHorizontal ? 0 : frame; col = isHorizontal ? frame : 0; } else { row = isHorizontal ? Math.floor(frame / wrapAfter) : frame % wrapAfter; col = isHorizontal ? frame % wrapAfter : Math.floor(frame / wrapAfter); } var _width = -width * col / scale; var _height = -height * row / scale; return "".concat(_width, "px ").concat(_height, "px"); }, [direction, width, height, wrapAfter, scale]); (0, _react.useEffect)(function () { setIsLoaded(false); setHasErrored(false); return loadSprite(sprite); }, [sprite]); (0, _react.useEffect)(function () { if (shouldAnimate) { var nextFrame = currentFrame + 1 >= maxFrames ? startFrame : currentFrame + 1; if (!shouldAnimate) { return; } if (currentFrame === maxFrames - 1 && stopLastFrame) { return onEnd(); } var id = requestAnimationFrame(function (time) { id = animate(nextFrame, time); }); return function () { cancelAnimationFrame(id); }; } }, [shouldAnimate, maxFrames, currentFrame, startFrame]); (0, _react.useEffect)(function () { setCurrentFrame(startFrame); }, [reset]); (0, _react.useEffect)(function () { if (typeof frame === 'number' && frame !== currentFrame) { setCurrentFrame(frame); } }, [frame]); return { backgroundImage: isLoaded ? "url(".concat(sprite, ")") : null, backgroundPosition: isLoaded ? getSpritePosition(currentFrame) : null, backgroundSize: "".concat(spriteWidth / scale, "px ").concat(spriteHeight / scale, "px"), width: "".concat(width / scale, "px"), height: "".concat(height / scale, "px") }; }; exports.useSprite = useSprite;