@antonkolesnik/counter
Version:
React number increase/decrease animation
201 lines (195 loc) • 8.02 kB
JavaScript
;
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = Counter;
var _react = _interopRequireWildcard(require("react"));
var _utils = require("@antonkolesnik/utils");
var _utils2 = require("./utils");
var _constants = require("./constants");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
// TODO: Add isLoop
/* eslint-disable no-param-reassign */
function Counter(_ref) {
var duration = _ref.duration,
_ref$start = _ref.start,
start = _ref$start === void 0 ? '0' : _ref$start,
_ref$end = _ref.end,
end = _ref$end === void 0 ? '0' : _ref$end,
className = _ref.className,
withAnimation = _ref.withAnimation,
_ref$fontSize = _ref.fontSize,
fontSize = _ref$fontSize === void 0 ? _constants.DEFAULT_VALUES.fontSize : _ref$fontSize,
decimalsProp = _ref.decimals,
onUpdate = _ref.onUpdate,
_ref$isStart = _ref.isStart,
isStart = _ref$isStart === void 0 ? null : _ref$isStart;
var inputRef = (0, _react.useRef)(null);
var currentInputValueRef = (0, _react.useRef)('');
var intervalRef = (0, _react.useRef)(null);
var timeoutRef = (0, _react.useRef)(null);
var decimals = decimalsProp || (0, _utils2.getDecimals)({
start: start,
end: end
});
var isDecrease = Number(start) > Number(end);
var intervalStepTime = (0, _utils2.getIntervalStepTime)({
startNumber: start,
endNumber: end,
duration: duration,
isDecrease: isDecrease,
decimals: decimals
});
var isFloatRange = (0, _utils2.isFloat)(end) || (0, _utils2.isFloat)(start);
var operation = isDecrease ? -1 : 1;
var fontHeight = fontSize * 1.15;
// We can't make it slowly than intervalStepTime / 2,
// because we will not have time to see the animation
var ANIMATION_SPEED = intervalStepTime / 2;
function setInputPosition(_ref2) {
var isInitial = _ref2.isInitial,
currentEl = _ref2.currentEl,
nextEl = _ref2.nextEl;
// Show the translate changes slowly
// intervalStepTime / 2 - Show it at the same time as intervalStepTime, but smoother
var transition = isInitial ? 'transform 0s' : "transform ".concat(ANIMATION_SPEED, "ms");
currentEl.style.transition = transition;
nextEl.style.transition = transition;
var currentElTranslate = isInitial
// Go back and wait for the next transition
? 'translate(0, 0)'
// The current element goes down/up depends on isDecrease
: "translate(0, ".concat(isDecrease ? fontHeight : -fontHeight, "px)");
var nextElTranslate = isInitial
/*
isDecrease: move nextEl above currentEl:
* 2 - position of currentEl + one more
!isDecrease: do nothing
isInitial: go back and wait for the next transition
*/ ? "translate(0, ".concat(isDecrease ? -fontHeight * 2 : 0, "px)")
/*
Move nextEl to the currentEl position
0 - current nextEl position, we don't see it
0 - fontHeight = goes up
fontHeight*2 - fontHeight = goes down
*/
: "translate(0, ".concat(-fontHeight, "px)");
currentEl.style.transform = currentElTranslate;
nextEl.style.transform = nextElTranslate;
}
function setInitialValue(value, currentEl) {
currentInputValueRef.current = value;
currentEl.innerText = value;
}
function changeInputValue(nextInputValue, currentEl, nextEl) {
if (withAnimation) {
// Set nextInputValue from the previous call
// or currentValue from setInitialValue
currentEl.innerText = currentInputValueRef.current;
nextEl.innerText = nextInputValue;
// This is for currentEl.innerText
// when changeInputValue will be called again
currentInputValueRef.current = nextInputValue;
// Go back to inital translate values
setInputPosition({
isInitial: true,
currentEl: currentEl,
nextEl: nextEl
});
// Start animation here
var timeout = setTimeout(function () {
setInputPosition({
isInitial: false,
currentEl: currentEl,
nextEl: nextEl
});
}, ANIMATION_SPEED);
// To clear when unmount
timeoutRef.current = timeout;
return;
}
currentEl.innerText = nextInputValue;
}
function play(_ref3) {
var counterEl = _ref3.counterEl;
var currentEl = counterEl.children[0];
var nextEl = counterEl.children[1];
// To see the first start value before changing
setInitialValue(start, currentEl);
var currentIntervalValue = start;
var timer = setInterval(function () {
var nextIntervalValue = (0, _utils2.getNextIntervalValue)({
isFloatRange: isFloatRange,
currentValue: Number(currentIntervalValue),
decimals: decimals,
operation: operation
});
changeInputValue(nextIntervalValue, currentEl, nextEl);
if (onUpdate) {
onUpdate(nextIntervalValue);
}
if ((0, _utils2.isIntervalEnd)({
nextIntervalValue: nextIntervalValue,
end: end,
decimals: decimals
})) {
clearInterval(timer);
}
currentIntervalValue = nextIntervalValue;
}, intervalStepTime);
intervalRef.current = timer;
}
(0, _react.useEffect)(function () {
if (inputRef.current) {
play({
counterEl: inputRef.current
});
}
return function () {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
if (timeoutRef.current) {
clearInterval(timeoutRef.current);
}
};
}, [isStart]);
if (isStart !== null && !isStart) {
return null;
}
if (!start && !end) {
// eslint-disable-next-line no-console
console.error(_constants.ERROR_MESSAGES.emptyStartEndValue);
return /*#__PURE__*/_react["default"].createElement("span", null, _constants.ERROR_MESSAGES.emptyStartEndValue);
}
if (!intervalStepTime) {
// eslint-disable-next-line no-console
console.error(_constants.ERROR_MESSAGES.incorrectProps);
return /*#__PURE__*/_react["default"].createElement("span", null, _constants.ERROR_MESSAGES.incorrectProps);
}
if (intervalStepTime < _constants.MINIMAL_INTERVAL_TIME || withAnimation && intervalStepTime < _constants.MINIMAL_ANIMATION_INTERVAL_TIME) {
// eslint-disable-next-line no-console
console.error(_constants.ERROR_MESSAGES.smallDurationValue);
return /*#__PURE__*/_react["default"].createElement("span", null, _constants.ERROR_MESSAGES.smallDurationValue);
}
return /*#__PURE__*/_react["default"].createElement("div", {
ref: inputRef,
className: (0, _utils.combineStrings)('counter-number', className),
style: {
fontSize: fontSize,
height: fontHeight
}
}, /*#__PURE__*/_react["default"].createElement("div", {
style: {
height: fontHeight
},
className: "current"
}), /*#__PURE__*/_react["default"].createElement("div", {
style: {
height: fontHeight
},
className: "next"
}));
}