【问题标题】:ReactJS: AnimationValue.interpolate for sin and cosReactJS:用于 sin 和 cos 的 AnimationValue.interpolate
【发布时间】:2016-09-04 18:32:14
【问题描述】:

我正在尝试在 ReactJS 本机中为组件设置动画,以便它以圆形方式围绕一个点连续运行。基本上,我需要一个不断增加的计时器,我可以使用它来设置 X、Y 坐标,例如 {x: r * Math.sin(timer), y: r * Math.cos(timer)}

我发现本指南包含一些关于插值的非常有用的信息,但我仍然没有成功:

http://browniefed.com/react-native-animation-book/INTERPOLATION.html

如何像这样更新组件的位置?

额外问题:如何使动画连续?

【问题讨论】:

  • 你已经尝试了什么?请发布代码以便我们为您提供帮助。
  • 检查我的答案。这对我来说不是一个理想的解决方案,所以如果您有任何建议,请告诉我。

标签: reactjs react-native react-animated


【解决方案1】:

由于 react-native 插值不支持函数回调,所以我做了一个辅助函数:

function withFunction(callback) {
    let inputRange = [], outputRange = [], steps = 50;
    /// input range 0-1
    for (let i=0; i<=steps; ++i) {
        let key = i/steps;
        inputRange.push(key);
        outputRange.push(callback(key));
    }
    return { inputRange, outputRange };
}

将动画应用于 translateX 和 translateY:

transform: [
{
   translateX:
      this.rotate.interpolate(
         withFunction( (value) => Math.cos(Math.PI*value*2) * radius )
      )
},
{
   translateY:
      this.rotate.interpolate(
         withFunction( (value) => Math.sin(Math.PI*value*2) * radius )
      )
},
]

视觉效果:

为了使动画连续,我做了这样的事情:

rotate = new Animated.Value(0);

componentDidMount() {
    setInterval( () => {
        let step = 0.05;
        let final = this.rotate._value+step;
        if (final >= 1) final = 0;
        this.rotate.setValue(final);
    }, 50);
}

【讨论】:

    【解决方案2】:

    我最终找到了一种方法来做到这一点,尽管它并不理想,我很感激任何其他意见。这是我的代码:

    class MyComponent extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                rotation: new Animated.Value(0),
                pos: new Animated.ValueXY()
            }
        }
    
        componentDidMount() {
            this.animate(10000);
        }
    
        animate(speed) {
            Animated.parallel([
                Animated.timing(this.state.rotation, {
                    toValue: 360,
                    duration: speed,
                    easing: Easing.linear
                }),
                Animated.timing(this.state.pos, {
                    toValue: {x: 360, y: 360},
                    duration: speed,
                    easing: Easing.linear,
                    extrapolate: 'clamp'
                })
            ]).start(() => {
                // Cycle the animation
                this.state.rotation.setValue(0);
                this.state.pos.setValue({x: 0, y: 0});
                this.animate(speed);
            });
        }
    
        render() {
            let range = new Array(360);
            for (let i = 0; i < 360; i++) {
                range[i] = i;
            }
    
            var radius = 51,
                offset = 40;
    
            return (
                <View style={this.props.style}>
                    <Animated.View style={{position: 'absolute', transform: [
                        // Map the inputs [0..360) to x, y values
                        {translateX: this.state.pos.x.interpolate({
                            inputRange: range,
                            outputRange: range.map(i => offset + radius * Math.cos(i * Math.PI / 180))
                        })},
                        {translateY: this.state.pos.y.interpolate({
                            inputRange: range,
                            outputRange: range.map(i => offset - radius * Math.sin(i * Math.PI / 180))
                        })},
                        {rotate: this.state.rotation.interpolate({
                            inputRange: [0, 360],
                            outputRange: ['360deg', '0deg']
                        })}
                    ]}}>
                        <Image source={require('image.png')}/>
                    </Animated.View>
                </View>
            )
        }
    }
    

    基本上,我使用 interpolate 函数将 Animated.Value pos 映射到我的函数的所有 360 度结果。我认为这可以通过使用一种映射函数而不是插值函数来改进。有谁知道有没有?

    【讨论】: