用expo / react-native记录按钮的样式

时间:2018-10-01 16:45:07

标签: react-native animation button record 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秒。所以我认为这是因为每次都需要重新进行所有数学运算...

但是我对Expo / React-native非常陌生,对于动画甚至还更多,所以我不知道如何优化它。或者,如果有更好的方法来做我想做的事。

编辑:我注意到当我在父组件中用setState隐藏按钮后试图显示按钮时,它的速度很慢。 因此,我找到了一种解决方法:我只是创建一个在recordButton内执行相同操作的方法。那样,重新渲染不会再做数学运算,它的性能更高。我认为有更好的方法可以做到,所以我仍然知道其他解决方案...

0 个答案:

没有答案