Skip to content

Instantly share code, notes, and snippets.

@birkir
Last active October 31, 2018 21:13
Show Gist options
  • Select an option

  • Save birkir/bbcd1ce584a568ec108db15b291a8710 to your computer and use it in GitHub Desktop.

Select an option

Save birkir/bbcd1ce584a568ec108db15b291a8710 to your computer and use it in GitHub Desktop.
SharedElementTransition for ios using react-native-navigation by wix
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { Dimensions, Animated, View, Text, findNodeHandle } from 'react-native';
import { Navigation } from 'react-native-navigation';
import Modal from './Modal';
const RCTUIManager = require('NativeModules').UIManager;
const data = {
translateX: new Animated.Value(0),
translateY: new Animated.Value(0),
scaleX: new Animated.Value(1),
scaleY: new Animated.Value(1),
cursor: new Animated.Value(0),
};
const duration = 3300;
const getLayout = (id, node) => new Promise((resolve, reject) => {
try {
const nodeHandle = findNodeHandle(node);
setTimeout(() => RCTUIManager.measureInWindow(nodeHandle, (x, y, width, height, pageX, pageY) => {
console.log('measure', id, x, y, width, height, pageX, pageY);
resolve({ x, y, width, height });
}));
} catch (err) {
console.log('Error measuring node %o', err);
resolve(null);
}
});
export default class SharedElementTransition extends PureComponent {
static propTypes = {
children: PropTypes.node,
sharedElementId: PropTypes.string,
showDuration: PropTypes.number,
hideDuration: PropTypes.number,
};
static defaultProps = {
showDuration: 600,
hideDuration: 400,
};
static canGoBack = false;
static async fromEvent(screen, e) {
if (e.id === 'willDisappear') {
data.fromScreen = screen;
const { width, height } = await getLayout('from', data.fromComponent);
// Set width and height
data.width = width;
data.height = height;
// Setup lightbox component screen
Navigation.registerComponent('MODAL_NAV_FROM', () => () =>
React.createElement(SharedElementTransition, { isMock: true }, React.cloneElement(data.fromComponent.props.children)));
// Display modal
data.fromScreen.props.navigator.showLightBox({
screen: 'MODAL_NAV_FROM',
style: {
animated: false,
},
passProps: {},
});
data.fromComponent.hide(data.fromLayout);
}
if (e.id === 'willAppear') {
if (SharedElementTransition.canGoBack) {
data.fromComponent.hide(data.fromLayout);
data.toComponent.hide(data.toLayout);
data.fromScreen.props.navigator.showLightBox({
screen: 'MODAL_NAV_FROM',
style: {
animated: false,
},
passProps: {},
});
Animated.parallel([
Animated.timing(data.translateX, { toValue: data.fromLayout.x, duration }),
Animated.timing(data.translateY, { toValue: data.fromLayout.y, duration }),
Animated.timing(data.scaleX, { toValue: 1, duration }),
Animated.timing(data.scaleY, { toValue: 1, duration }),
Animated.timing(data.cursor, { toValue: 1, duration }),
])
.start(() => {
data.fromComponent.show();
data.toComponent.show();
screen.props.navigator.dismissLightBox();
SharedElementTransition.canGoBack = false;
});
}
}
}
static async toEvent(screen, e) {
if (e.id === 'willAppear') {
data.toScreen = screen;
}
if (e.id === 'didAppear') {
screen.props.navigator.dismissLightBox();
}
}
static onBothReady() {
if (SharedElementTransition.canGoBack) {
return false;
}
if (data.fromLayout && !data.toLayout) {
data.translateX.setValue(data.fromLayout.x);
data.translateY.setValue(data.fromLayout.y);
}
if (!data.fromLayout || !data.toLayout) return;
data.toComponent.hide(data.toLayout);
const scaleX = data.toLayout.width / data.fromLayout.width;
const scaleY = data.toLayout.height / data.fromLayout.height;
const deltaX = (data.toLayout.width - data.fromLayout.width) / 2;
const deltaY = (data.toLayout.height - data.fromLayout.height) / 2;
const x = data.toLayout.x + deltaX;
const y = data.toLayout.y + deltaY;
Animated.parallel([
Animated.timing(data.translateX, { toValue: x, duration }),
Animated.timing(data.translateY, { toValue: y, duration }),
Animated.timing(data.scaleX, { toValue: scaleX, duration }),
Animated.timing(data.scaleY, { toValue: scaleY, duration }),
])
.start(() => {
data.fromComponent.show();
data.toComponent.show();
SharedElementTransition.canGoBack = true;
});
}
constructor(...args) {
super(...args);
if (!data.fromComponent) {
data.fromComponent = this;
this.isFrom = true;
} else if (this.props.isMock) {
data.mockComponent = this;
this.isMock = true;
} else {
data.toComponent = this;
this.isTo = true;
}
}
componentDidMount() {
if (this.isFrom) {
getLayout('didmount from', this)
.then(layout => {
data.fromLayout = layout;
SharedElementTransition.onBothReady();
});
}
if (this.isTo) {
getLayout('didmount to', this)
.then(layout => {
data.toLayout = layout;
SharedElementTransition.onBothReady();
});
}
}
state = {
hidden: false,
width: 0,
height: 0,
};
hide({ width, height }) {
this.setState({ hidden: true, width, height });
}
show() {
this.setState({ hidden: false });
}
render() {
const { isMock, children } = this.props;
if (isMock) {
const { width, height, translateX, translateY, scaleX, scaleY } = data;
const transform = [
{ translateX }, { translateY },
{ scaleX }, { scaleY }
];
return (
<Animated.View style={{ width, height, transform }}>
{React.cloneElement(children, { cursor: data.cursor })}
</Animated.View>
);
}
if (this.state.hidden) {
const { width, height } = this.state;
return <View style={{ width, height }} />;
}
return React.cloneElement(children, { cursor: data.cursor });
}
}
@alejoasotelo

Copy link
Copy Markdown

do you have any example how to use?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment