/* eslint-disable no-return-assign */ import React, { Component } from 'react'; import _get from 'lodash/get'; import { View, Animated, StyleSheet, Image, Easing, StyleProp } from 'react-native'; import { getOptionsByType, getCurtainGestureByType, createCurtainPanResponder, parseImageSource, } from './utils'; import { IGesture, IProps, IState, ControlType } from './index.type'; import StatusDisplay from './status-display'; import ArrowGroup from './arrow-group'; export default class CurtainGesture extends Component { static defaultProps = { curtainType: 'trietex', styleType: 'zoosemy', min: 0, max: 100, value: 0, }; constructor(props) { super(props); const { value } = props; this.state = { curtainCeilAnimate: new Animated.Value(value), circleAnimate: new Animated.Value(value), }; this.moveVal = value; this.newValue = value; this.statusDisplayRef = null; this._curDeltaX = this.calcDeltaX(props.value, this._curDeltaX); this._cirDeltaX = this.calcDeltaX(props.value, this._cirDeltaX); } componentDidMount() { this.state.curtainCeilAnimate.addListener(({ value }) => { this.statusDisplayRef && this.statusDisplayRef.setAccVal(value); }); } onValueChangeFromProps = value => { this.startAnimation(value); this.startCircleAnimation(value); this.statusDisplayRef && this.statusDisplayRef.setAccVal(value); }; onMovingToTargetValue = value => { this._curDeltaX = this.calcDeltaX(value, this._curDeltaX); this._cirDeltaX = this.calcDeltaX(value, this._cirDeltaX); this.moveVal = value; this.startAnimation(value); this.startCircleAnimation(value); }; onControlTypeChange = (control: ControlType) => { const { min, max } = this.props; switch (control) { case 'open': case 'close': this.startAnimation(control === 'close' ? max : min); this.startCircleAnimation(control === 'close' ? max : min); this.statusDisplayRef && this.statusDisplayRef.setGestureStatus(false); break; default: this.state.curtainCeilAnimate.stopAnimation(); this.state.circleAnimate.stopAnimation(); this.statusDisplayRef && this.statusDisplayRef.setMovingStatus('stop'); break; } }; get Gesture(): IGesture { const { curtainType } = this.props; const [curtainCeil, circle] = getCurtainGestureByType(curtainType); const Gesture = {}; curtainCeil.forEach((curtainGesture, index) => { const isLeft = index === 0; Gesture[curtainGesture] = createCurtainPanResponder({ grant: () => {}, move: (e, gesture) => this.handleResponderMove(e, gesture, 'ceil', isLeft), release: (e, gesture) => this.handleResponderRelease(e, gesture, 'ceil', isLeft), }); }); circle.forEach((circleGesture, index) => { const isLeft = index === 0; Gesture[circleGesture] = createCurtainPanResponder({ grant: () => {}, move: (e, gesture) => this.handleResponderMove(e, gesture, 'circle', isLeft), release: (e, gesture) => this.handleResponderRelease(e, gesture, 'circle', isLeft), }); }); return Gesture; } get options() { const { curtainType, styleType, min, max, staticPrefix } = this.props; return getOptionsByType(curtainType, styleType, { min, max, staticPrefix }); } getValueDiffToAbsPercent = (val, diff): number => Math.abs(val - diff) / 100; getDurationPercent = (v, next) => { const { totalTime, min, max } = this.props; const durationTime = totalTime || 5000; const percent = Math.abs(v - next) / (max - min); if (percent === 0) return durationTime; return percent * durationTime; }; moveVal: number; _curDeltaX?: number; _cirDeltaX?: number; newValue: number; statusDisplayRef: any; calcDeltaX = (value: number, defaultValue: number) => { const { min, max } = this.props; if (value < min || value > max) { return defaultValue; } const deltaX = (value / (max - min)) * (this.options.curtainWidth / 2); return deltaX; }; handleResponderMove = (__, gestureState, type = 'circle', isLeft) => { const { disabled, curtainType } = this.props; const { dx, dy } = gestureState; const val = curtainType === 'roller' ? dy : dx; if (disabled || val === 0) { return; } if (type === 'ceil') { this._moveTo(val, isLeft ? 0 : 1); } else { this._circleMoveTo(val, isLeft ? 0 : 1, 'move'); } }; handleResponderRelease = (__, gestureState, type, isLeft) => { const { disabled, curtainType } = this.props; const { dx, dy } = gestureState; const val = curtainType === 'roller' ? dy : dx; if (disabled || val === 0) { return; } if (type === 'ceil') { this._moveTo(val, isLeft ? 0 : 1, 'release'); } else { this._circleMoveTo(val, isLeft ? 0 : 1, 'release'); } this._cirDeltaX = this.calcDeltaX(this.newValue, this._cirDeltaX); }; _moveTo = (dx, type, action?) => { const { min, max, onSlideTo } = this.props; const RAIL_WIDTH = this.options.curtainWidth; let value = type ? ((this._curDeltaX - dx) / (RAIL_WIDTH / 2)) * (max - min) : ((this._curDeltaX + dx) / (RAIL_WIDTH / 2)) * (max - min); if (value < min) { value = min; } else if (value > max) { value = max; } this.moveVal = Math.round(value); this.newValue = value; this.startAnimation(value); this.startCircleAnimation(value); action === 'release' && onSlideTo && onSlideTo(value); }; _circleMoveTo(dx, type, action) { const { min, max, onSlideTo } = this.props; const RAIL_WIDTH = this.options.curtainWidth; let value = type ? ((this._cirDeltaX - dx) / (RAIL_WIDTH / 2)) * (max - min) : ((this._cirDeltaX + dx) / (RAIL_WIDTH / 2)) * (max - min); if (value < min) { value = min; } else if (value > max) { value = max; } this.moveVal = Math.round(value); this.newValue = value; if (value) { this.state.circleAnimate.setValue(value); } switch (action) { case 'move': this.statusDisplayRef && this.statusDisplayRef.setGestureTargetVal(value); break; case 'release': this.startAnimation(value); onSlideTo && onSlideTo(value); break; default: break; } } startAnimation = v => { const accAnimateVal = _get(this.state.curtainCeilAnimate, '_value'); const curtainDuration = this.getDurationPercent(v, accAnimateVal); this.statusDisplayRef && this.statusDisplayRef.setMovingStatus(v > accAnimateVal ? 'closing' : 'opening'); Animated.timing(this.state.curtainCeilAnimate, { toValue: v, duration: curtainDuration, easing: Easing.inOut(Easing.ease), }).start(({ finished }) => { finished && this.statusDisplayRef && this.statusDisplayRef.setMovingStatus('stop'); }); }; startCircleAnimation = v => { const circleDuration = this.getDurationPercent(v, _get(this.state.circleAnimate, '_value')); Animated.timing(this.state.circleAnimate, { toValue: v, duration: circleDuration, easing: Easing.inOut(Easing.ease), }).start(() => { this.moveVal = Math.round(_get(this.state.circleAnimate, '_value')); }); }; renderCurtainView = () => { const { curtainType, styleType, curtainBgColor, curtainCeilImage, curtainHeight, curtainWidth, } = this.props; const { curtainImg, curtain, curtainInterpolate, getCurtainInterpolate, curtainWrap, } = this.options; const curtainSizeType = curtainType === 'roller' ? 'height' : 'width'; const CurtainElement = styleType === 'flat' ? Animated.View : Animated.Image; const curtainCeilOption = styleType === 'flat' ? {} : { source: parseImageSource(curtainCeilImage || curtainImg), }; const size = { height: curtainHeight, width: curtainWidth, }; const usePropsSize = curtainHeight && curtainWidth; return ( {curtainType === 'trietex' && ( )} ); }; renderCircleView = () => { const { curtainType, buttonColor } = this.props; const { circle, curtainWidth, circleLeftInterpolate, circleRightInterpolate, circleType, shouldCircleFlip, } = this.options; return ( {circleType === 'arrow' && } {curtainType === 'trietex' && circleRightInterpolate && ( {circleType === 'arrow' && } )} ); }; render() { const { min, max, styleType, style, curtainBgColor, curtainBgImage, curtainBgHeight, curtainBgWidth, textColor, openingText, closingText, } = this.props; const { container, bgImg, bg, topContainerStyle } = this.options; return ( (this.statusDisplayRef = ref)} min={min} max={max} value={this.props.value} textColor={textColor} openingText={openingText} closingText={closingText} /> {styleType === 'flat' && ( )} {styleType !== 'flat' && ( )} {this.renderCurtainView()} {this.renderCircleView()} ); } } const styles = StyleSheet.create>({ container: { alignItems: 'center', justifyContent: 'center', }, });