File size: 4,535 Bytes
ff5e06a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
const {useEffect, useRef} = React;

function ArrowFlow({direction = 'rtl', reverseFlow = false}) {
  const stemPathRef = useRef(null);
  const circlePathRef = useRef(null);
  const arrowRef = useRef(null);
  const circleRefs = useRef([]);
  circleRefs.current = [];

  const circleRadii = [4, 14, 7, 16, 9];

  const stemPaths = {
    'rtl': 'M 250,75 L 125,75 A 50 50 0 0 0 75,125 L 75,132',
    'ltr': 'M 0,75 L 125,75 A 50 50 0 0 1 175,125 L 175,132',
  };
  const circlePaths = {
    'rtl': 'M 300,75 L 125,75 A 50 50 0 0 0 75,125 L 75,182',
    'ltr': 'M -50,75 L 125,75 A 50 50 0 0 1 175,125 L 175,182',
  };

  const stemPathD = stemPaths[direction];
  const circlePathD = circlePaths[direction];

  const addToCircleRefs = el => {
    if (el && !circleRefs.current.includes(el)) {
      circleRefs.current.push(el);
    }
  };

  useEffect(() => {
    gsap.registerPlugin(MotionPathPlugin);
    gsap.set(arrowRef.current, {opacity: 0});
    gsap.set(circleRefs.current, {opacity: 0});

    const len = stemPathRef.current.getTotalLength();
    let pathTween, arrowTween, circlesTl;

    if (reverseFlow) {
      gsap.set(
          stemPathRef.current, {strokeDasharray: len, strokeDashoffset: -len});
      pathTween = gsap.to(
          stemPathRef.current,
          {strokeDashoffset: 0, duration: 1.6, delay: 0, ease: 'power1.inOut'});
    } else {
      gsap.set(
          stemPathRef.current, {strokeDasharray: len, strokeDashoffset: len});
      pathTween = gsap.to(
          stemPathRef.current,
          {strokeDashoffset: 0, duration: 1.6, delay: 0, ease: 'power1.inOut'});
    }

    const motionPathConfig = {
      path: stemPathRef.current,
      align: stemPathRef.current,
      alignOrigin: [0.5, 0.5],
      autoRotate: true,
      start: reverseFlow ? 1 : 0,
      end: reverseFlow ? 0 : 1,
    };

    arrowTween = gsap.to(arrowRef.current, {
      opacity: 1,
      duration: 1.6,
      delay: 0,
      motionPath: motionPathConfig,
      ease: 'power1.inOut',
    });

    const circleMotionPathConfig = {
      path: circlePathRef.current,
      align: circlePathRef.current,
      alignOrigin: [0.5, 0.5],
      start: reverseFlow ? 1 : 0,
      end: reverseFlow ? 0 : 1,
    };

    circlesTl = gsap.timeline({delay: 0});
    circleRefs.current.forEach((circle, index) => {
      circlesTl.fromTo(
          circle, {opacity: 0}, {opacity: 1, duration: 0.2},
          index * 0.3);  // stagger start time
      circlesTl.to(
          circle, {
            motionPath: circleMotionPathConfig,
            duration: 1.6,
            ease: 'power1.inOut',
          },
          index * 0.3);  // same start time as opacity fade in
      circlesTl.to(
          circle, {opacity: 0, duration: 0.2},
          index * 0.3 + 1.4);  // fade out before end
    });
    circlesTl.to(arrowRef.current, {opacity: 0, duration: 0.2}, 2.8);
    circlesTl.to(stemPathRef.current, {opacity: 0, duration: 0.2}, 2.8);

    return () => {
      pathTween.kill();
      arrowTween.kill();
      circlesTl.kill();
    }
  }, [direction, reverseFlow]);

    return (
        <div>
            <svg width='250px' height='150px' viewBox='0 0 250 150' version='1.1' style={{
    overflow: 'visible' }}>
                <defs>
                    <filter id='glow' x='-100%' y='-100%' width='300%' height='300%'>
                        <feGaussianBlur in='SourceAlpha' stdDeviation='3' result='blur'/>
                        <feFlood floodColor='#4285F4' floodOpacity='1' result='color'/>
                        <feComposite in='color' in2='blur' operator='in' result='glow'/>
                        <feMerge>
                            <feMergeNode in='glow'/>
                            <feMergeNode in='SourceGraphic'/>
                        </feMerge>
                    </filter>
                </defs>
                <g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
                    <path ref={stemPathRef} id="Path-1" className="path" fill="none" stroke="#484135" strokeWidth="8" strokeLinejoin="round" strokeMiterlimit="10" d={stemPathD} />
                    <path ref={circlePathRef} id='Path-Circles' stroke='none' d={
    circlePathD} />
                    {[...Array(5)].map((_, i) => (
                        <circle key={i} ref={addToCircleRefs} r={circleRadii[i]} fill="#4285F4" filter="url(#glow)" />
                    ))
}
<polyline ref = {arrowRef} id = 'arrow' points = '0,-9 18,0 0,9 5,0' fill =
     '#484135' /></g>
            </svg>< /div>
    );
}