0

我正在尝试使用 react-native 的 Animated 将 react 组件移植到 React-Native

我设法创建了圆圈,它的轨道和工作良好。

但我不知道如何在 react-native 的 Animated 中编写这些规则

@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;
4

1 回答 1

0

我花了一些时间在 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;

于 2019-12-31T17:59:52.880 回答