react-native-marquee-text-loop
Version:
A smooth marquee text component for react native.
139 lines (132 loc) • 5.41 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MarqueeText = void 0;
var React = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (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; }
// Clone of the marquee text
const ScrollingTextClone = ({
index,
children,
anim,
textLayout
}) => {
// Animated styles to position the cloned text
const styles = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
return {
position: 'absolute',
left: (index - 1) * (textLayout.value.width + 10),
// Spacing after each clone text
transform: [{
translateX: -(anim.value % (textLayout.value.width + 10) // Scroll the text horizontally
)
}]
};
}, [index, textLayout]);
// Return the cloned text element with animated styles
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: styles,
children: children
});
};
// Marquee text component
const MarqueeText = exports.MarqueeText = /*#__PURE__*/React.memo(({
speed = 1,
text = '',
textStyles = {}
}) => {
// Store measurements of the parent layout and text
const parentLayout = (0, _reactNativeReanimated.useSharedValue)({
width: 0,
height: 0,
x: 0,
y: 0
});
const textLayout = (0, _reactNativeReanimated.useSharedValue)({
width: 0,
height: 0,
x: 0,
y: 0
});
// number clones of the text are needed for scrolling
const [numberOfClones, setNumberOfClones] = React.useState(0);
// animated value for the scrolling animation
const scrollAnimationValue = (0, _reactNativeReanimated.useSharedValue)(0);
const frameRateMs = 30; // frame rate
// update the animation on each animation
(0, _reactNativeReanimated.useFrameCallback)(frameInfo => {
if (frameInfo.timeSincePreviousFrame === null) {
return;
}
const frameDelta = frameInfo.timeSincePreviousFrame / frameRateMs;
scrollAnimationValue.value += speed * frameDelta; // Update the scroll animation
}, true);
// update the number of clones based on text size
(0, _reactNativeReanimated.useAnimatedReaction)(() => {
// If the text or parent container size is zero - zero clones
if (textLayout.value.width === 0 || parentLayout.value.width === 0 || textLayout.value.height === 0 || parentLayout.value.height === 0) {
return 0;
}
// Calculate the number of clones needed based on the parent layout
return Math.round(parentLayout.value.width / textLayout.value.width) + 1 // Adding extra clones to ensure the loop continues
;
}, v => {
if (v === 0) {
return; // No clones if the measurement values are zero
}
(0, _reactNativeReanimated.runOnJS)(setNumberOfClones)(v + 2); // Set the clone count in JS thread (runOnJS avoids Reanimated thread issues)
}, []);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
onLayout: ev => {
parentLayout.value = ev.nativeEvent.layout; // dimensions of the parent container
},
pointerEvents: "box-none",
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
style: styles.row,
pointerEvents: "box-none",
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.ScrollView, {
horizontal: true,
style: styles.scrollContainer,
pointerEvents: "box-none",
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
onLayout: ev => {
textLayout.value = ev.nativeEvent.layout; // width of the text
},
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: textStyles,
children: text
})
})
}), numberOfClones > 0 &&
// Render the clones
[...Array(numberOfClones).keys()].map(index => {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ScrollingTextClone, {
index: index,
anim: scrollAnimationValue,
textLayout: textLayout,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: textStyles,
children: text
})
}, `clone-${index}`);
})]
})
});
});
const styles = _reactNative.StyleSheet.create({
scrollContainer: {
opacity: 0,
zIndex: -9999
},
// invisible scrollView
row: {
flexDirection: 'row',
overflow: 'hidden'
} //cloned texts in a horizontal row
});
//# sourceMappingURL=index.js.map
;