Evgenii Klepilin
Evgenii Klepilin

Reputation: 695

Circling motion animation in React Native

I need to create an animation with an image that will circle around another image. I have already tried to use suggestions from a similar question like Animate a Circle around another circle , but unfortunately it didn't help. I tried looking into 3rd party modules that would offer the desirable functionality, but haven't found something that would fit my need.

I found a helpful article to understand the circular motion in JavaScript, however I have a hard time to replicate it in React Native animation. I believe I simply have a hard time understanding a proper usage of Animated API and transform style properties when it comes to animating circular movement.


<View style={animationContainer}>
    <Image
      source={require('./images/image.png')}
      style={image}
    />
    <Animated.Image
      source={require('./images/icon.png')}
      style={circlingIcon}
    />
</View>

Upvotes: 4

Views: 9944

Answers (2)

Aswin C
Aswin C

Reputation: 1222

I have posted a similar butter smooth solution for the question react native circle transform translate animation

Full code:


import React, {Component} from 'react';
import {View, Text, Animated, StyleSheet, Easing} from 'react-native';

export default class Circle extends Component {
    constructor() {
        super();
        this.animated = new Animated.Value(0);
        var inputRange = [0, 1];
        var outputRange = ['0deg', '360deg'];
        this.rotate = this.animated.interpolate({inputRange, outputRange});
        outputRange = ['0deg', '-360deg'];
        this.rotateOpposit = this.animated.interpolate({inputRange, outputRange});
    }

    componentDidMount() {
        this.animate();
    }

    animate() {
      Animated.loop(
        Animated.timing(this.animated, {
            toValue: 1,
            duration: 4000,
            useNativeDriver: true,
            easing: Easing.linear,
        }),
      ).start();
    }
    render() {
        const transform = [{rotate: this.rotate}];
        const transform1 = [{rotate: this.rotateOpposit}];
        return (
          <View style={styles.container}>
            <Animated.View style={[styles.item, {transform}]}>
              <Animated.View style={[styles.topItem, {transform: transform1}]}>
                <Text style={styles.text}>Test</Text>
              </Animated.View>
            </Animated.View>
          </View>
        );
    }
 }
 const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    item: {
        position: 'absolute',
        width: 100,
        height: 200, // this is the diameter of circle
    },
    topItem: {
        width: '100%',
        height: 20,
        backgroundColor: 'red',
        position: 'absolute',
        alignItems: 'center',
        justifyContent: 'center',
    },
    text: {
        color: '#fff',
    },
 });

I hope it will help you..

Upvotes: 2

Evgenii Klepilin
Evgenii Klepilin

Reputation: 695

I have taken a solution from a question react native circle transform translate animation and refactored it a little by separating interpolation of coordinates over Y and X in two different functions.

interface InterpolatedMotion {
  translateX: Animated.Value;
  translateY: Animated.Value;
}

interface AnimatedIcon extends InterpolatedMotion {
  animated: Animated.Value;
}

interface State {
  icon: AnimatedIcon;
}

const defaultAnimatedIcon = (animatedValue: number): AnimatedIcon => ({
  animated: new Animated.Value(animatedValue),
  translateX: new Animated.Value(0),
  translateY: new Animated.Value(0),
});

export class Animation extends PureComponent<Props, State> {
  state = {
    icon: defaultAnimatedIcon(0),
  }

  constructor(props) {
    super(props);
    let { icon } = this.state;

    icon.animated.setValue(0);

    const snapshot = 50;
    const radius = 200;

    const inOutX = this.interpolateCircularMotionOverX(snapshot, radius);

    icon.translateX = coins.animated.interpolate(inOutX);

    const inOutY = this.interpolateCircularMotionOverY(snapshot, radius);

    icon.translateY = coins.animated.interpolate(inOutY);
  }

  componentWillMount(): void {
    this.startAnimation();
  }

  startAnimation = (): void => {
    let { icon } = this.state;

    icon.animated.setValue(0);

    let animations = [
      Animated.timing(
        icon.animated,
        {
          toValue: 1,
          duration: 8000,
          easing: Easing.linear,
        },
      ),
    ];

    Animated.loop(
      Animated.parallel(animations),
    ).start(() => {
      icon.animated.setValue(0);
    });
  }

  interpolateCircularMotionOverX = (snapshot: number, radius: number) => {
    const inputRange = [];
    const outputRange = [];
    for (let i = 0; i <= snapshot * 2; ++i) {
      const value = i / snapshot;
      const move = Math.sin(value * Math.PI * 2) * radius;
      inputRange.push(value);
      outputRange.push(move);
    }
    return { inputRange, outputRange };
  }

  interpolateCircularMotionOverY = (snapshot: number, radius: number) => {
    const inputRange = [];
    const outputRange = [];
    for (let i = 0; i <= snapshot * 2; ++i) {
      const value = i / snapshot;
      const move = -Math.cos(value * Math.PI * 2) * radius;
      inputRange.push(value);
      outputRange.push(move);
    }
    return { inputRange, outputRange };
  }

  render(): JSX.Element {
    const { icon } = this.state;

    const transformIcon = [
      { translateY: icon.translateY },
      { translateX: icon.translateX },
    ];

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
          <View style={{ flex: 1 }}>
            <Animated.Image
              source={require('./images/coins.png')}
              style={[Styles.forms.circlingIcon, { transform: transformCoins }]}
            />
          </View>
        </View>
    );
}

This has helped me by providing a flexible and reusable solution for any future instances of circular animation.

Upvotes: 1

Related Questions