Запись в стиле Snapchat с помощью кнопки expo / реагировать - PullRequest
0 голосов
/ 01 октября 2018

Я пытаюсь создать компонент кнопки записи для приложения камеры expo.Я не извлек свое приложение, и я хотел бы сохранить его, если это возможно.

Итак, я сделал работающий компонент:

import React from 'react';
import {TouchableWithoutFeedback, Animated, StyleSheet, View} from 'react-native';
import { Svg } from 'expo';

const AnimatedPath = Animated.createAnimatedComponent(Svg.Path);
const PRESSIN_DELAY = 200

export default class RecordButton extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            progress: new Animated.Value(0),
        }
        this.stopping = false
    }

    componentWillMount(){

        let R = this.props.radius;
        let strokeWidth = 7
        let dRange = [];
        let iRange = [];
        let steps = 359;

        for (var i = 0; i<steps; i++){
            dRange.push(this.describeArc(R+strokeWidth+2/2, R+strokeWidth+2/2, R, 0, i));
            iRange.push(i/(steps-1));
        }


        var _d = this.state.progress.interpolate({
            inputRange: iRange,
            outputRange: dRange
        })

        this.animationData = {
            R: R,
            strokeWidth: strokeWidth,
            _d: _d,
        }
    }

    describeArc = (x, y, radius, startAngle, endAngle)=>{

        var start = this.polarToCartesian(x, y, radius, endAngle);
        var end = this.polarToCartesian(x, y, radius, startAngle);

        var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

        var d = [
            "M", start.x, start.y,
            "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
        ].join(" ");

        return d;
    }

    polarToCartesian(centerX, centerY, radius, angleInDegrees) {
        var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

        return {
            x: centerX + (radius * Math.cos(angleInRadians)),
            y: centerY + (radius * Math.sin(angleInRadians))
        };
    }

    startAnimation = async ()=>{

        this.Animation = Animated.timing(this.state.progress,{
            toValue:1,
            duration:this.props.duration,

        })

        this.props.onPressIn().then(res => {
            if(res){
                this.Animation.start(()=>{this.stopAnimation(false)})
            }
        })

    }


    stopAnimation = (abort=true) => {
        if(!this.stopping){
            this.stopping = true
            abort && this.Animation.stop()
            Animated.timing(this.state.progress,{
                toValue: 0,
                duration:0,
            }).start()
            this.props.onPressOut()
            this.stopping = false
        }
    }


    _HandlePressIn = async ()=>{
        this.timerTimeStamp = Date.now()
        this.timer = setTimeout(()=>{
            this.startAnimation()
        }, PRESSIN_DELAY)
    }


    _HandlePressOut = async ()=>{
        let timeStamp = Date.now()
        if(timeStamp - this.timerTimeStamp <= PRESSIN_DELAY){
            clearTimeout(this.timer)
            this.props.onJustPress()
        }else{
            this.stopAnimation()
        }
    }


    render() {
        const { R, _d, strokeWidth } = this.animationData

        return (
            <View style={this.props.style}>
                <TouchableWithoutFeedback style={styles.touchableStyle} onPress={this.props.onPress} onPressIn={this._HandlePressIn} onPressOut={this._HandlePressOut}>
                    <Svg 
                        style={styles.svgStyle}
                        height={R*2+13}
                        width={R*2+13}
                    >
                        <Svg.Circle
                            cx={R+strokeWidth+2/2}
                            cy={R+strokeWidth+2/2}
                            r={R}
                            stroke="grey"
                            strokeWidth={strokeWidth+1}
                            fill="none"
                        />
                        <Svg.Circle
                            cx={R+strokeWidth+2/2}
                            cy={R+strokeWidth+2/2}
                            r={R}
                            stroke="white"
                            strokeWidth={strokeWidth}
                            fill="none"
                        />
                        <AnimatedPath
                            d={_d}
                            stroke="red" 
                            strokeWidth={strokeWidth} 
                            fill="none"
                        />

                    </Svg>
                </TouchableWithoutFeedback>
            </View>
        );
    }
    }

    const styles = StyleSheet.create({
        svgStyle:{
            flex:1,
        },
        touchableStyle: {
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center'
        }
    });

Так что все почти нормально (недействительно производительный, но пока это нормально)

Проблема в том, что родительское представление запускает повторный рендеринг только с setState для примера.Это действительно много времени, как 3-5 секунд.Так что я думаю, это потому, что каждый раз, когда нужно заново делать всю математику ...

Но я довольно новичок в экспо / реагировать на нативную и даже больше в анимации, поэтому я не знаю, какоптимизировать это.Или, если есть лучший способ сделать то, что я хочу.

Редактировать: я заметил, что это было медленно, когда я пытался показать кнопку после того, как скрыл ее с помощью setState в родительском компоненте.Итак, я нашел обходной путь: я просто создал метод, который делает то же самое, но внутри recordButton.Таким образом, повторный рендеринг больше не выполняет математику, он более производительный.Я думаю, что есть лучший способ сделать это, поэтому я все еще знаю о других решениях ...

...