@baby-journey/rn-segmented-progress-bar
Version:
Animated circular progress bar with segments
212 lines (207 loc) • 10.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
var _helpers = require("./helpers");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const IndicatorCircle = _reactNative.Animated.createAnimatedComponent(_reactNativeSvg.Circle);
const ProgressCircle = _reactNative.Animated.createAnimatedComponent(_reactNativeSvg.Circle);
const max = 100;
const duration = 1200;
const RNSegmentedProgressBar = (props, ref) => {
const {
radius,
strokeWidth = 10,
baseColor = '#ffede1',
progressColor = '#F39E93',
segments = 3,
segmentsGap = 0,
indicator,
centerComponent
} = props;
const circleRef = (0, _react.useRef)([]);
const animatedValue = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const progressAnimatedValues = (0, _react.useRef)([...Array(segments)].map(() => new _reactNative.Animated.Value(0))).current;
const indicatorCircleRef = (0, _react.useRef)(null);
const tSpanRef = (0, _react.useRef)(null);
const indicatorSegmentsGap = (indicator === null || indicator === void 0 ? void 0 : indicator.radius) ?? 0;
const halfCircle = radius + strokeWidth + indicatorSegmentsGap;
const circleCircumference = 2 * Math.PI * radius;
const rotation = -90 + 180 * (segmentsGap / 2 / radius) / Math.PI;
const getProgressValues = (0, _react.useCallback)(progress => (0, _helpers.getPathValues)(progress, max, segments), [segments]);
const progressDelay = 10;
const animation = (0, _react.useCallback)((animatedVal, toValue, delay, durationValue) => {
return _reactNative.Animated.timing(animatedVal, {
toValue,
duration: durationValue,
delay,
useNativeDriver: true,
easing: _reactNative.Easing.linear
});
}, []);
(0, _react.useEffect)(() => {
() => {
animatedValue.removeAllListeners();
progressAnimatedValues.forEach(progressAnimatedValue => progressAnimatedValue.removeAllListeners());
};
}, [animatedValue, progressAnimatedValues]);
const getMeanSegmentsGap = (0, _react.useCallback)(progress => {
const pathValues = getProgressValues(progress);
return (progress / pathValues.filter(val => val > 0).length || 1) * segments * segmentsGap / 100;
}, [getProgressValues, segments, segmentsGap]);
const runIndicator = (0, _react.useCallback)((calculatedStrokeDashoffset, val) => {
const {
x: cx,
y: cy
} = (0, _helpers.getArcEndCoordinates)(radius, calculatedStrokeDashoffset, halfCircle, halfCircle, rotation);
if (!calculatedStrokeDashoffset) {
return;
}
const calculatedProgress = `${Math.round(val)}%`;
if (indicatorCircleRef !== null && indicatorCircleRef !== void 0 && indicatorCircleRef.current && tSpanRef !== null && tSpanRef !== void 0 && tSpanRef.current) {
//@ts-ignore
indicatorCircleRef.current.setNativeProps({
r: (indicator === null || indicator === void 0 ? void 0 : indicator.radius) || 0,
strokeWidth: (indicator === null || indicator === void 0 ? void 0 : indicator.strokeWidth) || 0,
cx,
cy
});
//@ts-ignore
tSpanRef === null || tSpanRef === void 0 ? void 0 : tSpanRef.current.setNativeProps({
children: calculatedProgress,
dx: cx,
dy: cy + 5,
font: {
textAnchor: 'middle',
fontSize: 18
}
});
}
}, [radius, halfCircle, rotation, indicator === null || indicator === void 0 ? void 0 : indicator.radius, indicator === null || indicator === void 0 ? void 0 : indicator.strokeWidth]);
const run = (0, _react.useCallback)(_ref => {
let {
progress
} = _ref;
const circleProgressValues = getProgressValues(progress);
progressAnimatedValues.forEach((progressAnimated, index) => {
progressAnimated.addListener(v => {
if (circleRef !== null && circleRef !== void 0 && circleRef.current[index]) {
var _circleRef$current$in;
var strokeDashoffset = circleCircumference;
var val = v.value <= (circleProgressValues[index] ?? 0) ? v.value : circleProgressValues[index] ?? 0;
strokeDashoffset = circleProgressValues[index] ? circleCircumference - circleCircumference * val / 100 : circleCircumference;
const paintedLength = circleCircumference - strokeDashoffset - segments * (circleProgressValues[index] ?? 0) * segmentsGap / 100;
//@ts-ignore
circleRef === null || circleRef === void 0 ? void 0 : (_circleRef$current$in = circleRef.current[index]) === null || _circleRef$current$in === void 0 ? void 0 : _circleRef$current$in.setNativeProps({
strokeDashoffset: circleCircumference - paintedLength > circleCircumference ? circleCircumference : circleCircumference - paintedLength
});
}
});
});
if (indicator !== null && indicator !== void 0 && indicator.show) {
animatedValue.addListener(v => {
var strokeDashoffset = circleCircumference;
var val = v.value <= progress ? v.value : progress;
strokeDashoffset = progress ? circleCircumference - circleCircumference * val / 100 : circleCircumference;
const paintedLength = circleCircumference - strokeDashoffset;
const meanSegmentsGap = getMeanSegmentsGap(progress);
const calculatedStrokeDashoffset = paintedLength - meanSegmentsGap;
runIndicator(calculatedStrokeDashoffset, progress);
});
}
// Animate circles sequentially
const progressAnimations = _reactNative.Animated.sequence(progressAnimatedValues.map((tav, index) => animation(tav,
// Animated value
circleProgressValues[index] ?? 0,
// To value
index === 0 ? progressDelay : 0,
// Delay
duration * (circleProgressValues[index] ?? 0) / max // Duration
)));
if (indicator !== null && indicator !== void 0 && indicator.show) {
// Animate percentage circle
const percentageAnim = animation(animatedValue,
// Animated value
progress,
// To value
progressDelay,
// Delay
duration * progress / max // Duration
);
// Progress Animations run parallelly with percentage circle
_reactNative.Animated.parallel([progressAnimations, percentageAnim]).start();
} else {
progressAnimations.start();
}
}, [animatedValue, animation, segments, circleCircumference, segmentsGap, getMeanSegmentsGap, indicator === null || indicator === void 0 ? void 0 : indicator.show, getProgressValues, runIndicator, progressAnimatedValues]);
const getProgress = (0, _react.useMemo)(() => {
const progressConfig = {
stroke: progressColor,
cx: halfCircle,
cy: halfCircle,
r: radius,
origin: `${halfCircle}, ${halfCircle}`,
strokeWidth: strokeWidth,
strokeDasharray: circleCircumference,
strokeDashoffset: circleCircumference
};
return progressAnimatedValues.map((_, key) => /*#__PURE__*/_react.default.createElement(ProgressCircle, _extends({
key: key
//@ts-ignore
,
ref: el => circleRef.current[key] = el
}, progressConfig, {
rotation: rotation + key * 360 / segments,
strokeLinecap: "round"
})));
}, [segments, circleCircumference, halfCircle, progressColor, radius, rotation, strokeWidth, progressAnimatedValues]);
(0, _react.useImperativeHandle)(ref, () => ({
run
}));
return /*#__PURE__*/_react.default.createElement(_reactNativeSvg.default, {
viewBox: `0 0 ${halfCircle * 2} ${halfCircle * 2}`,
width: '100%',
fill: "none",
height: radius * 2
}, centerComponent && /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.centerComponent
}, centerComponent), /*#__PURE__*/_react.default.createElement(_reactNativeSvg.G, null, [...Array(segments)].map((_, key) => {
return /*#__PURE__*/_react.default.createElement(_reactNativeSvg.Circle, {
key: key,
cx: halfCircle,
cy: halfCircle,
r: radius,
stroke: baseColor,
rotation: rotation + key * 360 / segments,
origin: `${halfCircle}, ${halfCircle}`,
strokeWidth: strokeWidth,
strokeDasharray: circleCircumference,
strokeDashoffset: circleCircumference - circleCircumference / segments + segmentsGap,
strokeLinecap: "round"
});
}), getProgress, (indicator === null || indicator === void 0 ? void 0 : indicator.show) === true && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(IndicatorCircle, {
stroke: progressColor,
ref: indicatorCircleRef,
fill: "white"
}), /*#__PURE__*/_react.default.createElement(_reactNativeSvg.TSpan, {
stroke: progressColor,
fill: progressColor,
ref: tSpanRef
}))));
};
var _default = /*#__PURE__*/(0, _react.memo)( /*#__PURE__*/(0, _react.forwardRef)(RNSegmentedProgressBar));
exports.default = _default;
const styles = _reactNative.StyleSheet.create({
centerComponent: {
height: '100%',
justifyContent: 'center',
alignItems: 'center'
}
});
//# sourceMappingURL=index.js.map