UNPKG

wobbly

Version:

〰️ parallax all the things in react-vr

215 lines (178 loc) 6.92 kB
/* */ import React, { Component } from 'react'; import { Animated } from 'react-vr'; import { unwrapArray, callAll, noop } from './utils'; const MOVE_INPUT_RANGE = [0, 1]; const negativeValue = new Animated.Value(-1); const positiveValue = new Animated.Value(1); /** * # Wobbly * <h1 align="center"> * 〰️ wobbly * </br> * <img src="https://user-images.githubusercontent.com/1127238/38072922-8250c22a-32dd-11e8-8259-fb8ea3346dfc.png" alt="wobbly logo" title="wobbly logo" width="100"> * </h1> * <p align="center">parallax all the things in react-vr</p> * <hr /> * </br> * wobbly manages the state needed to calculate `x, y` rotations for a parallax effect, allowing you to focus the UI, and apply the effect how/where you want. */ class Wobbly extends Component { /** * @type {object} * * @typedef {object} Props * * @property {number} [parallaxDegreeLowerBound=-15] - lower rotation degree bound * @property {number} [parallaxDegreeUpperBound=15] - upper rotation degree bound * @property {number} [slop=0.1] - slop to add to wrapper via prop getter * @property {number} [initialX=0] - initial x value in view, between 0 and 1 * @property {number} [initialY=0] - initial y value in view, between 0 and 1 * @property {number} [onExitSpringFriction=4] - controls "bounciness"/overshoot of the onExit animation * @property {number} [onExitSpringTension=40] - controls speed of onExit animation * @property {boolean} [flipX=false] - flip the sign on the x rotation transform style value * @property {boolean} [flipY=false] - flip the sign on the y rotation transform style value * @property {boolean} [moveOnLatchOnly=false] - only map onMove events to state when "latched" * @property {function} [children] - Is called with the StateAndHelpers of wobbly. * @property {function} [render] - Is called with the StateAndHelpers of wobbly. * @see {@link https://facebook.github.io/react-vr/docs/view.html#hitslop|hitSlop react-vr docs} */ static defaultProps = { parallaxDegreeLowerBound: -15, parallaxDegreeUpperBound: 15, slop: 0.1, initialX: 0.5, initialY: 0.5, onExitSpringFriction: 4, onExitSpringTension: 40, flipX: false, flipY: false, moveOnLatchOnly: false, }; /** * @type {object} * @private * @property {Object} rotation - state - The current x,y state * @property {boolean} latched - state - Whether onMoved events will be mapped to rotation state */ state = { rotation: { x: new Animated.Value(this.props.initialX), y: new Animated.Value(this.props.initialY), }, latched: this.props.moveOnLatchOnly ? false : true, }; interpolateMoveOffset = (value ) => value.interpolate({ inputRange: MOVE_INPUT_RANGE, outputRange: [ this.props.parallaxDegreeLowerBound, this.props.parallaxDegreeUpperBound, ], }); handleExit = () => { Animated.spring(this.state.rotation.x, { toValue: this.props.initialX, friction: this.props.onExitSpringFriction, tension: this.props.onExitSpringTension, }).start(); Animated.spring(this.state.rotation.y, { toValue: this.props.initialY, friction: this.props.onExitSpringFriction, tension: this.props.onExitSpringTension, }).start(); this.toggleLatch(); }; handleEnter = () => { if (!this.props.moveOnLatchOnly) { setTimeout(() => this.setState(() => ({ latched: true })), 30); } }; /** * The state of wobbly and prop getters are exposed as a parameter to the render prop. * * @typedef {object} StateAndHelpers * * @property {function} getMoveTargetProps - prop getter - returns the props to spread into the element which controls the parallax effect by moving over it. * @property {function} getWobblyTransformStyle - prop getter - returns the x,y state in a format the transform style property will take. Spread this into the style.transform array on an "Animated" element to which a parallax effect should be added. NOTE: This element must be "Animated" like "Animated.VrButton". * @property {number} x - state - x state value * @property {number} y - state - y state value * @property {number} latched - state - the latched state * @property {function} toggleLatch - action - function that toggles the latched date */ getMoveTargetProps = ( props = { onMove: () => {}, onExit: () => {}, onEnter: () => {}, } ) => ({ ...props, onMove: callAll( props.onMove, !this.state.latched ? noop : Animated.event([ { nativeEvent: { offset: [this.state.rotation.y, this.state.rotation.x], }, }, ]) ), onEnter: callAll(props.onEnter, this.handleEnter), onExit: callAll(props.onExit, this.handleExit), hitSlop: { top: this.props.slop, bottom: this.props.slop, left: this.props.slop, right: this.props.slop, }, }); getWobblyTransformStyle = () => [ { rotateX: Animated.multiply( this.props.flipX ? negativeValue : positiveValue, this.interpolateMoveOffset(this.state.rotation.x) ), }, { rotateY: Animated.multiply( this.props.flipY ? negativeValue : positiveValue, this.interpolateMoveOffset(this.state.rotation.y) ), }, ]; toggleLatch = () => this.setState(({ latched }) => ({ latched: !latched })); /** * Returns state and helpers for render callback. * @private * * @return {StateAndHelpers} * The state and helper functions exposed as a parameter to the render callback */ getStateAndHelpers() { return { // Prop Getters getMoveTargetProps: this.getMoveTargetProps, getWobblyTransformStyle: this.getWobblyTransformStyle, // State x: this.state.rotation.x, y: this.state.rotation.y, latched: this.state.latched, // Action toggleLatch: this.toggleLatch, }; } render() { const children = unwrapArray( this.props.render || this.props.children || noop ); const element = unwrapArray(children(this.getStateAndHelpers())); if (!element) { return null; } return element; } } export default Wobbly;