Newer
Older
My-Portfolio / frontend / src / components / design / FloatingSVG.jsx
  1. import React, { useEffect, useRef, useState } from "react";
  2. import useIsMobile from "../../constants/useIsMobile"; // Ensure this hook is correctly implemented
  3. const easeInOutSine = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
  4. const FloatingSVG = ({
  5. svg,
  6. position,
  7. speed = 0.5,
  8. amplitude = 20,
  9. rotationRange = 10,
  10. resetTrigger, // Prop to trigger reset
  11. }) => {
  12. const svgRef = useRef(null);
  13. const animationRef = useRef(null);
  14. const startTimeRef = useRef(null);
  15. const [isExploding, setIsExploding] = useState(true);
  16. const isMobile = useIsMobile(); // Detect if the screen is mobile
  17. // Reset the explosion animation when resetTrigger changes
  18. useEffect(() => {
  19. if (!resetTrigger) {
  20. // Section is not visible, reset to center
  21. const svgElement = svgRef.current;
  22. const centerX = window.innerWidth / 2 - (isMobile ? 7.5 : 15); // Adjust center based on size
  23. const centerY = window.innerHeight / 2 - (isMobile ? 7.5 : 15); // Adjust center based on size
  24. svgElement.style.transition =
  25. "left 1s ease-out, top 1s ease-out, opacity 1s ease-out";
  26. svgElement.style.left = `${centerX}px`;
  27. svgElement.style.top = `${centerY}px`;
  28. svgElement.style.opacity = "0"; // Fade out
  29. setIsExploding(true); // Prepare for next explosion
  30. } else {
  31. // Section is visible, start explosion animation
  32. setIsExploding(true);
  33. }
  34. }, [resetTrigger, isMobile]);
  35. // Explosion and floating animation
  36. useEffect(() => {
  37. const svgElement = svgRef.current;
  38. if (isExploding && resetTrigger) {
  39. // Set initial position to the center of the screen
  40. const centerX = window.innerWidth / 2 - (isMobile ? 7.5 : 15); // Adjust center based on size
  41. const centerY = window.innerHeight / 2 - (isMobile ? 7.5 : 15); // Adjust center based on size
  42. svgElement.style.left = `${centerX}px`;
  43. svgElement.style.top = `${centerY}px`;
  44. // Move to the target position with a smooth transition
  45. svgElement.style.transition =
  46. "left 1s ease-out, top 1s ease-out, opacity 1s ease-out";
  47. svgElement.style.opacity = "0"; // Start invisible
  48. setTimeout(() => {
  49. svgElement.style.left = position.x;
  50. svgElement.style.top = position.y;
  51. svgElement.style.opacity = "1";
  52. setIsExploding(false);
  53. }, Math.random() * 500); // Random delay between 0 and 500ms
  54. }
  55. // Floating animation
  56. const animate = (timestamp) => {
  57. if (!startTimeRef.current) startTimeRef.current = timestamp;
  58. const elapsed = (timestamp - startTimeRef.current) / 1000; // Convert to seconds
  59. // Simulate floating with multiple sine waves
  60. const offsetY =
  61. Math.sin(elapsed * speed * 1.8) * amplitude * 0.6 + // Primary wave
  62. Math.sin(elapsed * speed * 1.2) * amplitude * 0.4; // Secondary wave
  63. const offsetX =
  64. Math.cos(elapsed * speed * 1.3) * amplitude * 0.3 + // Horizontal drift
  65. Math.sin(elapsed * speed * 0.8) * amplitude * 0.2; // Subtle side drift
  66. // Eased Y motion for more natural feel
  67. const t = (Math.sin(elapsed * speed) + 1) / 2; // Normalize to 0-1
  68. const easedOffsetY = easeInOutSine(t) * amplitude * 2 - amplitude;
  69. // Add smooth rotation
  70. const rotation = Math.sin(elapsed * speed * 0.6) * rotationRange;
  71. // Apply transformation
  72. svgElement.style.transform = `translate(${offsetX}px, ${easedOffsetY}px) rotate(${rotation}deg)`;
  73. animationRef.current = requestAnimationFrame(animate);
  74. };
  75. // Start floating animation after explosion is complete
  76. if (!isExploding && resetTrigger) {
  77. animationRef.current = requestAnimationFrame(animate);
  78. }
  79. return () => cancelAnimationFrame(animationRef.current);
  80. }, [
  81. position,
  82. speed,
  83. amplitude,
  84. rotationRange,
  85. isExploding,
  86. resetTrigger,
  87. isMobile,
  88. ]);
  89. // Set size based on screen type (mobile or desktop)
  90. const size = isMobile ? "20px" : "30px";
  91. return (
  92. <div
  93. ref={svgRef}
  94. style={{
  95. position: "absolute",
  96. width: size,
  97. height: size,
  98. zIndex: 0,
  99. }}
  100. >
  101. <img
  102. src={svg}
  103. alt="floating-svg"
  104. style={{ width: "100%", height: "100%" }}
  105. />
  106. </div>
  107. );
  108. };
  109. export default FloatingSVG;