new and improved easter eggs (with sound effects!)
This commit is contained in:
parent
74ed7bd50a
commit
daa50887d6
BIN
public/earthquake.mp3
Normal file
BIN
public/earthquake.mp3
Normal file
Binary file not shown.
@ -248,7 +248,7 @@ const ContactUs = () => {
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<span className="sr-only">Instagram</span>
|
||||
<Image height={200} width={200} alt="Logo" className="z-10" src="/insta.webp" />
|
||||
<Image height={200} width={200} alt="Logo" className="z-10" src="/instagram.png" />
|
||||
</a>
|
||||
{/* Facebook: Pulsating Map */}
|
||||
<a
|
||||
|
||||
@ -38,7 +38,7 @@ body {
|
||||
@apply text-xs text-neutral-600 !important;
|
||||
}
|
||||
|
||||
.mapboxgl-popup-content p + p {
|
||||
.mapboxgl-popup-content p+p {
|
||||
@apply text-neutral-500 !important;
|
||||
}
|
||||
|
||||
@ -228,3 +228,106 @@ body {
|
||||
opacity: 0;
|
||||
transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.45, 1), opacity 0.6s;
|
||||
}
|
||||
|
||||
/* --- X Logo Crack-and-Spin Easter Egg --- */
|
||||
.x-logo.cracked {
|
||||
/* Overlay cracks with a clip-path or filters for effect (simple grayscale/contrast for demo) */
|
||||
filter: grayscale(1) brightness(0.6) contrast(2);
|
||||
/* Slight shake + scale for realism */
|
||||
transform: scale(1.04);
|
||||
transition: filter 0.2s, transform 0.2s;
|
||||
}
|
||||
|
||||
.x-logo.spin {
|
||||
animation: xspin180 0.8s cubic-bezier(0.77, 0, 0.18, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes xspin180 {
|
||||
0% {
|
||||
transform: rotate(0deg) scale(1.04);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: rotate(200deg) scale(1.04);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(180deg) scale(1.04);
|
||||
}
|
||||
}
|
||||
|
||||
.x-logo.cracked {
|
||||
filter: grayscale(1) brightness(0.7) contrast(2.6);
|
||||
transition: filter 0.2s, transform 0.2s;
|
||||
}
|
||||
|
||||
.x-logo.spin {
|
||||
animation: xspin180 0.8s cubic-bezier(0.77, 0, 0.18, 1) forwards;
|
||||
}
|
||||
|
||||
.x-logo.nospin {
|
||||
transform: rotate(0deg) scale(1.0);
|
||||
filter: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.x-logo-cracks {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.x-logo-cracks img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0.55;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.footer-x-logo-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* === X Logo Full Page Crack-and-Spin Easter Egg === */
|
||||
.body-cracked .crack-overlay,
|
||||
.body-cracked .crack1,
|
||||
.body-cracked .crack2 {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.body-cracked {
|
||||
/* Animate the whole page spin with a transform on html/body! */
|
||||
animation: bodyspin180 0.8s cubic-bezier(0.77, 0, 0.18, 1) forwards;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.body-spin-back {
|
||||
/* Immediately resets the transform, no animation needed */
|
||||
animation: none !important;
|
||||
transform: rotate(0deg) !important;
|
||||
}
|
||||
|
||||
@keyframes bodyspin180 {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: rotate(200deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
@ -2,29 +2,37 @@ import React, { useCallback, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function BottomFooter() {
|
||||
// ig easter egg
|
||||
// Instagram Lava Flood Easter Egg
|
||||
const [lavaActive, setLavaActive] = useState(false);
|
||||
const lavaTimeout = useRef<any>(null);
|
||||
|
||||
// LinkedIn easter egg
|
||||
// Instagram sound
|
||||
const earthquakeAudio = useRef<HTMLAudioElement | null>(null);
|
||||
|
||||
// LinkedIn Shake Easter Egg
|
||||
const [shaking, setShaking] = useState(false);
|
||||
const shakeTimeout = useRef<any>(null);
|
||||
|
||||
// x easter egg
|
||||
// X Logo Full-Page Crack & Spin Easter Egg
|
||||
const [showCracks, setShowCracks] = useState(false);
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const crackTimeout = useRef<any>(null);
|
||||
const [crackOverlayActive, setCrackOverlayActive] = useState(false);
|
||||
|
||||
// Lava flood handler (top-down flood)
|
||||
// Lava flood & sound handler
|
||||
const handleInstagramClick = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
setLavaActive(true);
|
||||
// Play earthquake sound
|
||||
if (earthquakeAudio.current) {
|
||||
earthquakeAudio.current.currentTime = 0;
|
||||
earthquakeAudio.current.play();
|
||||
}
|
||||
clearTimeout(lavaTimeout.current);
|
||||
lavaTimeout.current = setTimeout(() => setLavaActive(false), 2000);
|
||||
}, []);
|
||||
|
||||
// LinkedIn shake handler
|
||||
const handleLinkedInClick = useCallback((e: React.MouseEvent) => {
|
||||
const handleLinkedInClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (shaking) return; // prevent stacking
|
||||
setShaking(true);
|
||||
@ -36,45 +44,58 @@ export default function BottomFooter() {
|
||||
setShaking(false);
|
||||
body.classList.remove("shake-screen");
|
||||
}, 1000);
|
||||
}, [shaking]);
|
||||
},
|
||||
[shaking]
|
||||
);
|
||||
|
||||
// X (crack and collapse) handler
|
||||
const handleXClick = useCallback((e: React.MouseEvent) => {
|
||||
// X click = crack, spin page, then reset after 2s
|
||||
const handleXClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (crackOverlayActive) return;
|
||||
setShowCracks(true);
|
||||
crackTimeout.current = setTimeout(() => {
|
||||
setCollapse(true);
|
||||
setCrackOverlayActive(true);
|
||||
document.body.classList.remove("body-spin-back");
|
||||
document.body.classList.add("body-cracked");
|
||||
setTimeout(() => {
|
||||
document.body.classList.remove("body-cracked");
|
||||
document.body.classList.add("body-spin-back");
|
||||
setTimeout(() => {
|
||||
setShowCracks(false);
|
||||
setCollapse(false);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}, []);
|
||||
setCrackOverlayActive(false);
|
||||
document.body.classList.remove("body-spin-back");
|
||||
}, 200);
|
||||
}, 2000);
|
||||
},
|
||||
[crackOverlayActive]
|
||||
);
|
||||
|
||||
// Clean up classes and timeouts on unmount
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
clearTimeout(lavaTimeout.current);
|
||||
clearTimeout(shakeTimeout.current);
|
||||
clearTimeout(crackTimeout.current);
|
||||
document.body.classList.remove("shake-screen");
|
||||
document.body.classList.remove("body-cracked");
|
||||
document.body.classList.remove("body-spin-back");
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hidden audio element */}
|
||||
<audio ref={earthquakeAudio} src="/earthquake.mp3" preload="auto" />
|
||||
{/* Lava Flood Overlay */}
|
||||
{lavaActive && (
|
||||
<div className="lava-flood-overlay lava-active">
|
||||
<img src="/lava.jpg" alt="Lava flood" draggable={false} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Crack & Collapse Overlay */}
|
||||
{(showCracks || collapse) && (
|
||||
<div className={`crack-overlay${collapse ? " crack-collapse" : ""}`}>
|
||||
{/* Crack overlay for the spinning effect */}
|
||||
{showCracks && (
|
||||
<div className="crack-overlay" style={{ pointerEvents: "none" }}>
|
||||
<img className="crack crack1" src="/crack1.png" alt="" />
|
||||
<img className="crack crack2" src="/crack2.png" alt="" />
|
||||
{/* Add more cracks for extra effect if you wish */}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -113,7 +134,6 @@ export default function BottomFooter() {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Donate Section */}
|
||||
<div className="min-w-[220px] mb-8 md:mb-0 flex-1">
|
||||
<h3 className="font-bold underline text-lg mb-3">Donate</h3>
|
||||
@ -128,7 +148,6 @@ export default function BottomFooter() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<div className="max-w-6xl mx-auto mt-8 pt-6 flex flex-col md:flex-row items-center justify-between border-t border-gray-200/30">
|
||||
<div className="flex flex-row items-center w-full md:w-auto">
|
||||
@ -167,7 +186,14 @@ export default function BottomFooter() {
|
||||
style={{ cursor: "pointer" }}
|
||||
aria-label="X Crack Easter egg"
|
||||
>
|
||||
<img src="x_logo.jpg" alt="X" className="h-7 w-7 rounded-full shadow" />
|
||||
<span className="footer-x-logo-wrap">
|
||||
<img
|
||||
src="x_logo.jpg"
|
||||
alt="X"
|
||||
className="h-7 w-7 rounded-full shadow"
|
||||
style={{ display: "block"}}
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user