Created
October 23, 2018 13:53
-
-
Save kmagiera/476a43fdccc69e39863909262a367518 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import { StyleSheet, View } from 'react-native'; | |
import { PanGestureHandler, State } from 'react-native-gesture-handler'; | |
import Animated from 'react-native-reanimated'; | |
const { | |
event, | |
Value, | |
cond, | |
add, | |
multiply, | |
eq, | |
startClock, | |
Clock, | |
set, | |
divide, | |
pow, | |
diff, | |
sub, | |
and, | |
abs, | |
lessThan, | |
greaterThan, | |
stopClock, | |
debug, | |
sqrt, | |
exp, | |
block, | |
} = Animated; | |
function spring(dt, position, velocity, anchor, mass = 1, tension = 300) { | |
const dist = sub(position, anchor); | |
const acc = divide(multiply(-1, tension, dist), mass); | |
return set(velocity, add(velocity, multiply(dt, acc))); | |
} | |
const DEFAULT_GRAVITY_STRENGTH = 400; | |
const DEFAULT_GRAVITY_FALLOF = 40; | |
function gravity( | |
dt, | |
position, | |
velocity, | |
anchor, | |
mass = 1, | |
strength = DEFAULT_GRAVITY_STRENGTH, | |
falloff = DEFAULT_GRAVITY_FALLOF | |
) { | |
const dx = sub(position, anchor); | |
const drsq = multiply(dx, dx); | |
const dr = sqrt(drsq); | |
const a = divide( | |
multiply( | |
-1, | |
strength, | |
dr, | |
exp(divide(multiply(-0.5, drsq), multiply(falloff, falloff))) | |
), | |
mass | |
); | |
const div = cond(dr, divide(a, dr), 0); | |
return set(velocity, add(velocity, multiply(dt, dx, div))); | |
// return set(velocity, [debug('gravity', div), 0]); | |
} | |
function damping(dt, velocity, mass = 1, damping = 12) { | |
const acc = divide(multiply(-1, damping, velocity), mass); | |
return set(velocity, add(velocity, multiply(dt, acc))); | |
} | |
function _interaction(clock, dt, drag, dragState) { | |
const dragging = new Value(0); | |
const start = new Value(0); | |
const x = new Value(0); | |
const vx = new Value(0); | |
const anchor = new Value(0); | |
const step = cond( | |
eq(dragState, State.ACTIVE), | |
[ | |
cond(dragging, 0, [startClock(clock), set(dragging, 1), set(start, x)]), | |
set(anchor, add(start, drag)), | |
[springy(dt, x, vx, anchor), springy(dt, x, vx, 0), friction(dt, vx)], | |
], | |
[set(dragging, 0), springy(dt, x, vx, 0), friction(dt, vx)] | |
); | |
return block([step, set(x, add(x, multiply(vx, dt)))]); | |
} | |
const POSITION_THRESHOLD = 1; | |
const EPS = 1e-3; | |
const VELOCITY = 30; | |
function stopWhenNeeded(dt, position, velocity, clock) { | |
const ds = diff(position); | |
const noMovementFrames = new Value(0); | |
return cond( | |
lessThan(abs(ds), EPS), | |
[ | |
set(noMovementFrames, add(noMovementFrames, 1)), | |
cond(greaterThan(noMovementFrames, 5), stopClock(clock)), | |
], | |
set(noMovementFrames, 0) | |
); | |
} | |
function force(dt, position, velocity) { | |
return set(velocity, cond(lessThan(position, 0), VELOCITY, -VELOCITY)); | |
} | |
function interaction(gestureTranslation, gestureState) { | |
const dragging = new Value(0); | |
const start = new Value(0); | |
const position = new Value(0); | |
const anchor = new Value(0); | |
const velocity = new Value(0); | |
const clock = new Clock(); | |
const dt = divide(diff(clock), 1000); | |
return cond( | |
eq(gestureState, State.ACTIVE), | |
[ | |
cond(dragging, 0, [set(dragging, 1), set(start, position)]), | |
set(anchor, add(start, gestureTranslation)), | |
// spring attached to pan gesture "anchor" | |
spring(dt, position, velocity, anchor, 1, 100), | |
damping(dt, velocity), | |
// spring attached to the center position (0) | |
// spring(dt, position, velocity, 0), | |
// damping(dt, velocity), | |
set(position, add(position, multiply(velocity, dt))), | |
], | |
[ | |
set(dragging, 0), | |
startClock(clock), | |
spring(dt, position, velocity, 0), | |
damping(dt, velocity), | |
set(position, add(position, multiply(velocity, dt))), | |
] | |
); | |
} | |
class Box extends Component { | |
constructor(props) { | |
super(props); | |
// const gesture = { x: new Value(0), y: new Value(0) }; | |
const gestureX = new Value(0); | |
const state = new Value(-1); | |
this._onGestureEvent = event([ | |
{ | |
nativeEvent: { | |
translationX: gestureX, | |
state: state, | |
}, | |
}, | |
]); | |
const clock = new Clock(); | |
const dt = divide(diff(clock), 1000); | |
this._transX = interaction(gestureX, state); | |
// this._transY = interaction(clock, dt, gesture.y, state); | |
} | |
render() { | |
return ( | |
<PanGestureHandler | |
onGestureEvent={this._onGestureEvent} | |
onHandlerStateChange={this._onGestureEvent}> | |
<Animated.View | |
style={[ | |
styles.box, | |
{ | |
transform: [{ translateX: this._transX }], | |
}, | |
]} | |
/> | |
</PanGestureHandler> | |
); | |
} | |
} | |
export default class Example extends Component { | |
render() { | |
return ( | |
<View style={styles.container}> | |
<Box style={styles.box} /> | |
</View> | |
); | |
} | |
} | |
const BOX_SIZE = 100; | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
justifyContent: 'center', | |
alignItems: 'center', | |
backgroundColor: '#F5FCFF', | |
}, | |
box: { | |
width: BOX_SIZE, | |
height: BOX_SIZE, | |
alignSelf: 'center', | |
backgroundColor: 'teal', | |
margin: BOX_SIZE / 2, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment