Giter Site home page Giter Site logo

wix-incubator / react-native-interactable Goto Github PK

View Code? Open in Web Editor NEW
5.2K 5.2K 520.0 2.88 MB

Experimental implementation of high performance interactable views in React Native

License: MIT License

JavaScript 42.59% Java 27.68% Objective-C 27.28% Ruby 1.52% Shell 0.07% Starlark 0.86%

react-native-interactable's Introduction

              

Interactable

react-native-interactable


NPM Version Build Status NPM Downloads

LOOKING FOR A MAINTAINER

We love this project, but currently we don’t have enough time to work on it. So we are looking for a maintainer. If you have enough time and knowledge and want to become one - please let us know ([email protected])


Description


This is an experimental implementation of a declarative API for handling fluid user interactions with views at 60 FPS in React Native. Here are some example use-cases for views that users can interact with:
  • Swipeable card (a la Google Now) springing into place unless swiped away with enough force
  • Drawer snapping between closed and open with buttons appearing gradually as it's being dragged
  • Collapsible header that snaps to a smaller size as the content below is being scrolled
  • Chat heads (a la Facebook Messenger) that can be dragged around but snap to corners of the screen

All of these use-cases have views that continuously interact with the user's gestures. These interactions are normally physical in nature, having properties like springiness, friction, elasticity and damping. In order to feel natural on a touch device they need to run at 60 FPS.

Why is this challenging?

The async nature of the React Native bridge incurs an inherent performance penalty. This traditionally prevents JavaScript code from running at high framerates. One of the most noticeable challenges is animations in JavaScript, which aren't guaranteed to run at 60 FPS.

Modern animation libraries for React Native, like Animated, tackle this challenge with a declarative approach. In order to minimize passes over the bridge, animations are only declared in JavaScript but executed by a native driver on the other side - in 60 FPS.

Fluid user interactions take this a step further than animations. Interactions require UI to continuously react to the user's gestures. This library is designed to support complex physical interactions with ease, using a full-fledged physics engine to drive the interaction on the native side.

Why is it named interactable?

First off, we are aware that interactable isn't a real word. The correct form is interactive but this has connotation that isn't necessarily related to physical interactions. Similar to Animated.View, we wanted to have Interactable.View - meaning a view you can interact with. And hey, Unity did it too.


Installation

Requires RN 0.40 and above.

  • Install the package from npm
npm install react-native-interactable --save
  • Link the native library to your project
react-native link react-native-interactable

Note: instead of linking automatically you can also link manually according to these instructions

node_modules/react-native-interactable/ios/Interactable.xcodeproj

Manually link via Cocoa Pods (iOS)

  • Add the following to your Podfile and run pod update:
pod 'Interactable', :path => '../node_modules/react-native-interactable'

Example



The playground project has few use-cases implemented like: swipeable card, drawer, collapsible header and chat heads under the "Basic examples" section. It's simplistic but easy to learn from.

Under the "Real life example" you'll find more complex demonstrations. They're harder to learn from, but they're cool to watch. More info about the UX inspirations for the demo app.

  • Build and run the example project To see the library in action, clone the repo and run the playground from the root folder:
  npm start
  npm run ios



Note: It's recommended to experience it on a real device and not on a simulator. The simulator has poor performance so the framerate isn't like the real thing.

Usage

The core of this library is the Interactable.View component, used to wrap views you want to interact with:

import Interactable from 'react-native-interactable';

<Interactable.View
  horizontalOnly={true}
  snapPoints={[{x: 0}, {x: -200}]}
  onSnap={this.onDrawerSnap}>

  // the view that you wrap here will now support interactions

</Interactable.View>

Interactable.View Props

Click here for the full reference for all props

  • snapPoints - a list of points the view will snap to after being dragged by the user
snapPoints={[{x: 0}, {x: -200}]}
  • springPoints - connect the view's center to a group of constant springs
springPoints={[{x: 0, tension: 6000, damping: 0.5, influenceArea: {left: 0}}]}
  • gravityPoints - attract/repel the view's center with a group of constant gravity wells
gravityPoints={[{x: 0, y: 0, strength: 8000, falloff: 40, damping: 0.5}]}
  • frictionAreas - add friction to the view's movement with a group of friction regions
frictionAreas={[{damping: 0.5, influenceArea: {top: 0}}]}
  • alertAreas - send alert event when the view's center enters/leaves any region within the group
alertAreas={[{id: 'myArea', influenceArea: {top: 0}}]}
  • horizontalOnly - whether the view should be locked to horizontal movement only
horizontalOnly={true}
  • startOnFront - [ANDROID ONLY] whether the view should call bringToFront
startOnFront
  • verticalOnly - whether the view should be locked to vertical movement only
verticalOnly={true}
  • boundaries - limits to movement relative to the view's center (after initial layout)
boundaries={{left: -100, right: 100, bounce: 0.5}}
  • onSnap - a function called whenever the view finishes snapping to a snapPoints point (after dragging)
onSnap={this.onDrawerSnap}
  • onSnapStart - a function called whenever the view starts snapping to a snapPoints point (after dragging)
onSnapStart={this.onDrawerSnapStart}
  • onStop - a function called whenever the interaction stops (views freeze momentarily)
onStop={this.onStopInteraction}
  • onDrag - a function called whenever the user starts or stops dragging the view
onDrag={this.onDragEvent}
  • onAlert - a function called whenever the view's center enters/leaves an alert area
onAlert={this.onAlertEvent}
  • dragEnabled - whether the user can drag the view or not
dragEnabled={true}
  • dragWithSpring - specify to make dragging behavior of the view occur using a spring
dragWithSpring={{tension: 2000, damping: 0.5}}
  • dragToss - time in seconds the view is allowed to be tossed before snapping to a point
dragToss={0.1}
animatedValueX={this._deltaX}
animatedValueY={this._deltaY}
animatedNativeDriver={false}
  • initialPosition - used to initialize the view's position to a position different than it's original center
initialPosition={{x: -140, y: -280}}

Interactable.View Methods

setVelocity(params) - used to imperatively set the view's velocity in order to move it around
instance.setVelocity({x: 2000});

Takes a single argument, which is a params object containing:

  • x - The horizontal velocity. Optional.
  • y - The vertical velocity. Optional.
snapTo(params) - used to imperatively cause the view to snap to one of its snap points
instance.snapTo({index: 2});

Takes a single argument, which is a params object containing:

  • index - The index of the snap point in the snapPoints array. Optional.
changePosition(params) - used to imperatively set the view's position
instance.changePosition({x: 120, y: 40});

Takes a single argument, which is a params object containing:

  • x - The x coordinate.
  • y - The y coordinate.

bringToFront() - bring view to front (Android Only)
instance.bringToFront();

Animating other views according to Interactable.View position

This library is integrated with the Animated library in order to support performant animations of other views according to the movement of the Interactable.View.

Consider the following use-cases:

  • Buttons that appear using a fade & scale animation under a drawer as it's being dragged (example)
  • Image in a collapsible header that scales as it's snapped between states (example)

In these use-cases, we have views different from the one the user is interacting with, that animate according to the interactive view's position. Since Animated library uses Animated.Value to animate view properties, we support setting the value of an Animated.Value instance according to position of the interactable view. The Animated.Value will contain the delta between the Interactable.View original center and new center. This can be done separately on the X axis and Y axis.

After setting this up, use Animated to declaratively define interpolations of the Animated.Value to various animatable view properties like opacity, scale, rotation, translateX and translateY:

this._deltaY = new Animated.Value(0);

<Animated.View style={{
  transform: [{
    scale: this._deltaY.interpolate({
      inputRange: [-150, -150, 0, 0],
      outputRange: [0.3, 0.3, 1, 1]
    })
  }]
}}>
  ...
</Animated.View>

<Interactable.View
  verticalOnly={true}
  snapPoints={[{y: 0}, {y: -150}]}
  animatedValueY={this._deltaY}
>
  ...
</Interactable.View>

Implementation Details

Originally, the iOS implementation relied on UIKit Dynamics - a powerful native animation engine for physical interactions. A physics engine is required in order to make the interaction life-like. Consider the action of tossing a view connected via a spring to a snap point. A simple native spring animation will not be enough to take the initial velocity vector into account.

At some point, UIKit Dynamics was dropped in favor of a home-brewed physics implementation in order to provide more control over the behaviors. This also paved the way for the Android port since there's no parallel to UIKit Dynamics for Android. The home-brewed physics engine was straightforward to port from Objective-C to Java and is now part of this library.

Contributing

If you are interested in the project, have feedback or want to contribute don't hesitate to contact me. I'm particularly interested in ideas on how to expand the declarative API for more use-cases and suggestions on how to improve performance. PRs are always welcome.

License

MIT

react-native-interactable's People

Contributors

ahanriat avatar arnonz avatar bogobogo avatar buttershub avatar chourobin avatar d4vidi avatar electricmonk avatar emretekince avatar ethanshar avatar felipe-xavier avatar gabeboning avatar ghsamm avatar grin avatar iamtommcc avatar inbal-tish avatar leonatan avatar loyalblocks-tzachi avatar lukefanning avatar mkuczera avatar ofirdagan avatar pfeiffer avatar radko93 avatar rotemmiz avatar rush1506 avatar shahardavid avatar silyevsk avatar sondremare avatar talkol avatar tommeier avatar yershalom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-interactable's Issues

[Feature Request] Threshold events

I have a similar use case to #18 where I want to use interactable for a drawer, but in our case, the drawer pops in when you select a pin on a map, but goes completely off screen when you close it. The problem I'm running into is there is a couple second delay while the animation settles, before onSnap is received. I'm essentially destroying the interactable view after receiving the onSnap event, but if the user clicks another pin before the animation finishes, the component isn't actually destroyed and puts us in a weird state where the interactable is off screen, but our app thinks it's visible.
I was wondering if you would consider a pull request to add vertical/horizontal thresholds. Basically whenever the center point crosses the threshold(like near the bottom of the screen), fire a thresholdCrossed event, with a threshold id, similar to onSnap. I believe this would need to go in both the IntereactableView and the PhysicsAnimator. Does this sound right? Is there a simpler solution I'm missing? Thanks for the great library!

Cannot build for Android

Hi

When I run react-native run-android it fails with the error Cannot evaluate module react-native-interactable : Configuration with name 'default' not found.

Using react-native v0.42.0 and react-native-interactable v0.0.4

How can I move to snapPoint programmatically ?

Hello,

I need to be able to move an Intractable.View to a snapPoint by providing the index. I've tried with setVelocity, but the problem is that the view is moved relatively to it's current position. I have a lot of items on my screen that need to be swapped when you drag one on another.

How can I achieve that ?

How to select interactable area?

My situations is this:
image

I would like to make card which have two snapping points: closed and opened. I would also like to drag the card only from the red area. But at the moment I am facing problem where my Card component is wrapped in Interactable.View and the Card moves nicely but no matter where I drag it, it moves. This is not decired.

So basically I would like to move the whole card but drag would be enable only from the red area :) For example the white are of the card has ListView which I can't scroll at the moment

Let me know if you want more details :)

naming - snapPoints -> snapBehaviors, springPoints -> springBehaviors

There was a suggestion that I like to rename the following props for consistency:

snapPoints -> snapBehaviors

springPoints -> springBehaviors

gravityPoints -> gravityBehaviors

frictionAreas -> frictionBehaviors

alertAreas -> alertBehaviors

The "behaviors" convention is also used by UIKit Dynamics by Apple.

Do you think it's a good idea?

Crash on android when dragging is initiated from a view with prop pointerEvents="box-none"

When Interactable.View contains a view with the prop pointerEvents="box-none" the application will crash when dragging is started from this view.

I've created a repo which reproduces this issue. Specifically this line will crash the app.

the error log of the crash looks like this:

AndroidRuntime: Shutting down VM
AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.interactablecrash, PID: 15408
AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.wix.interactable.physics.PhysicsBehavior.setAnchorPoint(android.graphics.PointF)' on a null object reference
AndroidRuntime: 	at com.wix.interactable.InteractableView.handleTouch(InteractableView.java:218)
AndroidRuntime: 	at com.wix.interactable.InteractableView.onTouchEvent(InteractableView.java:197)
AndroidRuntime: 	at android.view.View.dispatchTouchEvent(View.java:10023)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2626)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2307)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2654)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2321)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2321)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2321)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2321)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
AndroidRuntime: 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2321)
AndroidRuntime: 	at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:414)
AndroidRuntime: 	at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1808)
AndroidRuntime: 	at android.app.Activity.dispatchTouchEvent(Activity.java:3064)
AndroidRuntime: 	at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:376)
AndroidRuntime: 	at android.view.View.dispatchPointerEvent(View.java:10243)
AndroidRuntime: 	at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)
AndroidRuntime: 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4306)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
AndroidRuntime: 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3999)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
AndroidRuntime: 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4056)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
AndroidRuntime: 	at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6247)
AndroidRuntime: 	at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6221)
AndroidRuntime: 	at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6182)
AndroidRuntime: 	at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6350)
AndroidRuntime: 	at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
AndroidRuntime: 	at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
AndroidRuntime: 	at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176)
AndroidRuntime: 	at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:6321)
AndroidRuntime: 	at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:6373)
AndroidRuntime: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
AndroidRuntime: 	at android.view.Choreographer.doCallbacks(Choreographer.java:686)
AndroidRuntime: 	at android.view.Choreographer.doFrame(Choreographer.java:615)
AndroidRuntime: 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
AndroidRuntime: 	at android.os.Handler.handleCallback(Handler.java:751)
AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:95)
AndroidRuntime: 	at android.os.Looper.loop(Looper.java:154)
AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6121)
AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
AndroidRuntime: 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
04-05 19:06:17.304   893  3451 W ActivityManager:   Force finishing activity com.interactablecrash/.MainActivity

On another note, it seems that having the Interactable.View as the root of your project (the component you pass to registerComponent) will crash the app when dragging anywhere. But I don't think many people would structure their app like that.

Naming changes

limitX + limitY -> boundaries

snapTo -> snapPoints
springs -> springPoints
gravity -> gravityPoints
friction -> frictionAreas

gravity.limitX + gravity.limitY -> gravity.influenceArea

onSnap + onStop -> onStop and combine both, to always include the closest point (snap / spring / gravity) with its id

drag -> dragWithSpring + dragToss

Toggle Interactable?

Is there any way to toggle the "Interactability" of a view? Basically what I am trying to do is only allow a user to interact with my view after they have performed another action such as pressing another button.

I have a scrollable tab view where one tab contains a list of images, where each image takes up the full width of the screen. Imagine this: (with limited white space)

687474703a2f2f692e696d6775722e636f6d2f4572413247516f2e676966

But you can swipe left and right to change tabs.

I am trying to achieve "swipeable" cards and swipe to change tabs. Thus, in order to swipe a card you have to perform another action to enable the interaction, such as pressing a button or a long press. Otherwise you will only ever be able to swipe the cards and not change tab.

setVelocity not working

I am not sure this is a bug or I used the api in a wrong way.

<Interactable.View
  style={Styles.modal} 
  ref={ref => (this.modal = ref)}
  initialPosition={{ y: -screenHeight }}
  snapPoints={[{ y: -screenHeight }, { y: 0 }, { y: screenHeight }]}
  boundaries={{ top: -screenHeight, bottom: 0 }}
  verticalOnly>
</Interactable.View>

when I use this.modal.setVelocity({ y: 2000 }); it works fine.
However, this.modal.setVelocity({ y: -2000 }); doesn't dismiss the modal view.

A way to have a swipe distance threshold

Hey!

Is there a current way how I could add a swipe distance threshold? Meaning a minimum amount of distance needs to be swiped for interaction to take effect?

Android build error

After linking, and also updating the settings.gradle, I'm faced with a build issue on Android:

android/app/build/intermediates/res/merged/debug/values-v24/values-v24.xml:3: AAPT: Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Borderless.Colored'.

This appears multiple times for other sources such as 'android:TextAppearance.Material.Widget.Button.Borderless.Colored'.

RN 0.42
Mac OS Sierra 10.11.16

Anchor gestures only to a specific component

Great library, this is the missing piece of Animation puzzle on React-Native!
Great job!

I have a question about handling gestures to only a specific component, in the examples like NotifPanel I saw that gestures are handled in all components wrapped by Interactable.View.
In some cases this is a desired behavior, but in this specific example it would be better that gestures are handled only by a panel footer View.

There is a way to get this behavior?

snapTo not a function (0.0.8)

Hi, first of all — thank you so much for all the hard work you guys are doing here.

I've updated to the latest version (0.0.8) and trying to use the snapTo function without success. I'm getting 'snapTo is not a function'. I'm successfully calling the setVelocity on the same ref, so I know the ref is functional.

Any idea?

[Feature Request] animate start api

I am not sure that using this library to build a modal animation is a good idea or not.
I just found I am not able to change the position of a view which is initialised by a Animated.Value().
I tried to use Animated.timing().start() to change the value but it never works.
So, I am wondering if we could have a function to call from the ref to trigger the animation programmatically?

Declarative API discussion

The hardest part in making this library isn't the native driver, it's designing a good declarative API.

On one hand it has to be simple and concise. On the other hand, it needs to be general-purpose and flexible.

I don't think we nailed it yet. If you have suggestions on how to improve the API, please make them here.

Once we have enough suggestions that cover the basic use-cases that people require, we can agree on the "official" API and release version 1.0 of this library. Maybe even submit it as a PR to react-native core then.

Picker does not respond when in Interactable.View

So I have a Picker component inside an Interactable.View (it is for a pull-down screen with customer billing information, works something like notifications pull-down area on iOS). The Picker does not respond to touches, the Interactable.View just trembles and that's all.
I have checked that if I just replace the Interactible.View with regular View, all works well.
Am I doing something wrong or is it a bug in the library?
Thanks

Add ability to configure movement threshold

First of all, amazing work. I was dealing with a lot of pain with dragging things in RN and currently trying to use Interactable instead. So far it looks excellent, except for one thing.

I have a draggable view that is tappable in some of it's areas. I looked through the examples and noticed you don't have an example of a Touchable inside an Interactable.View.
I tried it and it's rather hard to tap my buttons with it.

Before this solution I used PanResponder and was able to work through this issue by defining a threshold to distinguish between a drag and any other touch with something like this:

onMoveShouldSetPanResponder : (e, gestureState) => {
    const {dx, dy} = gestureState;
    const threshold = 20;        

    return (Math.abs(dx) > threshold) || (Math.abs(dy) > threshold);
},

I would imagine a nice api for that would just be some sensitivityThreshold prop that accepts a number or something similar.

Edit:

I've gathered a bit more information - looks like my taps are happening even if I drag, but because I'm not using the native driver (throws some exception atm) it happens after a couple of seconds of latency.
I'm guessing the cause of the latency is that the touch leave is queued after all the touch movement had been handled, although it seems to be handled perfectly on the spot but the onPress is still triggered at least a whole second after I stopped dragging.

Ideally I would like to prevent dragging inside my threshold and let it go through to my touchables, but if it is considered a drag - the touches shouldn't propagate to these touchables.

handleTouch crash on Android

Getting a crash on half of my Interactable.Views on version 0.0.7
It seems fine with my swipe row which is basically just a view with a button inside
But when it is a image or my Chart from Victory it crashes.

I know it's a odd use case but it was working before the changes the handleTouch,

Here is the stacktrace

com.wix.interactable.InteractableView.handleTouch

InteractableView.java line 218

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.wix.interactable.physics.PhysicsBehavior.setAnchorPoint(android.graphics.PointF)' on a null object reference

com.wix.interactable.InteractableView.handleTouch InteractableView.java:218
com.wix.interactable.InteractableView.onTouchEvent InteractableView.java:197
android.view.View.dispatchTouchEvent View.java:9993
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2828
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2499
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.widget.ScrollView.dispatchTouchEvent ScrollView.java:645
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
android.view.ViewGroup.dispatchTransformedTouchEvent ViewGroup.java:2839
android.view.ViewGroup.dispatchTouchEvent ViewGroup.java:2514
com.android.internal.policy.PhoneWindow$DecorView.superDispatchTouchEvent PhoneWindow.java:2831
com.android.internal.policy.PhoneWindow.superDispatchTouchEvent PhoneWindow.java:1863
android.app.Activity.dispatchTouchEvent Activity.java:3046
com.android.internal.policy.PhoneWindow$DecorView.dispatchTouchEvent PhoneWindow.java:2792
android.view.View.dispatchPointerEvent View.java:10228
android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent ViewRootImpl.java:5344
android.view.ViewRootImpl$ViewPostImeInputStage.onProcess ViewRootImpl.java:5180
android.view.ViewRootImpl$InputStage.deliver ViewRootImpl.java:4620
android.view.ViewRootImpl$InputStage.onDeliverToNext ViewRootImpl.java:4673
android.view.ViewRootImpl$InputStage.forward ViewRootImpl.java:4639
android.view.ViewRootImpl$AsyncInputStage.forward ViewRootImpl.java:4781
android.view.ViewRootImpl$InputStage.apply ViewRootImpl.java:4647
android.view.ViewRootImpl$AsyncInputStage.apply ViewRootImpl.java:4838
android.view.ViewRootImpl$InputStage.deliver ViewRootImpl.java:4620
android.view.ViewRootImpl$InputStage.onDeliverToNext ViewRootImpl.java:4673
android.view.ViewRootImpl$InputStage.forward ViewRootImpl.java:4639
android.view.ViewRootImpl$InputStage.apply ViewRootImpl.java:4647
android.view.ViewRootImpl$InputStage.deliver ViewRootImpl.java:4620
android.view.ViewRootImpl.deliverInputEvent ViewRootImpl.java:7306
android.view.ViewRootImpl.doProcessInputEvents ViewRootImpl.java:7184
android.view.ViewRootImpl.enqueueInputEvent ViewRootImpl.java:7145
android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent ViewRootImpl.java:7416
android.view.InputEventReceiver.dispatchInputEvent InputEventReceiver.java:185
android.view.InputEventReceiver.nativeConsumeBatchedInputEvents InputEventReceiver.java
android.view.InputEventReceiver.consumeBatchedInputEvents InputEventReceiver.java:176
android.view.ViewRootImpl.doConsumeBatchedInput ViewRootImpl.java:7380
android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run ViewRootImpl.java:7443
android.view.Choreographer$CallbackRecord.run Choreographer.java:920
android.view.Choreographer.doCallbacks Choreographer.java:695
android.view.Choreographer.doFrame Choreographer.java:625
android.view.Choreographer$FrameDisplayEventReceiver.run Choreographer.java:906
android.os.Handler.handleCallback Handler.java:739
android.os.Handler.dispatchMessage Handler.java:95
android.os.Looper.loop Looper.java:158
android.app.ActivityThread.main ActivityThread.java:7224
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run ZygoteInit.java:1230
com.android.internal.os.ZygoteInit.main ZygoteInit.java:1120

[Feature Request] snap disable

is that possible to disable the snap on the element <Interactable.view /> cause I want to use setVelocity to control the animation only.

Nesting Interactable.Views

Is it possible to nest Interactable.Views? I want to create something like the notification panel like example but with the functionality to swipe away the individual notifications like the Google-Now card.

I tried too make it myself quickly but couldn't get it to work. The parent view seems to always handle the panning.

My structure looks something like this:

<Interactable.View verticalOnly>         // notification panel
  <Interactable.View horizontalOnly />   // nofitication
  <Interactable.View horizontalOnly />   // nofitication
  <Interactable.View horizontalOnly />   // nofitication
  <Interactable.View horizontalOnly />   // nofitication
</Interactable.View>

Anyway, thanks for the amazing library!

MapPanel.js example with ScrollView (similar to Material Bottom sheets)

First, sorry if this is very related to #35, just wanted to make sure.

Basically, I was trying to mimic Bottom sheets from Material spec using this example MapPanel.js.

But, with one addition, changing content view from the panel here to ScrollView. Then, you cannot interact with it anymore (only internal scrolling).

You can see this is an issue in the real example, on Apple Maps-Style Panel, if you change your device orientation (I try with Android) to landscape, because there is no internal scroll, you cannot see the whole view.

Desired result would be something like:

ezgif-3-fa4a3bc778

Looking to add a community maintainer to our team!

One of the things we're trying to improve is our velocity in accepting PR's and community contributions for OSS we make at Wix.

One of the ways I want to test out, is to bring in people from the community to be involved as official maintainers. This repo can be a good test for this because we don't use it yet in our production.

In addition to me and other Wix guys who maintain this repo fix issues and accept PR's, is there anyone from the community who wants to join in?

It will be an interesting experiment

Issue with animated native driver

Hey! Thanks for real cool work! So

There is an issue if i set animatedNativeDriver={true} and then my component with interactable re-render - is's fail with this error
screen shot 2017-03-26 at 15 55 34

To reproduce take any example and add state value, then change it

Apple Maps-Style Panel - suggestion

When the card is fully visible (on the 2nd step) and the user starts panning card to the top there should be a linear deceleration (in Y position change).

CollapsingHeader example with scrolling content

In the CollapsingHeader example, the main body (below the header) does not scroll when the content goes off screen. The below example is a copy paste of the example with extra View content. Only 4 boxes are show.

I've tried experimenting with a ScrollView, however it either causes the header to scroll off screen (when a parent of Interactable) or does nothing (a child of Interactable).

If Interactable is wrapped in a ScrollView, it doesn't seem to pick up all of the scroll events (header scales sometimes, sometimes not), but I do see the content.

Is there a way to achieve this?

import React, { Component } from 'react';
import { StyleSheet, ScrollView, View, Animated } from 'react-native';
import Interactable from 'react-native-interactable';

export default class CollapsingHeader extends Component {
  constructor(props) {
    super(props);
    this._deltaY = new Animated.Value(0);
  }
  render() {
    return (
      <View style={styles.container}>

        <View style={{backgroundColor: 'red', height: 250, alignItems: 'center'}}>
          <Animated.View style={{
              transform: [
                {
                  translateY: this._deltaY.interpolate({
                    inputRange: [-150, -150, 0, 0],
                    outputRange: [-58, -58, 0, 0]
                  })
                },
                {
                  scale: this._deltaY.interpolate({
                    inputRange: [-150, -150, 0, 0],
                    outputRange: [0.35, 0.35, 1, 1]
                  })
                }
              ]
            }}>
            <View style={{width: 150, height: 150, backgroundColor: 'blue', borderRadius: 75, marginTop: 50}} />
          </Animated.View>
        </View>

        <Interactable.View
          verticalOnly={true}
          snapPoints={[{y: 0}, {y: -150}]}
          boundaries={{top: -150}}
          animatedValueY={this._deltaY}>
          <View style={{left: 0, right: 0, height: 650, backgroundColor: '#e0e0e0'}}>
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
            <View style={{ backgroundColor: 'grey', height: 100, marginVertical: 8 }} />
          </View>
        </Interactable.View>

      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
  }
});

How to catch onDrag ?

Hello,

I'm wondering how I can possibly catch an onDrag event on my Interactable.View because I want my UI to change when the user is dragging.
I didn't saw any prop that could do this in the doc.

snapTo no animation,

Creating a listview with a delete option using the real life example, mixed with animation list

My issue is 'On iOS, the list rows that are hidden do not reset on ListView re-rendering process'. So i have to reset the height of the cell View which work well, but i also need to snap the view back to it's original position. Currently i am using setVelocity to a high number, which limits the animation but there is still a small delay which doesn't look nice. As you can see in the code below.

_close() {
		this.refs['row'].snapTo({index: 2})

		Animated.parallel([
			Animated.timing(this.state._deleteRight, {
				toValue: -500,
				duration: 500
			}),
			Animated.timing(this.state._rowHeight, {
				toValue: 0,
				duration: 500
			}),
		]).start(this.removeItem)
	}

	removeItem = () => {

		if (this.refs['row']) {
			this.refs['row'].setVelocity({x: 60000})
		}
		this.resetHeight()

	}

	resetHeight() {
		
		Animated.parallel([

			Animated.timing(this.state._deleteRight, {
				toValue: width - 120,
				duration: 0
			}),
			Animated.timing(this.state._rowHeight, {
				toValue: this.state._defaultHeight,
				duration: 0
			}),
		]).start(this.props.removeFromList())
	}

My question is, has anyone else come across this issue, and came to a more elegant solution ? setting the view to the snapPoint of the closed index without an animation would be a great option.

onPress is passed to Animated.View while dragging

Sliding between Slides still passes an onPress to the child view.

I have a button, and if I drag from it, it gets pressed. Any idea how to solve this reasonably and show that I wouldn't have to handle it on every button separately? To specify, I released the mouse myself and onPressed wasn't passed while I was dragging, but upon release of of drag.

slidingandpress

onResponderRelease

Hi guys :)

Is there a way to get an event just after the user stopped dragging ? Something like onResponderRelease with panResponder.

I need to trigger an action only when the user stopped dragging and moved at least to a specific threshold.

Touches not detected below notifications pulldown.

I have the exact same setup as in the NotifPanel Real Life Example. The problem on iOS is no touches are detected in the screen below the panel (try wrapping the image into TouchableOpacity, it will not do a thing). On Android it works fine.

'React/RCTViewManager.h' file not found

/node_modules/react-native-interactable/ios/Interactable/InteractableViewManager.h:10:9: 'React/RCTViewManager.h' file not found

#import <React/RCTViewManager.h>

Using react-native 0.35.0

HandleTouches example isn't running properly on Android

The cards in the example are hard to drag on Android, they keep "slipping" off the finger after a short movement most of the time.

I tried to look into it a little bit. Looks like the ScrollView is what is causing the problem there. I added a TouchableWithoutFeedback on the IconDrawer example (no ScrollView + horizontal), it drags perfectly (but it is pressed even when you drag it all the way).

I also tried to remove all touchables from the card and just keep the ScrollView, same result. Also tried removing the horizontalOnly prop, still slipping.

Screen recording of the issue

How to keep buttons under swipeout invisible?

I have a swipeout that reveals a delete button, but when I press the TouchableOpacity, it reveals the button underneath briefly. How does one avoid this? I tried TouchableHighlight, but that seems to do the same thing (even if I set the activeOpacity to 1)
screen shot 2017-04-17 at 4 05 20 pm

Error builiding (PhysicsBehavior.m)

I'm getting the following:

PhysicsBehavior.m
Unknown type name 'UIImpactFeedbackGenerator'
impliciit conversion of an Object-C pointer to 'int*' is disallowed with ARC
Use of undeclared identifier 'UIImpactFeedbackStyleLight'
Bad receiver type 'int*'

Add direction specific touch blocker for Android

Is there a possibility to add, in addition to 'blockAllTouch', something like 'blockVerticalSwipe' && 'blockHorizontalSwipe'? Having some problems with a ListView that sits inside a horizontal carousel, that sits inside a vertical Interactable. The blocker does solve the problem of the ListView and it's scrolling regularly, but I can't swipe inside the carousel, as all touch events are blocked inside the ListView. If I could block only vertical swipes, the combination of the Interactable + carousel + ListView would've worked.

Sorry I don't just open a PR to solve this - I'm not familiar with neither Objective-C nor Java.

Gestures that aren't "horizontal" enough are blocked by horizontalOnly

While I was playing with some of the examples, I noticed that certain gestures were not resulting in any movement. For example, a SwipeableCard, which has horizontalOnly specified would not pick up on certain gestures I was making (e.g. an arc that starts fairly vertical and eventually moves horizontally).

In the iOS code, I see that gestureRecognizerShouldBegin uses the heuristic of "more horizontal than vertical" to determine whether it should respond to a gesture. I'm curious whether this was done for performance reasons, or something else? It may be worth adding an additional property that controls this behavior, while leaving verticalOnly and horizontalOnly to control movement axes.

Notification Panel bug

When swiping notification panel with a big velocity (top to bottom) it gets stuck jumping and flickering.

Manual Linking in iOS using react-native as a Pod

Hi Guys , I here to share with you a situation using interactable lib in a already existing brownfield app.

The installation instructions sais that we need do make this 2 steps ( http://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking)

But if you are using react-native as a pod , including te lib in a PodFile, you will face a problem building your app. The Interactable lib does not find the React lib .

We solved this issue creating another pod target named Interactable , and pointing to the node_modules right directory. At this target we declared React lib dependencies. After that run a $ pod update solved this issue.

What you think about including this steps ? Maybe someone is working in create a podSpec to interactable already.

Hope that this sharing helps others !

Change state programmatically without an interaction

You all have done some really brilliant work here – kudos!

One thing I'm wondering – would it be feasible to add a method for changing the Interactable state programmatically? Something like snapTo({x: 360})?

This would help in the case of using an Interactable.View as an app menu drawer. It is sometimes necessary to provide a "menu button" that toggles an open or closed state onPress, as an alternative to a swipe interaction.

I've been unable to discern a way of doing this, given the current API. My understanding is that animatedValueX and animatedValueY are essentially read-only?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.