@animatereactnative/marquee
Version:
A React Native Marquee component
168 lines (166 loc) • 6.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Marquee = void 0;
var React = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeGestureHandler = require("react-native-gesture-handler");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
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; }
const AnimatedChild = ({
index,
children,
anim,
textMeasurement,
spacing,
direction
}) => {
const stylez = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
if (direction === 'vertical') {
return {
position: 'absolute',
top: (index - 1) * (textMeasurement.value.height + spacing),
transform: [{
translateY: -(anim.value % (textMeasurement.value.height + spacing))
}]
};
}
return {
position: 'absolute',
left: (index - 1) * (textMeasurement.value.width + spacing),
transform: [{
translateX: -(anim.value % (textMeasurement.value.width + spacing))
}]
};
}, [index, spacing, textMeasurement]);
return /*#__PURE__*/React.createElement(_reactNativeReanimated.default.View, {
style: stylez
}, children);
};
/**
* Used to animate the given children in a horizontal manner.
*/
const Marquee = exports.Marquee = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(({
speed = 1,
children,
spacing = 0,
style,
reverse,
frameRate,
direction = 'horizontal',
position,
withGesture = true
}, ref) => {
const parentMeasurement = (0, _reactNativeReanimated.useSharedValue)({
width: 0,
height: 0,
x: 0,
y: 0
});
const textMeasurement = (0, _reactNativeReanimated.useSharedValue)({
width: 0,
height: 0,
x: 0,
y: 0
});
const [cloneTimes, setCloneTimes] = React.useState(0);
const anim = (0, _reactNativeReanimated.useSharedValue)(0);
const frameRateMs = frameRate ? 1000 / frameRate : null;
const frameCallback = (0, _reactNativeReanimated.useFrameCallback)(frameInfo => {
if (frameInfo.timeSincePreviousFrame === null) return;
const frameDelta = frameRateMs ? frameInfo.timeSincePreviousFrame / frameRateMs : 1;
if (reverse) {
anim.value -= speed * frameDelta;
} else {
anim.value += speed * frameDelta;
}
}, true);
(0, _reactNativeReanimated.useDerivedValue)(() => {
if (position) {
position.value = anim.value;
}
});
(0, _reactNativeReanimated.useAnimatedReaction)(() => {
if (textMeasurement.value.width === 0 || parentMeasurement.value.width === 0 || textMeasurement.value.height === 0 || parentMeasurement.value.height === 0) {
return 0;
}
return Math.round(direction === 'horizontal' ? parentMeasurement.value.width / textMeasurement.value.width : parentMeasurement.value.height / textMeasurement.value.height) + 1;
}, v => {
if (v === 0) {
return;
}
// This is going to cover the case when the text/element size
// is greater than the actual parent size
// Double this to cover the entire screen twice, in this way we can
// reset the position of the first element when its going to move out
// of the screen without any noticible glitch
(0, _reactNativeReanimated.runOnJS)(setCloneTimes)(v + 2);
}, [direction]);
// Pan Gestures
function start() {
frameCallback.setActive(true);
}
function stop() {
frameCallback.setActive(false);
}
React.useImperativeHandle(ref, () => ({
start,
stop,
isActive: frameCallback.isActive
}));
const pan = _reactNativeGestureHandler.Gesture.Pan().enabled(withGesture).onBegin(() => {
(0, _reactNativeReanimated.runOnJS)(stop)();
}).onChange(e => {
anim.value += -(direction === 'horizontal' ? e.changeX : e.changeY);
}).onFinalize(e => {
anim.value = (0, _reactNativeReanimated.withDecay)({
velocity: -(direction === 'horizontal' ? e.velocityX : e.velocityY)
}, finished => {
if (finished) {
(0, _reactNativeReanimated.runOnJS)(start)();
}
});
});
return /*#__PURE__*/React.createElement(_reactNativeReanimated.default.View, {
key: direction,
style: style,
onLayout: ev => {
parentMeasurement.value = ev.nativeEvent.layout;
},
pointerEvents: "box-none"
}, /*#__PURE__*/React.createElement(_reactNativeGestureHandler.GestureDetector, {
gesture: pan
}, /*#__PURE__*/React.createElement(_reactNativeReanimated.default.View, {
style: styles.row,
pointerEvents: "box-none"
}, /*#__PURE__*/React.createElement(_reactNativeReanimated.default.ScrollView, {
horizontal: direction === 'horizontal',
style: styles.hidden,
pointerEvents: "box-none"
}, /*#__PURE__*/React.createElement(_reactNative.View, {
onLayout: ev => {
textMeasurement.value = ev.nativeEvent.layout;
}
}, children)), cloneTimes > 0 && [...Array(cloneTimes).keys()].map(index => {
return /*#__PURE__*/React.createElement(AnimatedChild, {
key: `clone-${index}`,
index: index,
anim: anim,
textMeasurement: textMeasurement,
spacing: spacing,
direction: direction
}, children);
}))));
}));
const styles = _reactNative.StyleSheet.create({
hidden: {
opacity: 0,
zIndex: -9999
},
row: {
flexDirection: 'row'
}
});
//# sourceMappingURL=index.js.map