react-native-interactable-with-android-link
Version:
Upgrade for react-native-interactable
487 lines (408 loc) • 16.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNativeReanimated = _interopRequireDefault(require("react-native-reanimated"));
var _reactNativeGestureHandler = require("react-native-gesture-handler");
var _lodash = _interopRequireDefault(require("lodash"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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 _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const {
add,
cond,
diff,
divide,
eq,
event,
exp,
lessThan,
and,
call,
block,
multiply,
pow,
set,
abs,
clockRunning,
greaterOrEq,
lessOrEq,
sqrt,
startClock,
stopClock,
sub,
Clock,
Value,
onChange
} = _reactNativeReanimated.default;
const ANIMATOR_PAUSE_CONSECUTIVE_FRAMES = 10;
const ANIMATOR_PAUSE_ZERO_VELOCITY = 1;
const DEFAULT_SNAP_TENSION = 300;
const DEFAULT_SNAP_DAMPING = 0.7;
const DEFAULT_GRAVITY_STRENGTH = 400;
const DEFAULT_GRAVITY_FALLOF = 40;
function sq(x) {
return multiply(x, x);
}
function influenceAreaWithRadius(radius, anchor) {
return {
left: (anchor.x || 0) - radius,
right: (anchor.x || 0) + radius,
top: (anchor.y || 0) - radius,
bottom: (anchor.y || 0) + radius
};
}
function snapTo(target, snapPoints, best, clb, dragClb) {
const dist = new Value(0);
const snap = pt => [set(best.tension, pt.tension || DEFAULT_SNAP_TENSION), set(best.damping, pt.damping || DEFAULT_SNAP_DAMPING), set(best.x, pt.x || 0), set(best.y, pt.y || 0)];
const snapDist = pt => add(sq(sub(target.x, pt.x || 0)), sq(sub(target.y, pt.y || 0)));
return [set(dist, snapDist(snapPoints[0])), ...snap(snapPoints[0]), ...snapPoints.map(pt => {
const newDist = snapDist(pt);
return cond(lessThan(newDist, dist), [set(dist, newDist), ...snap(pt)]);
}), (clb || dragClb) && call([best.x, best.y, target.x, target.y], ([bx, by, x, y]) => {
snapPoints.forEach((pt, index) => {
if ((pt.x === undefined || pt.x === bx) && (pt.y === undefined || pt.y === by)) {
clb && clb({
nativeEvent: { ...pt,
index
}
});
dragClb && dragClb({
nativeEvent: {
x,
y,
targetSnapPointId: pt.id,
state: 'end'
}
});
}
});
})];
}
function springBehavior(dt, target, obj, anchor, tension = 300) {
const dx = sub(target.x, anchor.x);
const ax = divide(multiply(-1, tension, dx), obj.mass);
const dy = sub(target.y, anchor.y);
const ay = divide(multiply(-1, tension, dy), obj.mass);
return {
x: set(obj.vx, add(obj.vx, multiply(dt, ax))),
y: set(obj.vy, add(obj.vy, multiply(dt, ay)))
};
}
function frictionBehavior(dt, target, obj, damping = 0.7) {
const friction = pow(damping, multiply(60, dt));
return {
x: set(obj.vx, multiply(obj.vx, friction)),
y: set(obj.vy, multiply(obj.vy, friction))
};
}
function anchorBehavior(dt, target, obj, anchor) {
const dx = sub(anchor.x, target.x);
const dy = sub(anchor.y, target.y);
return {
x: set(obj.vx, divide(dx, dt)),
y: set(obj.vy, divide(dy, dt))
};
}
function gravityBehavior(dt, target, obj, anchor, strength = DEFAULT_GRAVITY_STRENGTH, falloff = DEFAULT_GRAVITY_FALLOF) {
const dx = sub(target.x, anchor.x);
const dy = sub(target.y, anchor.y);
const drsq = add(sq(dx), sq(dy));
const dr = sqrt(drsq);
const a = divide(multiply(-1, strength, dr, exp(divide(multiply(-0.5, drsq), sq(falloff)))), obj.mass);
const div = divide(a, dr);
return {
x: cond(dr, set(obj.vx, add(obj.vx, multiply(dt, dx, div)))),
y: cond(dr, set(obj.vy, add(obj.vy, multiply(dt, dy, div))))
};
}
function bounceBehavior(dt, target, obj, area, bounce = 0) {
const xnodes = [];
const ynodes = [];
const flipx = set(obj.vx, multiply(-1, obj.vx, bounce));
const flipy = set(obj.vy, multiply(-1, obj.vy, bounce));
if (area.left !== undefined) {
xnodes.push(cond(and(eq(target.x, area.left), lessThan(obj.vx, 0)), flipx));
}
if (area.right !== undefined) {
xnodes.push(cond(and(eq(target.x, area.right), lessThan(0, obj.vx)), flipx));
}
if (area.top !== undefined) {
xnodes.push(cond(and(eq(target.y, area.top), lessThan(obj.vy, 0)), flipy));
}
if (area.bottom !== undefined) {
xnodes.push(cond(and(eq(target.y, area.bottom), lessThan(0, obj.vy)), flipy));
}
return {
x: xnodes,
y: ynodes
};
}
function withInfluence(area, target, behavior) {
if (!area) {
return behavior;
}
const testLeft = area.left === undefined || lessOrEq(area.left, target.x);
const testRight = area.right === undefined || lessOrEq(target.x, area.right);
const testTop = area.top === undefined || lessOrEq(area.top, target.y);
const testBottom = area.bottom === undefined || lessOrEq(target.y, area.bottom);
const testNodes = [testLeft, testRight, testTop, testBottom].filter(t => t !== true);
const test = and(...testNodes);
return {
x: cond(test, behavior.x),
y: cond(test, behavior.y)
};
}
function withLimits(value, lowerBound, upperBound) {
let result = value;
if (lowerBound !== undefined) {
result = cond(lessThan(value, lowerBound), lowerBound, result);
}
if (upperBound !== undefined) {
result = cond(lessThan(upperBound, value), upperBound, result);
}
return result;
}
class Interactable extends _react.Component {
constructor(_props) {
super(_props);
_defineProperty(this, "initialize", props => {
const update = {
x: props.animatedValueX,
y: props.animatedValueY
};
const clock = new Clock();
const dt = divide(diff(clock), 1000);
const obj = {
vx: new Value(0),
vy: new Value(0),
mass: 1
};
const tossedTarget = {
x: add(new Value(props.initialPosition.x || 0), multiply(props.dragToss, obj.vx)),
y: add(this.target.y, multiply(props.dragToss, obj.vy))
};
const permBuckets = [[], [], []];
const addSpring = (anchor, tension, influence, buckets = permBuckets) => {
buckets[0].push(withInfluence(influence, this.target, springBehavior(dt, this.target, obj, anchor, tension)));
};
const addFriction = (damping, influence, buckets = permBuckets) => {
buckets[1].push(withInfluence(influence, this.target, frictionBehavior(dt, this.target, obj, damping)));
};
const addGravity = (anchor, strength, falloff, influence, buckets = permBuckets) => {
buckets[0].push(withInfluence(influence, this.target, gravityBehavior(dt, this.target, obj, anchor, strength, falloff)));
};
const dragAnchor = {
x: new Value(0),
y: new Value(0)
};
const dragBuckets = [[], [], []];
if (props.dragWithSpring) {
const {
tension,
damping
} = props.dragWithSpring;
addSpring(dragAnchor, tension, null, dragBuckets);
addFriction(damping, null, dragBuckets);
} else {
dragBuckets[0].push(anchorBehavior(dt, this.target, obj, dragAnchor));
}
const handleStartDrag = props.onDrag && call([this.target.x, this.target.y], ([x, y]) => props.onDrag({
nativeEvent: {
x,
y,
state: 'start'
}
}));
const snapBuckets = [[], [], []];
const updateSnapTo = snapTo(tossedTarget, props.snapPoints, this.snapAnchor, props.onSnap, props.onDrag);
addSpring(this.snapAnchor, this.snapAnchor.tension, null, snapBuckets);
addFriction(this.snapAnchor.damping, null, snapBuckets);
if (props.springPoints) {
props.springPoints.forEach(pt => {
addSpring(pt, pt.tension, pt.influenceArea);
if (pt.damping) {
addFriction(pt.damping, pt.influenceArea);
}
});
}
if (props.gravityPoints) {
props.gravityPoints.forEach(pt => {
const falloff = pt.falloff || DEFAULT_GRAVITY_FALLOF;
addGravity(pt, pt.strength, falloff, pt.influenceArea);
if (pt.damping) {
const influenceArea = pt.influenceArea || influenceAreaWithRadius(1.4 * falloff, pt);
addFriction(pt.damping, influenceArea);
}
});
}
if (props.frictionAreas) {
props.frictionAreas.forEach(pt => {
addFriction(pt.damping, pt.influenceArea);
});
}
if (props.boundaries) {
snapBuckets[0].push(bounceBehavior(dt, this.target, obj, props.boundaries, props.boundaries.bounce));
} // behaviors can go under one of three buckets depending on their priority
// we append to each bucket but in Interactable behaviors get added to the
// front, so we join in reverse order and then reverse the array.
const sortBuckets = specialBuckets => ({
x: specialBuckets.map((b, idx) => [...permBuckets[idx], ...b].reverse().map(b => b.x)).reduce((acc, b) => acc.concat(b), []),
y: specialBuckets.map((b, idx) => [...permBuckets[idx], ...b].reverse().map(b => b.y)).reduce((acc, b) => acc.concat(b), [])
});
const dragBehaviors = sortBuckets(dragBuckets);
const snapBehaviors = sortBuckets(snapBuckets);
const noMovementFrames = {
x: new Value(props.verticalOnly ? ANIMATOR_PAUSE_CONSECUTIVE_FRAMES + 1 : 0),
y: new Value(props.horizontalOnly ? ANIMATOR_PAUSE_CONSECUTIVE_FRAMES + 1 : 0)
};
const stopWhenNeeded = cond(and(greaterOrEq(noMovementFrames.x, ANIMATOR_PAUSE_CONSECUTIVE_FRAMES), greaterOrEq(noMovementFrames.y, ANIMATOR_PAUSE_CONSECUTIVE_FRAMES)), [props.onStop ? cond(clockRunning(clock), call([this.target.x, this.target.y], ([x, y]) => props.onStop({
nativeEvent: {
x,
y
}
}))) : undefined, stopClock(clock)], startClock(clock));
const trans = (axis, vaxis, lowerBound, upperBound) => {
const dragging = new Value(0);
const start = new Value(0);
const x = this.target[axis];
const vx = obj[vaxis];
const anchor = dragAnchor[axis];
const drag = this.gesture[axis];
let advance = cond(lessThan(abs(vx), ANIMATOR_PAUSE_ZERO_VELOCITY), x, add(x, multiply(vx, dt)));
if (props.boundaries) {
advance = withLimits(advance, props.boundaries[lowerBound], props.boundaries[upperBound]);
}
const last = new Value(Number.MAX_SAFE_INTEGER);
const noMoveFrameCount = noMovementFrames[axis];
const testMovementFrames = block([onChange(this.snapAnchor.x, set(last, Number.MAX_SAFE_INTEGER)), onChange(this.snapAnchor.y, set(last, Number.MAX_SAFE_INTEGER)), cond(eq(advance, last), set(noMoveFrameCount, add(noMoveFrameCount, 1)), [set(last, advance), set(noMoveFrameCount, 0)])]);
const step = cond(eq(this.state, _reactNativeGestureHandler.State.ACTIVE), [cond(dragging, 0, [handleStartDrag, startClock(clock), set(dragging, 1), set(start, x)]), set(anchor, add(start, drag)), cond(dt, dragBehaviors[axis])], [cond(dragging, [updateSnapTo, set(dragging, 0)]), cond(dt, snapBehaviors[axis]), testMovementFrames, stopWhenNeeded]);
const wrapStep = props.dragEnabled ? cond(props.dragEnabled, step, [set(dragging, 1), stopClock(clock)]) : step; // export some values to be available for imperative commands
this._dragging[axis] = dragging;
this._velocity[axis] = vx; // update animatedValueX/animatedValueY
const doUpdateAnReturn = update[axis] ? set(update[axis], x) : x;
return block([wrapStep, set(x, advance), doUpdateAnReturn]);
}; // variables to be used to access reanimated values from imperative commands
this._dragging = {};
this._velocity = {};
this._position = this.target;
this._snapAnchor = this.snapAnchor;
this._transX = trans('x', 'vx', 'left', 'right');
this._transY = trans('y', 'vy', 'top', 'bottom');
});
this.gesture = {
x: new Value(0),
y: new Value(0)
};
this.state = new Value(-1);
this._onGestureEvent = event([{
nativeEvent: {
translationX: this.gesture.x,
translationY: this.gesture.y,
state: this.state
}
}]);
this.target = {
x: new Value(_props.initialPosition.x || 0),
y: new Value(_props.initialPosition.y || 0)
};
this.snapAnchor = {
x: new Value(_props.initialPosition.x || 0),
y: new Value(_props.initialPosition.y || 0),
tension: new Value(DEFAULT_SNAP_TENSION),
damping: new Value(DEFAULT_SNAP_DAMPING)
};
this.initialize(_props);
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (!_lodash.default.isEqual(nextProps.snapPoints, this.props.snapPoints)) {
this.initialize(nextProps);
}
}
render() {
const {
children,
style,
horizontalOnly,
verticalOnly
} = this.props;
return /*#__PURE__*/_react.default.createElement(_reactNativeGestureHandler.PanGestureHandler, {
maxPointers: 1,
minDist: 10,
enabled: this.props.dragEnabled,
onGestureEvent: this._onGestureEvent,
onHandlerStateChange: this._onGestureEvent
}, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
style: [style, {
transform: [{
translateX: verticalOnly ? 0 : this._transX
}, {
translateY: horizontalOnly ? 0 : this._transY
}]
}]
}, children));
} // imperative commands
setVelocity({
x,
y
}) {
if (x !== undefined) {
this._dragging.x.setValue(1);
this._velocity.x.setValue(x);
}
if (y !== undefined) {
this._dragging.y.setValue(1);
this._velocity.y.setValue(y);
}
}
snapTo({
index
}) {
const snapPoint = this.props.snapPoints[index];
this._snapAnchor.tension.setValue(snapPoint.tension || DEFAULT_SNAP_TENSION);
this._snapAnchor.damping.setValue(snapPoint.damping || DEFAULT_SNAP_DAMPING);
this._snapAnchor.x.setValue(snapPoint.x || 0);
this._snapAnchor.y.setValue(snapPoint.y || 0);
this.props.onSnap && this.props.onSnap({
nativeEvent: { ...snapPoint,
index
}
});
}
changePosition({
x,
y
}) {
if (x !== undefined) {
this._dragging.x.setValue(1);
this._position.x.setValue(x);
}
if (y !== undefined) {
this._dragging.x.setValue(1);
this._position.y.setValue(y);
}
}
snapToPosition({
x,
y
}) {
this._snapAnchor.x.setValue(x || 0);
this._snapAnchor.y.setValue(y || 0);
}
}
_defineProperty(Interactable, "defaultProps", {
dragToss: 0.1,
dragEnabled: true,
initialPosition: {
x: 0,
y: 0
}
});
var _default = {
View: Interactable
};
exports.default = _default;
//# sourceMappingURL=index.js.map