From 9e354a8b68ffb4e1ad0a9c62c7f87c555bb0fd89 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 23 Apr 2017 15:17:54 +0100 Subject: [PATCH 01/31] Switched from react motion to react move MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit React move doesn’t work on React Native. Had to: - Remove babel section from package json of react-move - Removed timestamp || from https://github.com/tannerlinsley/react-move/blob/master/src/Transition.j s#L220 --- .../sample/medley/createStateNavigator.js | 25 ++++++++-------- .../src/NavigationMotion.tsx | 29 ++++++++----------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/NavigationReactNative/sample/medley/createStateNavigator.js b/NavigationReactNative/sample/medley/createStateNavigator.js index 0739fc3ab..b5873ef8f 100644 --- a/NavigationReactNative/sample/medley/createStateNavigator.js +++ b/NavigationReactNative/sample/medley/createStateNavigator.js @@ -1,7 +1,6 @@ import React from 'react'; import {StateNavigator} from 'navigation'; import Scene from './Scene'; -import {spring} from 'navigation-react-native'; export default () => { const stateNavigator = new StateNavigator([ @@ -17,20 +16,20 @@ export default () => { sceneSouth.renderScene = (data, moveScene) => ; sceneWest.renderScene = (data, moveScene) => ; - sceneNorth.unmountedStyle = () => ({translateY: spring(-1, {stiffness: 90})}); - sceneEast.unmountedStyle = () => ({translateX: spring(1, {stiffness: 90})}); - sceneSouth.unmountedStyle = () => ({translateY: spring(1, {stiffness: 90})}); - sceneWest.unmountedStyle = () => ({translateX: spring(-1, {stiffness: 90})}); + sceneNorth.unmountedStyle = () => ({translateY: -1}); + sceneEast.unmountedStyle = () => ({translateX: 1}); + sceneSouth.unmountedStyle = () => ({translateY: 1}); + sceneWest.unmountedStyle = () => ({translateX: -1}); - sceneNorth.mountedStyle = () => ({translateY: spring(0, {stiffness: 90})}); - sceneEast.mountedStyle = () => ({translateX: spring(0, {stiffness: 90})}); - sceneSouth.mountedStyle = () => ({translateY: spring(0, {stiffness: 90})}); - sceneWest.mountedStyle = () => ({translateX: spring(0, {stiffness: 90})}); + sceneNorth.mountedStyle = () => ({translateY: 0}); + sceneEast.mountedStyle = () => ({translateX: 0}); + sceneSouth.mountedStyle = () => ({translateY: 0}); + sceneWest.mountedStyle = () => ({translateX: 0}); - sceneNorth.crumbStyle = () => ({translateY: spring(-.3, {stiffness: 90})}); - sceneEast.crumbStyle = () => ({translateX: spring(.3, {stiffness: 90})}); - sceneSouth.crumbStyle = () => ({translateY: spring(.3, {stiffness: 90})}); - sceneWest.crumbStyle = () => ({translateX: spring(-.3, {stiffness: 90})}); + sceneNorth.crumbStyle = () => ({translateY: -.3}); + sceneEast.crumbStyle = () => ({translateX: .3}); + sceneSouth.crumbStyle = () => ({translateY: .3}); + sceneWest.crumbStyle = () => ({translateX: -.3}); return stateNavigator; } diff --git a/NavigationReactNative/src/NavigationMotion.tsx b/NavigationReactNative/src/NavigationMotion.tsx index 8d5b7c818..c212093e5 100644 --- a/NavigationReactNative/src/NavigationMotion.tsx +++ b/NavigationReactNative/src/NavigationMotion.tsx @@ -1,6 +1,6 @@ import { StateNavigator } from 'navigation'; import * as React from 'react'; -import { TransitionMotion } from 'react-motion'; +var Transition = require('react-move').Transition; import { View } from 'react-native'; class NavigationMotion extends React.Component { @@ -113,32 +113,27 @@ class NavigationMotion extends React.Component { return {...data, ...(scene && scene.data)}; } getStyle(styleProp, {state, data, url}, strip = false) { - var style = typeof styleProp === 'function' ? styleProp(state, this.getSceneData(data, url)) : styleProp; - var newStyle: any = {}; - for(var key in style) - newStyle[key] = !strip ? style[key] : style[key].val; - return newStyle; + return typeof styleProp === 'function' ? styleProp(state, this.getSceneData(data, url)) : styleProp; } render() { var {unmountedStyle, mountedStyle, crumbStyle, style, children} = this.props; return (this.getStateNavigator().stateContext.state && - this.getStyle(unmountedStyle, sceneContext, true)} - willLeave={({data: sceneContext}) => this.getStyle(unmountedStyle, sceneContext)} - didLeave={({data: sceneContext}) => {this.clearScene(sceneContext.url)}} - styles={this.getScenes().map(({mount, ...sceneContext}) => ({ - key: sceneContext.url, - data: sceneContext, - style: this.getStyle(mount ? mountedStyle : crumbStyle, sceneContext) - }))}> + sceneContext.url} + enter={sceneContext => this.getStyle(unmountedStyle, sceneContext, true)} + leave={sceneContext => this.getStyle(unmountedStyle, sceneContext)} + // didLeave={({data: sceneContext}) => {this.clearScene(sceneContext.url)}} + update={sceneContext => this.getStyle(sceneContext.mount ? mountedStyle : crumbStyle, sceneContext)}> {tweenStyles => ( - {tweenStyles.map(({key, data: {scene, state, data, url}, style}) => ( + {tweenStyles.map(({key, data: {scene, state, data, url}, state: style}) => ( children(style, scene && scene.element, key, state, this.getSceneData(data, url)) ))} )} - + ); } } From 20ae613cddb56f466284d301a7a9332b570c4cea Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 23 Apr 2017 15:18:02 +0100 Subject: [PATCH 02/31] Tweaked format --- NavigationReactNative/sample/medley/Medley.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/sample/medley/Medley.js b/NavigationReactNative/sample/medley/Medley.js index d0fe65083..9472d7a00 100644 --- a/NavigationReactNative/sample/medley/Medley.js +++ b/NavigationReactNative/sample/medley/Medley.js @@ -10,7 +10,7 @@ export default ({stateNavigator}) => ( crumbStyle={state => state.crumbStyle()} style={{flex: 1}} stateNavigator={stateNavigator}> - {({translateX = 0, translateY = 0}, scene, url) => ( + {({translateX = 0, translateY = 0}, scene, url) => ( Date: Sun, 23 Apr 2017 18:00:13 +0100 Subject: [PATCH 03/31] Prevented re-rendering crumb scenes When shared elements came in, it brought in context. Turns out ReactReconciler always re-renders when context is there. Before context it the reconciler short circuited the update because the scenes where === identical. So, cached the url in constructor and added a shouldComponentUpdate to avoid re-rendering crumb scenes --- NavigationReactNative/sample/medley/Scene.js | 65 ++++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/NavigationReactNative/sample/medley/Scene.js b/NavigationReactNative/sample/medley/Scene.js index e0993f9e0..6799286a7 100644 --- a/NavigationReactNative/sample/medley/Scene.js +++ b/NavigationReactNative/sample/medley/Scene.js @@ -9,33 +9,44 @@ const nextDirection = { West: 'North', }; -export default ({direction, color, stateNavigator}) => { - const {url, crumbs} = stateNavigator.stateContext; - return ( - - - { - if (url === stateNavigator.stateContext.url) - stateNavigator.navigate(`scene${nextDirection[direction]}`); - }}> - {direction} {crumbs.length} - - {stateNavigator.canNavigateBack(1) && { - if (url === stateNavigator.stateContext.url) - stateNavigator.navigateBack(1); - }}> - Back - } - - ) -}; +export default class Scene extends React.Component { + constructor(props, context) { + super(props, context); + const {url, crumbs} = props.stateNavigator.stateContext; + this.url = url; + this.crumbs = crumbs; + } + shouldComponentUpdate(props) { + return this.url === props.stateNavigator.stateContext.url; + } + render() { + const {direction, color, stateNavigator} = this.props; + return ( + + + { + if (this.url === stateNavigator.stateContext.url) + stateNavigator.navigate(`scene${nextDirection[direction]}`); + }}> + {direction} {this.crumbs.length} + + {stateNavigator.canNavigateBack(1) && { + if (this.url === stateNavigator.stateContext.url) + stateNavigator.navigateBack(1); + }}> + Back + } + + ) + } +} const styles = StyleSheet.create({ scene: { From 358aae82a6339f52eae14fdbaa68d55dc06655fb Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 23 Apr 2017 18:34:03 +0100 Subject: [PATCH 04/31] Cleared out scenes when animation's over React Motion calls it per item, React Move calls it for all together. Prefer React Move, but means have to clear out possible multiple scenes in one go --- .../src/NavigationMotion.tsx | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/NavigationReactNative/src/NavigationMotion.tsx b/NavigationReactNative/src/NavigationMotion.tsx index c212093e5..264b4a8e3 100644 --- a/NavigationReactNative/src/NavigationMotion.tsx +++ b/NavigationReactNative/src/NavigationMotion.tsx @@ -94,11 +94,19 @@ class NavigationMotion extends React.Component { } return sharedElements; } - clearScene(url) { - this.setState(prevState => { - var scenes = {...prevState.scenes}; - delete scenes[url]; - delete this.sharedElements[url]; + clearScenes() { + this.setState(({scenes: prevScenes}) => { + var scenes = {...prevScenes}; + var urls = this.getScenes().reduce((urls, {url}) => { + urls[url] = true; + return urls; + }, {}); + for(var url in prevScenes) { + if (!urls[url]) { + delete scenes[url]; + delete this.sharedElements[url]; + } + } return {scenes}; }); } @@ -124,7 +132,7 @@ class NavigationMotion extends React.Component { getKey={sceneContext => sceneContext.url} enter={sceneContext => this.getStyle(unmountedStyle, sceneContext, true)} leave={sceneContext => this.getStyle(unmountedStyle, sceneContext)} - // didLeave={({data: sceneContext}) => {this.clearScene(sceneContext.url)}} + onRest={() => this.clearScenes()} update={sceneContext => this.getStyle(sceneContext.mount ? mountedStyle : crumbStyle, sceneContext)}> {tweenStyles => ( From 000f4e68edc9e0797acb6a5e77af87b8334c1752 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 23 Apr 2017 18:35:33 +0100 Subject: [PATCH 05/31] Tweaked format --- NavigationReactNative/src/NavigationMotion.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/NavigationMotion.tsx b/NavigationReactNative/src/NavigationMotion.tsx index 264b4a8e3..ada3f21ff 100644 --- a/NavigationReactNative/src/NavigationMotion.tsx +++ b/NavigationReactNative/src/NavigationMotion.tsx @@ -131,9 +131,9 @@ class NavigationMotion extends React.Component { data={this.getScenes()} getKey={sceneContext => sceneContext.url} enter={sceneContext => this.getStyle(unmountedStyle, sceneContext, true)} + update={sceneContext => this.getStyle(sceneContext.mount ? mountedStyle : crumbStyle, sceneContext)} leave={sceneContext => this.getStyle(unmountedStyle, sceneContext)} - onRest={() => this.clearScenes()} - update={sceneContext => this.getStyle(sceneContext.mount ? mountedStyle : crumbStyle, sceneContext)}> + onRest={() => this.clearScenes()}> {tweenStyles => ( {tweenStyles.map(({key, data: {scene, state, data, url}, state: style}) => ( From 547a8ba428ad7b7078bee7cbf8327594e791e877 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 23 Apr 2017 18:38:20 +0100 Subject: [PATCH 06/31] Renamed variable --- NavigationReactNative/src/NavigationMotion.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/NavigationMotion.tsx b/NavigationReactNative/src/NavigationMotion.tsx index ada3f21ff..1045eb18a 100644 --- a/NavigationReactNative/src/NavigationMotion.tsx +++ b/NavigationReactNative/src/NavigationMotion.tsx @@ -116,8 +116,8 @@ class NavigationMotion extends React.Component { {state, data, url, scene: this.state.scenes[url], mount: url === nextCrumb.url} )); } - getSceneData(data, url, prevState?) { - var scene = (prevState || this.state.scenes)[url]; + getSceneData(data, url, prevScenes?) { + var scene = (prevScenes || this.state.scenes)[url]; return {...data, ...(scene && scene.data)}; } getStyle(styleProp, {state, data, url}, strip = false) { From 77a0f42827d7ed24d5a03fdee29cd608bf8e4010 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 23 Apr 2017 19:17:10 +0100 Subject: [PATCH 07/31] Added basic typings for react move --- NavigationReactNative/src/NavigationMotion.tsx | 2 +- .../src/node_modules/@types/react-move.d.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 NavigationReactNative/src/node_modules/@types/react-move.d.ts diff --git a/NavigationReactNative/src/NavigationMotion.tsx b/NavigationReactNative/src/NavigationMotion.tsx index 1045eb18a..e03afbd8d 100644 --- a/NavigationReactNative/src/NavigationMotion.tsx +++ b/NavigationReactNative/src/NavigationMotion.tsx @@ -1,6 +1,6 @@ import { StateNavigator } from 'navigation'; import * as React from 'react'; -var Transition = require('react-move').Transition; +import { Transition } from 'react-move'; import { View } from 'react-native'; class NavigationMotion extends React.Component { diff --git a/NavigationReactNative/src/node_modules/@types/react-move.d.ts b/NavigationReactNative/src/node_modules/@types/react-move.d.ts new file mode 100644 index 000000000..e2a558e09 --- /dev/null +++ b/NavigationReactNative/src/node_modules/@types/react-move.d.ts @@ -0,0 +1,18 @@ +/// + +declare module "react-move" { + import { Component, ReactElement } from 'react'; + + interface TransitionProps { + data: Array; + getKey?: (data: any, i: number) => string; + update: (data: any) => any; + enter?: (data: any) => any; + leave?: (data: any) => any; + duration?: number; + easing?: string; + onRest: () => void; + children?: (data: any) => ReactElement; + } + export class Transition extends Component { } +} From f53f177e84a019a516455de15abecbfad8b8d681 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 25 Apr 2017 19:43:50 +0100 Subject: [PATCH 08/31] Switched Motion for Animate component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tricky bit was setting immutable to false on Animate component. If don’t set this then every time the SharedElementMotion sets State the Animate updates which resets the timer progress. So a 300ms animation takes longer because it resets 300 multiple times. Setting immutable makes it compare the data props on values instead of ===. Because the shared elements are going to the same place each update, the data’s the same so it doesn’t update and so doesn’t reset the timer --- NavigationReactNative/sample/zoom/Zoom.js | 8 +++---- .../sample/zoom/ZoomShared.js | 14 +++++------ .../src/SharedElementMotion.tsx | 24 +++++++++---------- .../src/node_modules/@types/react-move.d.ts | 11 +++++++++ 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/NavigationReactNative/sample/zoom/Zoom.js b/NavigationReactNative/sample/zoom/Zoom.js index 7ae7dfda0..646b8b5eb 100644 --- a/NavigationReactNative/sample/zoom/Zoom.js +++ b/NavigationReactNative/sample/zoom/Zoom.js @@ -1,13 +1,13 @@ import React from 'react'; import {View} from 'react-native'; -import {NavigationMotion, spring} from 'navigation-react-native'; +import {NavigationMotion} from 'navigation-react-native'; export default ({stateNavigator}) => ( {({opacity}, scene, url) => ( diff --git a/NavigationReactNative/sample/zoom/ZoomShared.js b/NavigationReactNative/sample/zoom/ZoomShared.js index f13028a71..f24d6eb8d 100644 --- a/NavigationReactNative/sample/zoom/ZoomShared.js +++ b/NavigationReactNative/sample/zoom/ZoomShared.js @@ -1,14 +1,14 @@ import React from 'react'; import {Text, View} from 'react-native'; -import {SharedElementMotion, spring} from 'navigation-react-native'; +import {SharedElementMotion} from 'navigation-react-native'; const getStyle = ({x, y, w, h, fontSize = 0, fontColor = 0}) => ({ - x: spring(x, {precision: 10}), - y: spring(y, {precision: 10}), - w: spring(w, {precision: 10}), - h: spring(h, {precision: 10}), - fontSize: spring(fontSize, {precision: 10}), - fontColor: spring(fontColor, {precision: 10}), + x, + y, + w, + h, + fontSize, + fontColor, }); export default ({stateNavigator}) => ( diff --git a/NavigationReactNative/src/SharedElementMotion.tsx b/NavigationReactNative/src/SharedElementMotion.tsx index c95feca27..7fb9e2305 100644 --- a/NavigationReactNative/src/SharedElementMotion.tsx +++ b/NavigationReactNative/src/SharedElementMotion.tsx @@ -1,6 +1,6 @@ import { StateNavigator } from 'navigation'; import * as React from 'react'; -import { Motion } from 'react-motion'; +import { Animate } from 'react-move'; import { Modal } from 'react-native'; import spring from './spring'; @@ -36,7 +36,7 @@ class SharedElementMotion extends React.Component { componentDidMount() { this.getStateNavigator().onNavigate(this.reset); this.animate(); - this.animateTimeout = setTimeout(() => cancelAnimationFrame(this.animateFrame), 500); + this.animateTimeout = setTimeout(() => cancelAnimationFrame(this.animateFrame), 150); } componentWillUnmount() { this.getStateNavigator().offNavigate(this.reset); @@ -64,7 +64,7 @@ class SharedElementMotion extends React.Component { if (this.state.url === this.getStateNavigator().stateContext.url) { this.setState(({force}) => ({sharedElements: [], animatedElements: {}, force: force + 1})); this.animate(); - this.animateTimeout = setTimeout(() => cancelAnimationFrame(this.animateFrame), 500); + this.animateTimeout = setTimeout(() => cancelAnimationFrame(this.animateFrame), 150); } } onAnimated(name, mounted) { @@ -81,11 +81,7 @@ class SharedElementMotion extends React.Component { getStyle(name, {measurements, data, style: defaultStyle}, strip = false) { if (strip && defaultStyle) return defaultStyle; - var style = this.props.elementStyle(name, {...measurements, ...data}); - var newStyle = {}; - for (var key in style) - newStyle[key] = !strip ? style[key] : style[key].val; - return newStyle; + return this.props.elementStyle(name, {...measurements, ...data}); } render() { var {url, sharedElements, animatedElements, force} = this.state; @@ -96,15 +92,17 @@ class SharedElementMotion extends React.Component { visible={sharedElements.length > Object.keys(animatedElements).length} supportedOrientations={['portrait', 'landscape', 'landscape-left', 'landscape-right']}> {sharedElements.map(({name, oldElement: old, mountedElement: mounted}) => ( - {this.onAnimated(name, mounted)}} - defaultStyle={{...this.getStyle(name, old, true), __force: 0}} - style={{...this.getStyle(name, mounted), __force: spring(force)}}> + {this.onAnimated(name, mounted)}}> {({__force, ...tweenStyle}) => { this.context.movingSharedElement(url, name, tweenStyle); return this.props.children(tweenStyle, name, old.data, mounted.data) }} - + ))} ); diff --git a/NavigationReactNative/src/node_modules/@types/react-move.d.ts b/NavigationReactNative/src/node_modules/@types/react-move.d.ts index e2a558e09..77cd89a21 100644 --- a/NavigationReactNative/src/node_modules/@types/react-move.d.ts +++ b/NavigationReactNative/src/node_modules/@types/react-move.d.ts @@ -15,4 +15,15 @@ declare module "react-move" { children?: (data: any) => ReactElement; } export class Transition extends Component { } + + interface AnimateProps { + data: Array; + default?: Array; + duration?: number; + easing?: string; + immutable?: boolean; + onRest: () => void; + children?: (data: any) => ReactElement; + } + export class Animate extends Component { } } From 0ba1da795c34c7f069db06c303dc36bc2f860ed8 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 25 Apr 2017 19:48:00 +0100 Subject: [PATCH 09/31] Without spring can return data directly --- NavigationReactNative/sample/zoom/ZoomShared.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/NavigationReactNative/sample/zoom/ZoomShared.js b/NavigationReactNative/sample/zoom/ZoomShared.js index f24d6eb8d..67d8d7f43 100644 --- a/NavigationReactNative/sample/zoom/ZoomShared.js +++ b/NavigationReactNative/sample/zoom/ZoomShared.js @@ -2,18 +2,9 @@ import React from 'react'; import {Text, View} from 'react-native'; import {SharedElementMotion} from 'navigation-react-native'; -const getStyle = ({x, y, w, h, fontSize = 0, fontColor = 0}) => ({ - x, - y, - w, - h, - fontSize, - fontColor, -}); - export default ({stateNavigator}) => ( getStyle(data)} + elementStyle={(name, data) => data} onAnimating={(name, oldRef, mountedRef, oldData, mountedData) => { mountedData.hide && mountedRef.setNativeProps({style:{opacity: 0}}) }} From 5a3049993a1997e7a1bd9240266d6086e9f43c11 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 25 Apr 2017 19:49:57 +0100 Subject: [PATCH 10/31] Replaced with official typings from react move Added missing onRest prop --- .../src/node_modules/@types/react-move.d.ts | 144 ++++++++++++++---- 1 file changed, 116 insertions(+), 28 deletions(-) diff --git a/NavigationReactNative/src/node_modules/@types/react-move.d.ts b/NavigationReactNative/src/node_modules/@types/react-move.d.ts index 77cd89a21..018f9b2bf 100644 --- a/NavigationReactNative/src/node_modules/@types/react-move.d.ts +++ b/NavigationReactNative/src/node_modules/@types/react-move.d.ts @@ -1,29 +1,117 @@ -/// - -declare module "react-move" { - import { Component, ReactElement } from 'react'; - - interface TransitionProps { - data: Array; - getKey?: (data: any, i: number) => string; - update: (data: any) => any; - enter?: (data: any) => any; - leave?: (data: any) => any; - duration?: number; - easing?: string; - onRest: () => void; - children?: (data: any) => ReactElement; - } - export class Transition extends Component { } - - interface AnimateProps { - data: Array; - default?: Array; - duration?: number; - easing?: string; - immutable?: boolean; - onRest: () => void; - children?: (data: any) => ReactElement; - } - export class Animate extends Component { } +import * as React from "react"; + +interface EasingFunction { + (progress: number): number; +} +export interface IAnimateProps { + /** + * An object of keys that that you wish to animate. + * When these values are changed, each and every value in this object will be + * interpolated (unless its key is found in the ignore prop) + */ + data: {}; + + /** + * An object of keys to be used as the initial state of the animation. + */ + default?: {}; + + /** + * The duration in milliseconds for each item to animate + * Default: 500 + */ + duration?: number; + + /** + * A string that references a d3-ease function, + * or a custom easing function that receives a progress decimal + * and returns a new progress decimal. + * Default: 'easeCubicOut' + */ + easing?: string | EasingFunction; + + /** + * Any keys found in this array will not be interpolated, + * and instead will be immediately set to the new value + * Default: false + */ + ignore?: Array; + + /** + * Avoid dropping frames at all cost by + * dynamically increasing the duration of the animation loop becomes overwhelmed. + * Default: false + */ + flexDuration?: boolean; + + /** + * By default, strict equality === between the old data and new data is used to detect when an animation should occur. + * If you wish, you can disable immutable mode which falls back to using JSON.stringify to determine if an animation should occur. + * Default: true + */ + immutable?: boolean; + onRest: () => void; +} +export declare class Animate extends React.Component { + static defaultProps: IAnimateProps; +} + +export interface ITransitionProps { + /** + * An array of objects you wish to track. + * These are not necessarily the exact values you wish to animate, + * but will used to produce the animated values. + */ + data: Array<{}>; + /** + * A function that returns a unique identifier for each item. + * This is used to track enter, update and leave states/groups. + */ + getKey?: (item: any, index: number | string) => number | string; + /** + * A function that returns the state for an item if it is neither entering or leaving the list of items. + */ + update: (item: any) => any; + /** + * A function that returns the state for an item if it is entering the list of items. + * If nothing is returned, the update state is used. + */ + enter?: (item: any) => any; + /** + * A function that returns the state for an item if it is leaving the list of items. + * If nothing is returned, the update state is used. + */ + leave?: (item: any) => any; + /** + * The duration in milliseconds for each item to animate + */ + duration?: number; + /** + * A string that references a d3-ease function, or a custom easing function + * that receives a progress decimal and returns a new progress decimal. + */ + easing?: string | EasingFunction; + /** + * Number of milliseconds for each item to wait relative to it's preceding item. + */ + stagger?: number; + /** + * Delays item animation relative to status groups instead of the entire list. + * The relative groups used in this mode are entering, updating and leaving. + */ + staggerGroups?: boolean; + /** + * Any keys found in this array will not be interpolated, + * and instead will be immediately set to the new value + */ + ignore?: Array; + /** + * Avoid dropping frames at all cost by dynamically + * increasing the duration of the animation loop becomes overwhelmed. + */ + flexDuration?: boolean; + onRest: () => void; } +export declare class Transition extends React.Component { + static defaultProps: ITransitionProps; +} \ No newline at end of file From 7c98fab9741ba7c1955c437ad30d5173a816152b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 26 Apr 2017 20:04:35 +0100 Subject: [PATCH 11/31] Used color interpolation from react move --- NavigationReactNative/sample/zoom/Detail.js | 2 +- NavigationReactNative/sample/zoom/Grid.js | 2 +- NavigationReactNative/sample/zoom/ZoomShared.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/sample/zoom/Detail.js b/NavigationReactNative/sample/zoom/Detail.js index 0db0780a4..238726442 100644 --- a/NavigationReactNative/sample/zoom/Detail.js +++ b/NavigationReactNative/sample/zoom/Detail.js @@ -29,7 +29,7 @@ export default ({color, stateNavigator}) => { {color} diff --git a/NavigationReactNative/sample/zoom/Grid.js b/NavigationReactNative/sample/zoom/Grid.js index 746f4f095..9b8cdd2fe 100644 --- a/NavigationReactNative/sample/zoom/Grid.js +++ b/NavigationReactNative/sample/zoom/Grid.js @@ -35,7 +35,7 @@ export default ({stateNavigator}) => { {color} diff --git a/NavigationReactNative/sample/zoom/ZoomShared.js b/NavigationReactNative/sample/zoom/ZoomShared.js index 67d8d7f43..7f317d886 100644 --- a/NavigationReactNative/sample/zoom/ZoomShared.js +++ b/NavigationReactNative/sample/zoom/ZoomShared.js @@ -30,7 +30,7 @@ export default ({stateNavigator}) => ( fontSize, textAlign: 'center', fontWeight: 'bold', - color: `rgb(${fontColor},${fontColor},${fontColor})`, + color: fontColor, zIndex: 1, }}> {color} From 5e5baae572b1ec81768248f84eed54f348dbba59 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 26 Apr 2017 20:13:26 +0100 Subject: [PATCH 12/31] Avoided over rendering with url check --- NavigationReactNative/sample/zoom/Detail.js | 75 ++++++++++-------- NavigationReactNative/sample/zoom/Grid.js | 88 ++++++++++++--------- 2 files changed, 92 insertions(+), 71 deletions(-) diff --git a/NavigationReactNative/sample/zoom/Detail.js b/NavigationReactNative/sample/zoom/Detail.js index 238726442..6b064bdc2 100644 --- a/NavigationReactNative/sample/zoom/Detail.js +++ b/NavigationReactNative/sample/zoom/Detail.js @@ -3,38 +3,49 @@ import {StyleSheet, Text, View, TouchableHighlight} from 'react-native'; import {NavigationBackAndroid, SharedElement} from 'navigation-react-native'; import ZoomShared from './ZoomShared'; -export default ({color, stateNavigator}) => { - const {url} = stateNavigator.stateContext; - return ( - - - - { - if (url === stateNavigator.stateContext.url) - stateNavigator.navigateBack(1); - }}> - X - - - - - - {color} - - - ); +export default class Detail extends React.Component { + constructor(props, context) { + super(props, context); + const {url, crumbs} = props.stateNavigator.stateContext; + this.url = url; + this.crumbs = crumbs; + } + shouldComponentUpdate(props) { + return this.url === props.stateNavigator.stateContext.url; + } + render() { + const {color, stateNavigator} = this.props; + return ( + + + + { + if (this.url === stateNavigator.stateContext.url) + stateNavigator.navigateBack(1); + }}> + X + + + + + + {color} + + + ); + } }; const styles = StyleSheet.create({ diff --git a/NavigationReactNative/sample/zoom/Grid.js b/NavigationReactNative/sample/zoom/Grid.js index 9b8cdd2fe..96b04c10b 100644 --- a/NavigationReactNative/sample/zoom/Grid.js +++ b/NavigationReactNative/sample/zoom/Grid.js @@ -8,45 +8,55 @@ const colors = [ 'purple', 'fuchsia', 'indigo', 'green', 'navy', 'blue', 'teal', 'black' ]; -export default ({stateNavigator}) => { - const {url} = stateNavigator.stateContext; - return ( - - - - - {colors.map(color => ( - - { - if (url === stateNavigator.stateContext.url) { - stateNavigator.navigate('detail', {color}); - } - }}> - - - {color} - - - - - ))} - - - - ); +export default class Grid extends React.Component { + constructor(props, context) { + super(props, context); + const {url, crumbs} = props.stateNavigator.stateContext; + this.url = url; + this.crumbs = crumbs; + } + shouldComponentUpdate(props) { + return this.url === props.stateNavigator.stateContext.url; + } + render() { + const {stateNavigator} = this.props; + return ( + + + + + {colors.map(color => ( + + { + if (this.url === stateNavigator.stateContext.url) + stateNavigator.navigate('detail', {color}); + }}> + + + {color} + + + + + ))} + + + + ); + } }; const styles = StyleSheet.create({ From c04574cff8977dc88953e0d3e67a29a4d8ab7453 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 26 Apr 2017 20:18:02 +0100 Subject: [PATCH 13/31] Removed spring now react motion is gone --- NavigationReactNative/src/NavigationReactNative.ts | 3 +-- NavigationReactNative/src/SharedElementMotion.tsx | 1 - NavigationReactNative/src/spring.ts | 6 ------ 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 NavigationReactNative/src/spring.ts diff --git a/NavigationReactNative/src/NavigationReactNative.ts b/NavigationReactNative/src/NavigationReactNative.ts index 572d70728..017776fbc 100644 --- a/NavigationReactNative/src/NavigationReactNative.ts +++ b/NavigationReactNative/src/NavigationReactNative.ts @@ -4,6 +4,5 @@ var NavigationBackAndroid = require('./NavigationBackAndroid').default; import NavigationMotion from './NavigationMotion'; import SharedElement from './SharedElement'; import SharedElementMotion from './SharedElementMotion'; -import spring from './spring'; -export { NavigationBackAndroid, NavigationMotion, SharedElement, SharedElementMotion, spring }; +export { NavigationBackAndroid, NavigationMotion, SharedElement, SharedElementMotion }; diff --git a/NavigationReactNative/src/SharedElementMotion.tsx b/NavigationReactNative/src/SharedElementMotion.tsx index 7fb9e2305..c6651dcc4 100644 --- a/NavigationReactNative/src/SharedElementMotion.tsx +++ b/NavigationReactNative/src/SharedElementMotion.tsx @@ -2,7 +2,6 @@ import { StateNavigator } from 'navigation'; import * as React from 'react'; import { Animate } from 'react-move'; import { Modal } from 'react-native'; -import spring from './spring'; class SharedElementMotion extends React.Component { private animateTimeout: number; diff --git a/NavigationReactNative/src/spring.ts b/NavigationReactNative/src/spring.ts deleted file mode 100644 index 3a1128a92..000000000 --- a/NavigationReactNative/src/spring.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { OpaqueConfig, spring, SpringHelperConfig } from 'react-motion'; - -export default (val: number, config?: SpringHelperConfig): OpaqueConfig => ( - spring(val, {precision: 0.1, ...config}) -); - From d566ae2e81320c27b39a02faa8b826cbc758c814 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 26 Apr 2017 20:18:33 +0100 Subject: [PATCH 14/31] Removed react motion typings --- .../src/node_modules/@types/react-motion.d.ts | 159 ------------------ 1 file changed, 159 deletions(-) delete mode 100644 NavigationReactNative/src/node_modules/@types/react-motion.d.ts diff --git a/NavigationReactNative/src/node_modules/@types/react-motion.d.ts b/NavigationReactNative/src/node_modules/@types/react-motion.d.ts deleted file mode 100644 index 468b2ac36..000000000 --- a/NavigationReactNative/src/node_modules/@types/react-motion.d.ts +++ /dev/null @@ -1,159 +0,0 @@ -// Type definitions for react-motion -// Project: https://github.com/chenglou/react-motion -// Definitions by: Stepan Mikhaylyuk , Alexey Svetliakov -// Definitions: https://github.com/borisyankov/DefinitelyTyped - -/// - -declare module "react-motion" { - import { Component, ReactElement } from 'react'; - - - // your typical style object given in props. Maps to a number or a spring config - export type Style = { [key: string]: number | OpaqueConfig }; - // the interpolating style object, with the same keys as the above Style object, - // with the values mapped to numbers, naturally - export type PlainStyle = { [key: string]: number }; - // internal velocity object. Similar to PlainStyle, but whose numbers represent - // speed. Might be exposed one day. - export type Velocity = { [key: string]: number }; - - /** - * Spring additional configuration - */ - interface SpringHelperConfig { - /** - * Specified stiffness - * @defaults 170 - */ - stiffness?: number; - /** - * Specifies damping - * @defaults 26 - */ - damping?: number; - /** - * Specifies both the rounding of the interpolated value and the speed (internal). - * @defaults 0.01 - */ - precision?: number; - } - - - export interface OpaqueConfig { - val: number; - stiffness: number; - damping: number; - precision: number; - } - - /** - * properties - */ - interface MotionProps { - /** - * The default style. Being ignored on subsequent renders - * @default Object with same keys as in style whose values are the initial numbers you're interpolating on - */ - defaultStyle?: PlainStyle; - /** - * Object that maps to either number or opaque config returned by spring(). - * Must keep same keys throughout component's existence - */ - style: Style; - /** - * Callback with your interpolated styles. Must return one react element to render - * @param interpolatedStyle - */ - children?: (interpolatedStyle: PlainStyle) => ReactElement; - /** - * The callback that fires when the animation comes to a rest. - */ - onRest?: () => void; - } - - export class Motion extends Component { } - - // === TransitionMotion === - interface TransitionStyle { - /** - * The ID that TransitionMotion uses to track which configuration is which across renders, even when things are reordered. - * Typically reused as the component key when you map over the interpolated styles. - */ - key: string; - /** - * Anything you'd like to carry along. Will be preserved on re-renders until key off - */ - data?: any; - /** - * Actual starting style configuration - */ - style: Style; // actual style you're passing - } - /** - * Default style for transition - */ - interface TransitionPlainStyle { - key: any; - data?: any; - // same as TransitionStyle, passed as argument to style/children function - style: PlainStyle; - } - type InterpolateFunction = (previousInterpolatedStyles?: Array) => Array; - /** - * Transition properties - */ - interface TransitionProps { - /** - * Default styles on first render - */ - defaultStyles?: Array; - /** - * Styles to interpolate. Accepts array of TransitionStyle objects or interpolated function similar as for - * - */ - styles: Array | InterpolateFunction; - children?: (interpolatedStyles: Array) => ReactElement; - /** - * Triggers when new elements appears - * @param styleThatEntered - */ - willEnter?: (styleThatEntered: TransitionStyle) => PlainStyle; - /** - * Triggers when new element disappears - * @param styleThatLeft - */ - willLeave?: (styleThatLeft: TransitionStyle) => Style | void; - didLeave?: (styleThatEntered: TransitionStyle) => void; - } - export class TransitionMotion extends Component { } - - - interface StaggeredMotionProps { - /** - * Default styles - */ - defaultStyles?: Array; - /** - * Styles to interpolate - * @param previousInterpolatedStyles The previously interpolating (array of) styles (undefined at first render, unless defaultStyles is provided). - */ - styles: (previousInterpolatedStyles?: Array) => Array