import React, { useState } from 'react';
import { StyleSheet, View, Animated, Text, Easing } from 'react-native';
import Svg, { Circle, G } from 'react-native-svg';
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
export default ({
  image,
  width,
  height,
  innerCircle = false,
  innerCircleRadius = 13,
  innerCircleFillPercentage = 25,
  innerCircleStroke = '#143c5b',
  innerCircleStrokeAnimated = '#02ac8a',
  outerCircle = false,
  outerCircleRadius = 18,
  outerCircleFillPercentage = 50,
  outerCircleStroke = '#1f4a42',
  outerCircleStrokeAnimated = '#028cfe',
  degree,
}) => {
  const innerCirclePerimeter = 2 * Math.PI * innerCircleRadius;
  const innerCircleStrokeDashOffset =
    innerCirclePerimeter -
    (innerCirclePerimeter * innerCircleFillPercentage) / 100;
  const outerCirclePerimeter = 2 * Math.PI * outerCircleRadius;
  const outerCircleStrokeDashOffset =
    outerCirclePerimeter -
    (outerCirclePerimeter * outerCircleFillPercentage) / 100;
  const [springValue] = useState(new Animated.Value(1.3));
  const [innerCircleInitialFill] = useState(
    new Animated.Value(innerCirclePerimeter)
  const [outerCircleInitialFill] = useState(
    new Animated.Value(outerCirclePerimeter)
  React.useEffect(() => {
    Animated.parallel([
      Animated.timing(innerCircleInitialFill, {
        toValue: innerCircleStrokeDashOffset,
        duration: 1000,
      Animated.timing(outerCircleInitialFill, {
        toValue: outerCircleStrokeDashOffset,
        duration: 2000,
      Animated.spring(springValue, {
        toValue: 1,
        friction: 1,
    ]).start();
    innerCircleInitialFill,
    outerCircleInitialFill,
    springValue,
    innerCircleStrokeDashOffset,
    outerCircleStrokeDashOffset,
  const outer = () => {
    return (
      outerCircle && (
          <Circle
            cx="25"
            cy="25"
            r={outerCircleRadius}
            fill="transparent"
            stroke={outerCircleStroke}
            strokeDasharray="10, 1"
            strokeDashoffset="30"
            strokeWidth={0.5}
          <AnimatedCircle
            cx="25"
            cy="25"
            r={outerCircleRadius}
            fill="transparent"
            stroke={innerCircleStrokeAnimated}
            strokeDasharray={outerCirclePerimeter}
            strokeDashoffset={outerCircleInitialFill}
            strokeLinecap={'round'}
  const inner = () => {
    return (
      innerCircle && (
          <Circle
            cx="25"
            cy="25"
            r={innerCircleRadius}
            fill="transparent"
            stroke={innerCircleStroke}
            strokeDasharray="1"
            strokeWidth={0.5}
          <AnimatedCircle
            cx="25"
            cy="25"
            r={innerCircleRadius}
            fill="transparent"
            stroke={outerCircleStrokeAnimated}
            strokeDasharray={innerCirclePerimeter}
            strokeDashoffset={innerCircleInitialFill}
            strokeLinecap={'round'}
  const Image = () => (
      style={{
        position: 'absolute',
        justifyContent: 'center',
        alignItems: 'center',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
      <Animated.Image
        source={image}
        style={[
          styles.image,
            width,
            height,
            borderRadius: width * 0.5,
            transform: [{ scale: springValue }],
    </View>
  //console.log(degree, height * Math.cos(degree), width * Math.sin(degree));
  const grado = React.useRef(new Animated.Value(0)).current;
  React.useEffect(() => {
    /*Animated.loop(
      Animated.timing(grado, {
        toValue: 1,
        duration: 300,
        easing: Easing.linear,
    ).start();*/
    Animated.loop(
      Animated.spring(grado, {
        toValue: 1,
        friction: 1,
      { iterations: 1000 }
    ).start();
  }, []);
  const spinT = grado.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 360],
  console.log('c', grado, spinT);
  const spin = 45;
  return (
      style={[
        styles.container,
          //width: width * 1.5,
          //height: height * 1.5,
          //borderRadius: 2 * Math.PI * outerCircleRadius,
          backgroundColor: '#cccccca1',
          viewBox={`0 0 50 50`}
          width={width * 2.5}
          height={height * 2.5}
          style={{
            transform: [{ rotateZ: '-90deg' }],
            {outer()}
            {inner()}
        {Image()}
          style={{
            fontSize: width * 0.12,
            fontWeight: 'bold',
            color: '#ffffff',
            position: 'absolute',
            justifyContent: 'center',
            alignItems: 'center',
            textAlign: 'center',
            display: 'flex',
              height -
              height * Math.cos((spinT * Math.PI) / 180) +
              (height * 0.2) / 2,
            left:
              width +
              width * Math.sin((spinT * Math.PI) / 180) +
              (width * 0.2) / 2,
            backgroundColor: '#1393DB',
            borderRadius: 10,
            width: width * 0.2,
            height: height * 0.2,
            shadowColor: '#000',
            shadowOffset: {
              width: 0,
              height: 2,
            shadowOpacity: 0.25,
            shadowRadius: 3.84,
            elevation: 5,
        </Text>
      </View>
    </View>
const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
    //backgroundColor: 'rgba(168, 152, 50, 0.5)',
    
javascript
android
ios
react-native
animation
Paul
Paul
发布于 2020-05-03
1 个回答
Jonathan Derewith Canevese
Jonathan Derewith Canevese
发布于 2020-11-19
已采纳
0 人赞同

只要在并行函数之前添加循环即可。

Animated.loop(
      Animated.parallel([
        Animated.timing(innerCircleInitialFill, {
          toValue: innerCircleStrokeDashOffset,
          duration: 1000,
        Animated.timing(outerCircleInitialFill, {
          toValue: outerCircleStrokeDashOffset,
          duration: 2000,
        Animated.spring(springValue, {
          toValue: 1,
          friction: 1,