|
6 | 6 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
7 | 7 | // ----- Import the required modules
|
8 | 8 |
|
| 9 | +import { useEffect, useRef } from "react"; |
9 | 10 | import { Container, Row, Col } from "react-bootstrap";
|
10 | 11 | import GitHubCalendar from "react-github-calendar";
|
11 |
| - |
| 12 | +import { useReward } from "react-rewards"; |
12 | 13 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
13 |
| -// -- Import Icons/Images |
| 14 | +// -- Import Icons/Images/Sounds |
14 | 15 |
|
15 | 16 | import { SiTypescript, SiJavascript, SiPython, SiReact, SiPowershell, SiAzurefunctions, SiMicrosoftazure, SiGithub, SiElectron, SiRedis, SiMicrosoftsqlserver } from "react-icons/si";
|
16 | 17 | import { FaNodeJs } from "react-icons/fa";
|
17 | 18 | import { DiGit } from "react-icons/di";
|
18 | 19 | import mojLogo from "../../assets/images/moj.jpeg";
|
19 |
| - |
| 20 | +import quackSound from "../../assets/sounds/quack.mp3"; |
20 | 21 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
21 | 22 | // -- Define the skills to display
|
22 | 23 |
|
@@ -65,9 +66,82 @@ function GitHubSection() {
|
65 | 66 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
66 | 67 | // ----- Define the MyWork component
|
67 | 68 |
|
68 |
| -function index() { |
| 69 | +function MyWork() { |
| 70 | + const duckRef = useRef<HTMLDivElement>(null); |
| 71 | + const sectionRef = useRef<HTMLElement>(null); |
| 72 | + |
| 73 | + // Duck confetti reward |
| 74 | + const duckConfettiConfig = { |
| 75 | + emoji: ["🦆", "🐤", "🐥"], |
| 76 | + elementCount: 60, |
| 77 | + spread: 150, |
| 78 | + zIndex: 9999, |
| 79 | + lifetime: 300, |
| 80 | + position: "absolute", |
| 81 | + }; |
| 82 | + |
| 83 | + // Make 🦆 the dominant emoji |
| 84 | + for (let i = 0; i < 25; i++) { |
| 85 | + duckConfettiConfig.emoji.push("🦆"); |
| 86 | + } |
| 87 | + const { reward: duckConfetti, isAnimating: ducksInFlight } = useReward("duckConfetti", "emoji", duckConfettiConfig); |
| 88 | + |
| 89 | + const handleDuckMouseOver = () => { |
| 90 | + if (!ducksInFlight) { |
| 91 | + duckConfetti(); // Launch the ducks! |
| 92 | + } |
| 93 | + const audio = new Audio(quackSound); |
| 94 | + audio.play().catch((error) => { |
| 95 | + console.warn("Duck can't quack:", error); |
| 96 | + }); |
| 97 | + }; |
| 98 | + const handleDuckClick = () => { |
| 99 | + window.open("https://rubberduckdebugging.com", "_blank"); |
| 100 | + }; |
| 101 | + |
| 102 | + // Some overkill code to position a div overlay ontop of the rubber ducky in the background image |
| 103 | + useEffect(() => { |
| 104 | + const duckOriginalPosition = { x: 3114, y: 2180 }; // Coordinates of the duck in the original image |
| 105 | + const backgroundOriginalSize = { width: 4032, height: 3024 }; // Size of the original image |
| 106 | + |
| 107 | + // Calculate the position of the overlay and update it |
| 108 | + const positionOverlay = () => { |
| 109 | + if (!sectionRef.current || !duckRef.current) return; |
| 110 | + |
| 111 | + const containerRect = sectionRef.current.getBoundingClientRect(); |
| 112 | + const containerAspectRatio = containerRect.width / containerRect.height; |
| 113 | + const imageAspectRatio = backgroundOriginalSize.width / backgroundOriginalSize.height; |
| 114 | + |
| 115 | + let scaleFactor; |
| 116 | + let offsetX = 0; |
| 117 | + let offsetY = 0; |
| 118 | + |
| 119 | + if (containerAspectRatio > imageAspectRatio) { |
| 120 | + scaleFactor = containerRect.width / backgroundOriginalSize.width; |
| 121 | + offsetY = (containerRect.height - backgroundOriginalSize.height * scaleFactor) / 2; |
| 122 | + } else { |
| 123 | + scaleFactor = containerRect.height / backgroundOriginalSize.height; |
| 124 | + offsetX = (containerRect.width - backgroundOriginalSize.width * scaleFactor) / 2; |
| 125 | + } |
| 126 | + |
| 127 | + const newX = duckOriginalPosition.x * scaleFactor + offsetX; |
| 128 | + const newY = duckOriginalPosition.y * scaleFactor + offsetY; |
| 129 | + |
| 130 | + duckRef.current.style.left = `${newX}px`; |
| 131 | + duckRef.current.style.top = `${newY}px`; |
| 132 | + }; |
| 133 | + |
| 134 | + window.addEventListener("resize", positionOverlay); |
| 135 | + positionOverlay(); // Initial call |
| 136 | + |
| 137 | + return () => { |
| 138 | + window.removeEventListener("resize", positionOverlay); |
| 139 | + }; |
| 140 | + }, []); |
| 141 | + |
69 | 142 | return (
|
70 |
| - <section id="mywork" className="mywork-section"> |
| 143 | + <section id="mywork" className="mywork-section" ref={sectionRef}> |
| 144 | + <div id="duckConfetti" className="interactive-duck" ref={duckRef} onMouseOver={handleDuckMouseOver} onClick={handleDuckClick}></div> |
71 | 145 | <Container className="mywork-description">
|
72 | 146 | <h1 className="main-heading">
|
73 | 147 | My <strong className="primary-color">Work</strong>
|
@@ -98,4 +172,4 @@ function index() {
|
98 | 172 | );
|
99 | 173 | }
|
100 | 174 |
|
101 |
| -export default index; |
| 175 | +export default MyWork; |
0 commit comments