Open chenglou opened 9 years ago
I have an idea around this.
a gesture recognizer is a reducer on some state over time, + change events. just like a flux store! for example, a swipe recognizer requires access to some accumulated time, and the movements made by the finger over that period of time. we can represent this like a flux store
let position = (state, event) => (/* accumulated velocity > threshold */) ?
{ position: state.position + 1, events: [event, ...state.events] } /* or -1 if swiped left */ :
{...state, events: [event, ...state.events]}
}
You can then make a 'tracker' that accepts inputs (much like a flux dispatcher)
<Tracker stores={{position}}>{
(addMovement, state) => <div onTouchMove={e => addMovement(e)}>
<Slideshow position={state.position}>
<Slide/>
<Slide/>
<Slide/>
</Slideshow>
</div>
}
</Tracker>
then react-motion kicks in and actually animates the slides (with the fun unmounting remounting stuff etc)
(For velocity measuremements, you could assume a js version of https://developer.android.com/reference/android/view/VelocityTracker.html in the background)
We can take it up a notch. Return an array of velocities, one for each finger. Order it by time pressed, and pass an accumulated average. We can have generic helpers that takes the list of presses and return something relevant, e.g. zoomFactor
, which also returns a velocity, etc.
Up a notch's up a notch: I'm thinking whether we can encode react's responder system in this paradigm. I'll leave that for the future.
Also, somewhat related: http://minuum.com/taps-and-swipes/#2
I'd suggest that this sounds like something to be kept as a separate module. It should still compose well, e.g.:
const horizontalFlick = flickRecognizer({
dx: 40,
velocity: ...,
});
<Gesture recognizers={horizontalFlick} handle={this.setVelocityToState}>
<Spring
currentVelocity={this.state.currentVelocity}
endValue={...}>
{currentValues => <div />}
</Spring>
</Gesture>
Or potentially using the “function as child”-pattern used in react-motion
instead of polluting the component's state.
Fundamentally, using onTouchMove
at the component level is problematic because gestures will cancel once the user moves outside of the div
. Fingers can also be added and removed at any point so it's important that we keep track of which pointers we're interested in. There also needs to be some way to normalize mouse and touch events or optionally specify which of them we're interested in.
I certainly won't mind if someone solves all these problems within react-motion
and I'm more than willing to help. But it seems like an entirely separate issue from the one it's trying to solve right now.
(This is based on very rough ideas I've jotted down over the weekend for a gesture recognizer library we need internally. Slightly more at https://github.com/HMILogic/react-gesture-recognizers but it's just a public notepad at this point. Of course I'm still open for input, if someone is interested.)
It seems the PanResponder would be something quite interesting for building that on top of.
It does the math (compensated distance, velocity, ...) and is built around the EventResponderPlugin which seems like a very nice abstraction for event handling.
It is very simple to port for web touch events. It requires a bit of thinking to get it working with mouse events, though maybe that's not something that's wanted?
woah :D any chance the PanResponder can be ported to just an imperative api/object? We can then wrap it with a component and do the same as above.
@sphire yeah check my initial snippet. It's children functions. No dirty state injection (yet) please. And this'll be a separate repo once we finish iterating on it, don't worry. We're not trying to build a monolithic animation framework here.
Regarding PanResponder: I'll leave that to you folks to experiment. I'm afraid of diving into the responder system because it'll constrain my way of thinking about these things =)
Initial work on velocity tracking - https://github.com/threepointone/disto/blob/redux/examples/velocity/app.js
The internals are a hack; I'm calculating delta/period, and using a Spring to smooth over it(!). You could imagine using a velocity tracker object or whatnot.
next up - flings.
Some crazy idea:
(Totally unrelated food for thought: what does declarative multitouch gestures look like in idiomatic React?)