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>
);
}
|