我正在尝试为博览会相机应用程序创建一个录制按钮组件。 我没有弹出我的应用程序,并且希望尽可能保持这种状态。
所以我做了一个工作正常的组件:
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内执行相同操作的方法。那样,重新渲染不会再做数学运算,它的性能更高。我认为有更好的方法可以做到,所以我仍然知道其他解决方案...