@digidem/wcmc-mapeo-mobile-intro
Version:
Intro screens for Mapeo Mobile for the WCMC ICCA registration app
269 lines (256 loc) • 8.46 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import { View, Text, StyleSheet, Platform } from 'react-native';
import { useValues, clamp, between } from 'react-native-redash'; // import LottieView from 'lottie-react-native'
import Animated, { Easing } from 'react-native-reanimated';
import { TouchableNativeFeedback, TouchableOpacity } from 'react-native-gesture-handler';
import getContrastRatio from 'get-contrast-ratio';
import UniversalImage from './UniversalImage';
// TypeScript woes - doesn't seem to understand this
const Touchable = Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity;
const {
interpolate,
Extrapolate,
useCode,
cond,
set,
or,
not,
onChange,
eq,
neq,
timing,
block,
startClock,
spring,
Clock,
Value
} = Animated;
/**
* This helper will transition a value *from* zero, but returns a value back to
* zero without any transition. Based on:
* https://github.com/wcandillon/react-native-redash/blob/1eb308e254e6ee0061887312f7f1b0669030bd83/src/Transitions.ts#L22-L55
*/
const withTimingTransitionRight = (value, timingConfig = {}) => {
const clock = new Clock();
const state = {
finished: new Value(0),
frameTime: new Value(0),
position: new Value(0),
time: new Value(0)
};
const config = {
toValue: new Value(0),
duration: 250,
easing: Easing.linear,
...timingConfig
};
return block([startClock(clock), cond(neq(config.toValue, value), [set(state.frameTime, 0), set(state.time, 0), set(state.finished, 0), set(config.toValue, value)]), cond(eq(value, 0), [set(state.position, value)], timing(clock, state, config)), state.position]);
};
/**
* This helper will spring transition a value *from* zero, but returns a value
* back to zero without any transition. Based on:
* https://github.com/wcandillon/react-native-redash/blob/1eb308e254e6ee0061887312f7f1b0669030bd83/src/Transitions.ts#L57-L90
*/
export const withSpringTransitionRight = (value, springConfig = {}) => {
const clock = new Clock();
const state = {
finished: new Value(0),
velocity: new Value(0),
position: new Value(0),
time: new Value(0)
};
const config = {
toValue: new Value(0),
damping: 15,
mass: 1,
stiffness: 150,
overshootClamping: false,
restSpeedThreshold: 1,
restDisplacementThreshold: 1,
...springConfig
};
return block([startClock(clock), set(config.toValue, value), cond(eq(value, 0), [set(state.position, value)], spring(clock, state, config)), state.position]);
};
/**
* Returns a boolean animated value which is true:
*
* - When the position approaches within 0.05 of index
* - Until position moved beyond 0.95 of index
*
* Used for showing animations when a view comes into focus, and removing the
* animation after it has moved out of focus.
*
* @param {Animated.Value} position Current position
* @param {number} index Integer current index
* @param {Object} options Options
* @param {number} options.maxPosition Set this to the max index value to stop animation on overScroll
* @param {number} options.buffer The buffer before animations are triggered between 0 - 1
*/
const useShowContent = (position, index, {
maxPosition = index,
buffer = 0.1
} = {}) => {
const [show, focussed, blurred, focussing, blurring, clampedPosition] = useValues([0, 0, 0, 0, 0, 0], []);
useCode(() => block([set(clampedPosition, clamp(position, 0, maxPosition)), set(focussed, between(clampedPosition, index - buffer, index + buffer)), set(blurred, not(between(clampedPosition, index - 1 + buffer, index + 1 - buffer))), // The screen is blurring when it is moving between focussed state and
// blurred state and focussing when moving from blurred to focussed
onChange(focussed, cond(eq(focussed, 0), set(blurring, 1), set(focussing, 0))), onChange(blurred, cond(eq(blurred, 0), set(focussing, 1), set(blurring, 0))), set(show, or(focussed, blurring))]), [buffer]);
return show;
}; // /**
// * Play a Lottie animation whenever Animated.Value `show` is true, and reset the
// * animation to the start when `show` is false
// */
// const Animation = ({ show, source, style }) => {
// const [animationProgress] = useValues([0], [])
// useCode(
// () => [
// // We want "in" animations to happen just after the screen comes into
// // focus, but we don't want to "out" animation to happen until the screen
// // is blurred
// set(
// animationProgress,
// withTimingTransitionRight(show, {
// duration: (source.op / source.fr) * 1000
// })
// )
// ],
// [show, source]
// )
// return (
// <LottieView source={source} progress={animationProgress} style={style} />
// )
// }
/**
* A View that will fade in and transition up when Animated.View `show` becomes
* true, and move down without transition when it is false
*/
const FadeUpView = ({
show,
children,
style,
...otherProps
}) => {
const [appearProgress, translateY, opacity] = useValues([0, 0, 0], []);
useCode(() => block([set(appearProgress, withTimingTransitionRight(show, {
duration: 150
})), set(opacity, interpolate(appearProgress, {
inputRange: [0.5, 1],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})), set(translateY, interpolate(appearProgress, {
inputRange: [0, 1],
outputRange: [75, 0]
}))]), []);
return /*#__PURE__*/React.createElement(Animated.View, _extends({
style: [style, {
opacity,
transform: [{
translateY
}]
}]
}, otherProps), children);
};
const IntroScreen = ({
position,
screen,
screenCount = 1,
index = 0,
onShowInfo = () => {}
}) => {
const show = useShowContent(position, index, {
maxPosition: screenCount
});
const textColor = getContrastRatio(screen.backgroundColor, 'white') <= 3 ? 'black' : 'white';
let asset = null;
if (screen.animation) {// asset = (
// <Animation show={show} source={screen.animation} style={styles.asset} />
// )
} else if (screen.image) {
asset = /*#__PURE__*/React.createElement(UniversalImage, {
source: screen.image
});
}
const {
moreLink,
moreText
} = screen;
return /*#__PURE__*/React.createElement(View, {
style: styles.root
}, /*#__PURE__*/React.createElement(View, {
style: styles.assetContainer
}, asset), /*#__PURE__*/React.createElement(FadeUpView, {
show: show,
style: styles.textContainer
}, screen.title ? /*#__PURE__*/React.createElement(Text, {
style: [styles.title, {
color: textColor
}]
}, screen.title) : null, screen.description ? /*#__PURE__*/React.createElement(Text, {
style: [styles.description, {
color: textColor
}],
textBreakStrategy: "balanced"
}, screen.description) : null, typeof moreLink === 'string' && typeof moreText === 'string' ? /*#__PURE__*/React.createElement(Touchable, {
style: styles.button,
onPress: () => {
onShowInfo({
title: screen.moreTitle || moreLink,
text: moreText
});
}
}, /*#__PURE__*/React.createElement(Text, {
style: styles.buttonText,
textBreakStrategy: "balanced"
}, screen.moreLink)) : /*#__PURE__*/React.createElement(View, {
style: styles.spacer
})));
};
export default /*#__PURE__*/React.memo(IntroScreen);
const styles = StyleSheet.create({
root: {
flex: 1
},
title: {
fontWeight: '700',
fontSize: 18,
textAlign: 'center',
marginBottom: 5
},
description: {
fontWeight: '400',
fontSize: 15,
textAlign: 'center',
lineHeight: 18 * 1.2
},
assetContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 30,
paddingVertical: 30
},
asset: {
width: '100%',
height: '100%',
flex: 1,
backgroundColor: 'yellow'
},
textContainer: {
paddingHorizontal: 16,
flex: 0
},
button: {
alignSelf: 'center',
padding: 10
},
buttonText: {
fontWeight: 'bold',
color: 'blue',
textAlign: 'center',
lineHeight: 20
},
spacer: {
height: 10
}
});
//# sourceMappingURL=IntroScreen.js.map