chenglou / react-motion

A spring that solves your animation problems.
MIT License
21.68k stars 1.16k forks source link

Implement some declarative gestures + animation demo #36

Open chenglou opened 9 years ago

chenglou commented 9 years ago

Some crazy idea:

<Flick settings={...}>
  {velocityDelta => 
    <Spring 
      currentVelocity={currentVelocity => velocityDelta === 0 ? currentVelocity :
        increase(currentVelocity, velocityDelta)
      } 
      endValue={...}>
      {currentValues => <div />}
    </Spring>
  }
</Flick>

(Totally unrelated food for thought: what does declarative multitouch gestures look like in idiomatic React?)

threepointone commented 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)

chenglou commented 9 years ago

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

sphire commented 9 years ago

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.)

dieppe commented 9 years ago

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?

threepointone commented 9 years ago

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.

chenglou commented 9 years ago

@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 =)

threepointone commented 9 years ago

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.