Временные ряды анимации React-native с процентным соотношением или альтернативой для него

Я пытаюсь перенести компонент реагирования на React-Native, используя Animated React-native.

Мне удалось создать круг, и его орбита работает хорошо.

но я не знаю, как написать эти правила в Animated React Native.

в @keyframes fulfilling-bouncing-circle-spinner-orbit-animation мне нужно дождаться половины анимации, затем я должен дважды уменьшить и увеличить ее

и в @keyframes fulfilling-bouncing-circle-spinner-circle-animation я не знаю, как добавлять и удалять стили, используя нативную анимацию реакции, все думал об этом, но не придумал.

Итак, есть ли способ добавить временные ряды и делать то, что я хочу, в этих кадрах так же, как работает @keyframes.

import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';

const BouncingCircle = styled.div`
  height: ${props => props.size}px;
  width: ${props => props.size}px;
  position: relative;
  animation: fulfilling-bouncing-circle-spinner-animation infinite
    ${props => props.animationDuration}ms ease;
  * {
    box-sizing: border-box;
  }
  .orbit {
    height: ${props => props.size}px;
    width: ${props => props.size}px;
    position: absolute;
    top: 0;
    left: 0;
    border-radius: 50%;
    border: calc(${props => props.size}px * 0.03) solid ${props => props.color};
    animation: fulfilling-bouncing-circle-spinner-orbit-animation infinite
      ${props => props.animationDuration}ms ease;
  }
  .circle {
    height: ${props => props.size}px;
    width: ${props => props.size}px;
    color: ${props => props.color};
    display: block;
    border-radius: 50%;
    position: relative;
    border: calc(${props => props.size}px * 0.1) solid ${props => props.color};
    animation: fulfilling-bouncing-circle-spinner-circle-animation infinite
      ${props => props.animationDuration}ms ease;
    transform: rotate(0deg) scale(1);
  }
  @keyframes fulfilling-bouncing-circle-spinner-animation {
    0% {
      transform: rotate(0deg);
    }
    100% {
      transform: rotate(360deg);
    }
  }
  @keyframes fulfilling-bouncing-circle-spinner-orbit-animation {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1);
    }
    62.5% {
      transform: scale(0.8);
    }
    75% {
      transform: scale(1);
    }
    87.5% {
      transform: scale(0.8);
    }
    100% {
      transform: scale(1);
    }
  }
  @keyframes fulfilling-bouncing-circle-spinner-circle-animation {
    0% {
      transform: scale(1);
      border-color: transparent;
      border-top-color: inherit;
    }
    16.7% {
      border-color: transparent;
      border-top-color: initial;
      border-right-color: initial;
    }
    33.4% {
      border-color: transparent;
      border-top-color: inherit;
      border-right-color: inherit;
      border-bottom-color: inherit;
    }
    50% {
      border-color: inherit;
      transform: scale(1);
    }
    62.5% {
      border-color: inherit;
      transform: scale(1.4);
    }
    75% {
      border-color: inherit;
      transform: scale(1);
      opacity: 1;
    }
    87.5% {
      border-color: inherit;
      transform: scale(1.4);
    }
    100% {
      border-color: transparent;
      border-top-color: inherit;
      transform: scale(1);
    }
  }
`;

const propTypes = {
  size: PropTypes.number,
  animationDuration: PropTypes.number,
  color: PropTypes.string,
  className: PropTypes.string,
  style: PropTypes.object,
};

const defaultProps = {
  size: 60,
  color: '#fff',
  animationDuration: 4000,
  className: '',
};

const FulfillingBouncingCircleSpinner = ({
  size,
  color,
  animationDuration,
  className,
  style,
  ...props
}) => (
  <BouncingCircle
    size={size}
    color={color}
    animationDuration={animationDuration}
    className={`fulfilling-bouncing-circle-spinner${
      className ? ' ' + className : ''
    }`}
    style={style}
    {...props}
  >
    <div className="circle" />
    <div className="orbit" />
  </BouncingCircle>
);

FulfillingBouncingCircleSpinner.propTypes = propTypes;
FulfillingBouncingCircleSpinner.defaultProps = defaultProps;

export default FulfillingBouncingCircleSpinner;
/** @flow **/
import React, { useEffect, useState } from 'react';
import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet';
import { Animated, Easing, StyleSheet } from 'react-native';
import { AnimationUtils } from '../animationUtils';

type EpicProps = {
  size?: number,
  duration?: number,
  color?: string,
  style?: ViewStyleProp
};

const EpicDefaultProps = {
  size: 200,
  color: 'red',
  duration: 1000
};

export const FulfillingBouncingCircleSpinner = ({ size, color, duration, style, ...props }: EpicProps) => {
  const [animate] = useState(new Animated.Value(0));
  const spinnerStyle = StyleSheet.create({
    container: {
      height: size,
      width: size,
      position: 'relative'
    },
    circle: {
      height: size,
      width: size,
      borderColor: color,
      borderRadius: size * 0.5,
      position: 'relative',
      borderWidth: size * 0.1
    },
    orbit: {
      height: size,
      width: size,
      position: 'absolute',
      top: 0,
      left: 0,
      borderColor: color,
      borderRadius: size * 0.5,
      borderWidth: size * 0.03
    }
  });

  const containerRotation = AnimationUtils.interpolation(animate, [0, 1], ['0deg', '360deg']);
  const circle = AnimationUtils.interpolation(animate, [0, 1], [1, 1.4]);
  const orbit = AnimationUtils.interpolation(animate, [0, 1], [1, 0.8]);

  useEffect(() => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(animate, { toValue: 1, duration: duration, useNativeDriver: true, easing: Easing.back() }),
        Animated.timing(animate, { toValue: 0, duration: duration, useNativeDriver: true, easing: Easing.back() })
      ])
    ).start();
  }, [animate, duration]);

  return (
    <Animated.View style={[style, spinnerStyle.container, { transform: [{ rotate: containerRotation }] }]} {...props}>
      <Animated.View style={[spinnerStyle.circle, { transform: [{ scaleX: circle }, { scaleY: circle }] }]} />
      <Animated.View style={[spinnerStyle.orbit, { transform: [{ scaleX: orbit }, { scaleY: orbit }] }]} />
    </Animated.View>
  );
};

FulfillingBouncingCircleSpinner.defaultProps = EpicDefaultProps;

person Snowflake    schedule 30.12.2019    source источник


Ответы (1)


Я потратил его когда-то на документацию по анимации React, и я придумал решение, если кто-то заинтересован или может улучшить его, я был бы очень признателен.

/** @flow **/
import React, { useEffect, useState } from 'react';
import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet';
import { Animated, Easing, StyleSheet } from 'react-native';

type EpicProps = {
  size?: number,
  duration?: number,
  color?: string,
  style?: ViewStyleProp
};

const EpicDefaultProps = {
  size: 200,
  color: 'red',
  duration: 1000
};

export const FulfillingBouncingCircleSpinner = ({ size, color, duration, style, ...props }: EpicProps) => {
  const [animate] = useState(new Animated.Value(0));
  const spinnerStyle = StyleSheet.create({
    container: {
      height: size,
      width: size,
      position: 'relative'
    },
    circle: {
      height: size,
      width: size,
      borderColor: color,
      borderRadius: size * 0.5,
      position: 'relative',
      borderWidth: size * 0.1
    },
    orbit: {
      height: size,
      width: size,
      borderColor: color,
      position: 'absolute',
      top: 0,
      left: 0,
      borderRadius: size * 0.5,
      borderWidth: size * 0.03
    }
  });
  const animateStyle = {
    container: {
      transform: [
        {
          rotate: animate.interpolate({
            inputRange: [0, 9, 10],
            outputRange: ['0deg', '360deg', '360deg']
          })
        }
      ]
    },
    orbit: {
      transform: [
        {
          scale: animate.interpolate({
            inputRange: [0, 6, 7, 8, 9, 10],
            outputRange: [1, 1, 0.8, 1, 0.8, 1]
          })
        }
      ]
    },
    circle: {
      transform: [
        {
          scale: animate.interpolate({
            inputRange: [0, 6, 7, 8, 9, 10],
            outputRange: [1, 1, 1.4, 1, 1.4, 1]
          })
        }
      ],
      borderColor: animate.interpolate({
        inputRange: [0, 4, 5, 9, 10],
        outputRange: ['transparent', 'transparent', color, color, 'transparent']
      }),
      borderTopColor: animate.interpolate({
        inputRange: [0, 10],
        outputRange: [color, color]
      }),
      borderRightColor: animate.interpolate({
        inputRange: [0, 1, 2, 9, 10],
        outputRange: ['transparent', 'transparent', color, color, 'transparent']
      }),
      borderBottomColor: animate.interpolate({
        inputRange: [0, 2, 3, 9, 10],
        outputRange: ['transparent', 'transparent', color, color, 'transparent']
      })
    }
  };

  useEffect(() => {
    Animated.loop(
      Animated.timing(animate, {
        toValue: 10,
        duration: duration * 4,
        easing: Easing.inOut(Easing.ease)
      })
    ).start();
  }, [animate, duration]);

  return (
    <Animated.View style={[style, spinnerStyle.container, animateStyle.container]} {...props}>
      <Animated.View style={[spinnerStyle.circle, animateStyle.circle]} />
      <Animated.View style={[spinnerStyle.orbit, animateStyle.orbit]} />
    </Animated.View>
  );
};

FulfillingBouncingCircleSpinner.defaultProps = EpicDefaultProps;

person Snowflake    schedule 31.12.2019