Easter Eggs
This commit is contained in:
parent
587b6b7f01
commit
4438953fab
BIN
public/crack1.png
Normal file
BIN
public/crack1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
BIN
public/crack2.png
Normal file
BIN
public/crack2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
public/lava.jpg
Normal file
BIN
public/lava.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@ -65,3 +65,166 @@ body {
|
|||||||
color: #111;
|
color: #111;
|
||||||
/* or black */
|
/* or black */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- LAVA FLOOD OVERLAY ---- */
|
||||||
|
.lava-flood-overlay {
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
top: -100vh;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
transition: top 0.9s cubic-bezier(.6, 0, .2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lava-flood-overlay.lava-active {
|
||||||
|
top: 0;
|
||||||
|
transition: top 0.33s cubic-bezier(.6, 0, .2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lava-flood-overlay img,
|
||||||
|
.lava-gradient {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
object-fit: cover;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
filter: brightness(1.15) saturate(1.8) drop-shadow(0 0 80px #ff5500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- INSANE SCREEN SHAKE (LinkedIn) ---- */
|
||||||
|
@keyframes supershake {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0) rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
5% {
|
||||||
|
transform: translate(-20px, 5px) rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
10% {
|
||||||
|
transform: translate(18px, -8px) rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
15% {
|
||||||
|
transform: translate(-22px, 8px) rotate(-4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
transform: translate(22px, -2px) rotate(4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
transform: translate(-18px, 12px) rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
transform: translate(18px, -10px) rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
35% {
|
||||||
|
transform: translate(-22px, 14px) rotate(-4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: translate(22px, -12px) rotate(4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
45% {
|
||||||
|
transform: translate(-18px, 8px) rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translate(18px, -14px) rotate(4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
55% {
|
||||||
|
transform: translate(-22px, 12px) rotate(-4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
60% {
|
||||||
|
transform: translate(22px, -8px) rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
65% {
|
||||||
|
transform: translate(-18px, 10px) rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: translate(18px, -12px) rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: translate(-22px, 14px) rotate(-4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
80% {
|
||||||
|
transform: translate(22px, -10px) rotate(4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
85% {
|
||||||
|
transform: translate(-18px, 8px) rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
90% {
|
||||||
|
transform: translate(18px, -14px) rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
95% {
|
||||||
|
transform: translate(-20px, 5px) rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate(0, 0) rotate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shake-screen {
|
||||||
|
animation: supershake 1s cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- CRACK + COLLAPSE OVERLAY (X icon) ---- */
|
||||||
|
.crack-overlay {
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: transform 1.1s cubic-bezier(.65, .05, .45, 1), opacity 0.5s;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crack {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crack1 {
|
||||||
|
width: 35vw;
|
||||||
|
left: 10vw;
|
||||||
|
top: 22vh;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crack2 {
|
||||||
|
width: 32vw;
|
||||||
|
right: 12vw;
|
||||||
|
top: 42vh;
|
||||||
|
opacity: 0.7;
|
||||||
|
transform: rotate(-8deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add more .crackN classes if using more cracks */
|
||||||
|
|
||||||
|
/* Collapse falling effect */
|
||||||
|
.crack-collapse {
|
||||||
|
transform: perspective(900px) rotateX(75deg) translateY(80vh) scale(0.9);
|
||||||
|
opacity: 0;
|
||||||
|
transition: transform 1.1s cubic-bezier(.65, .05, .45, 1), opacity 0.6s;
|
||||||
|
}
|
||||||
@ -55,7 +55,7 @@ export default function LearnPage() {
|
|||||||
<li>First aid kit and emergency medication</li>
|
<li>First aid kit and emergency medication</li>
|
||||||
<li>Food (non-perishable)</li>
|
<li>Food (non-perishable)</li>
|
||||||
<li>Bottled water</li>
|
<li>Bottled water</li>
|
||||||
<li>Torch (flashlight)</li>
|
<li>Torch</li>
|
||||||
<li>Satellite phone</li>
|
<li>Satellite phone</li>
|
||||||
<li>Warm clothing and blankets</li>
|
<li>Warm clothing and blankets</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -23,17 +23,16 @@ export default function Observatories() {
|
|||||||
() =>
|
() =>
|
||||||
data && data.observatories
|
data && data.observatories
|
||||||
? data.observatories
|
? data.observatories
|
||||||
.map(
|
.map((x: Observatory): GeologicalEvent & { isFunctional: boolean } => ({
|
||||||
(x: Observatory): GeologicalEvent => ({
|
|
||||||
id: x.id.toString(),
|
id: x.id.toString(),
|
||||||
title: `New Observatory - ${x.name}`,
|
title: ` ${x.name}`,
|
||||||
longitude: x.longitude,
|
longitude: x.longitude,
|
||||||
latitude: x.latitude,
|
latitude: x.latitude,
|
||||||
|
isFunctional: x.isFunctional, // <-- include this!
|
||||||
text1: "",
|
text1: "",
|
||||||
text2: getRelativeDate(x.dateEstablished),
|
text2: getRelativeDate(x.dateEstablished),
|
||||||
date: x.dateEstablished,
|
date: x.dateEstablished,
|
||||||
})
|
}))
|
||||||
)
|
|
||||||
.sort(
|
.sort(
|
||||||
(a: GeologicalEvent, b: GeologicalEvent) =>
|
(a: GeologicalEvent, b: GeologicalEvent) =>
|
||||||
new Date(b.date).getTime() - new Date(a.date).getTime()
|
new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||||
|
|||||||
@ -1,9 +1,84 @@
|
|||||||
// components/Footer.tsx
|
import React, { useCallback, useRef, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { FaFacebook, FaLinkedin, FaTwitter, FaYoutube } from "react-icons/fa";
|
|
||||||
|
|
||||||
export default function Footer() {
|
export default function BottomFooter() {
|
||||||
|
// Lava flood state & timer
|
||||||
|
const [lavaActive, setLavaActive] = useState(false);
|
||||||
|
const lavaTimeout = useRef<any>(null);
|
||||||
|
|
||||||
|
// LinkedIn shake state & timer
|
||||||
|
const [shaking, setShaking] = useState(false);
|
||||||
|
const shakeTimeout = useRef<any>(null);
|
||||||
|
|
||||||
|
// Crack+collapse states for the X logo
|
||||||
|
const [showCracks, setShowCracks] = useState(false);
|
||||||
|
const [collapse, setCollapse] = useState(false);
|
||||||
|
const crackTimeout = useRef<any>(null);
|
||||||
|
|
||||||
|
// Lava flood handler (top-down flood)
|
||||||
|
const handleInstagramClick = useCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLavaActive(true);
|
||||||
|
clearTimeout(lavaTimeout.current);
|
||||||
|
lavaTimeout.current = setTimeout(() => setLavaActive(false), 2000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// LinkedIn shake handler
|
||||||
|
const handleLinkedInClick = useCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (shaking) return; // prevent stacking
|
||||||
|
setShaking(true);
|
||||||
|
const body = document.body;
|
||||||
|
body.classList.remove("shake-screen");
|
||||||
|
void body.offsetWidth;
|
||||||
|
body.classList.add("shake-screen");
|
||||||
|
shakeTimeout.current = setTimeout(() => {
|
||||||
|
setShaking(false);
|
||||||
|
body.classList.remove("shake-screen");
|
||||||
|
}, 1000);
|
||||||
|
}, [shaking]);
|
||||||
|
|
||||||
|
// X (crack and collapse) handler
|
||||||
|
const handleXClick = useCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShowCracks(true);
|
||||||
|
crackTimeout.current = setTimeout(() => {
|
||||||
|
setCollapse(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowCracks(false);
|
||||||
|
setCollapse(false);
|
||||||
|
}, 1500);
|
||||||
|
}, 1000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
clearTimeout(lavaTimeout.current);
|
||||||
|
clearTimeout(shakeTimeout.current);
|
||||||
|
clearTimeout(crackTimeout.current);
|
||||||
|
document.body.classList.remove("shake-screen");
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{/* 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" : ""}`}>
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
<footer className="bg-[#16424b] text-white pt-12 pb-4 px-6 mt-12 z-0">
|
<footer className="bg-[#16424b] text-white pt-12 pb-4 px-6 mt-12 z-0">
|
||||||
<div className="max-w-6xl mx-auto flex flex-col md:flex-row justify-between gap-8">
|
<div className="max-w-6xl mx-auto flex flex-col md:flex-row justify-between gap-8">
|
||||||
{/* Useful Links */}
|
{/* Useful Links */}
|
||||||
@ -21,17 +96,24 @@ export default function Footer() {
|
|||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="https://www.dysoninstitute.ac.uk/about-us/governance/privacy-notices/" className="hover:underline">
|
<Link
|
||||||
|
href="https://www.dysoninstitute.ac.uk/about-us/governance/privacy-notices/"
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
Privacy policy
|
Privacy policy
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="https://privacy.dyson.com/en/globalcookiepolicy.aspx" className="hover:underline">
|
<Link
|
||||||
|
href="https://privacy.dyson.com/en/globalcookiepolicy.aspx"
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
Cookies policy
|
Cookies policy
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Donate Section */}
|
{/* Donate Section */}
|
||||||
<div className="min-w-[220px] mb-8 md:mb-0 flex-1">
|
<div className="min-w-[220px] mb-8 md:mb-0 flex-1">
|
||||||
<h3 className="font-bold underline text-lg mb-3">Donate</h3>
|
<h3 className="font-bold underline text-lg mb-3">Donate</h3>
|
||||||
@ -39,36 +121,50 @@ export default function Footer() {
|
|||||||
We are a nonprofit entirely funded by your donations, every penny helps provide life saving information.
|
We are a nonprofit entirely funded by your donations, every penny helps provide life saving information.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href="#"
|
href="https://shelterbox.org/"
|
||||||
className="bg-gray-200 hover:bg-blue-600 hover:text-white text-black font-bold rounded-full px-8 py-2 shadow transition-colors duration-200 inline-block text-center"
|
className="bg-gray-200 hover:bg-blue-600 hover:text-white text-black font-bold rounded-full px-8 py-2 shadow transition-colors duration-200 inline-block text-center"
|
||||||
>
|
>
|
||||||
Donate Now
|
Donate Now
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom bar */}
|
{/* 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="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">
|
||||||
{/* Bottom left: Copyright */}
|
|
||||||
<span className="text-sm flex items-center">
|
<span className="text-sm flex items-center">
|
||||||
<span className="mr-2">©</span> TremorTracker 2025
|
<span className="mr-2">©</span> TremorTracker 2025
|
||||||
</span>
|
</span>
|
||||||
{/* Bottom right: Social icons */}
|
|
||||||
<div className="flex flex-col items-end">
|
<div className="flex flex-col items-end">
|
||||||
<span className="text-sm mb-2">Follow us on</span>
|
<span className="text-sm mb-2">Follow us on</span>
|
||||||
<div className="flex space-x-3">
|
<div className="flex space-x-3">
|
||||||
{/* Replace src with your icon URLs, or use next/image if preferred */}
|
<a
|
||||||
<a href="#" target="_blank" rel="noopener noreferrer">
|
href="#"
|
||||||
|
onClick={handleInstagramClick}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
aria-label="Instagram Lava Easter egg"
|
||||||
|
>
|
||||||
<img src="instagram.png" alt="instagram" className="h-7 w-7 rounded-full shadow" />
|
<img src="instagram.png" alt="instagram" className="h-7 w-7 rounded-full shadow" />
|
||||||
</a>
|
</a>
|
||||||
<a href="#" target="_blank" rel="noopener noreferrer">
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={handleLinkedInClick}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
aria-label="LinkedIn Shake Easter egg"
|
||||||
|
>
|
||||||
<img src="linkedin.png" alt="linkedin" className="h-7 w-7 rounded-full shadow" />
|
<img src="linkedin.png" alt="linkedin" className="h-7 w-7 rounded-full shadow" />
|
||||||
</a>
|
</a>
|
||||||
<a href="#" target="_blank" rel="noopener noreferrer">
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={handleXClick}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
aria-label="X Crack Easter egg"
|
||||||
|
>
|
||||||
<img src="x_logo.jpg" alt="X" className="h-7 w-7 rounded-full shadow" />
|
<img src="x_logo.jpg" alt="X" className="h-7 w-7 rounded-full shadow" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -112,7 +112,11 @@ function MapComponent({
|
|||||||
|
|
||||||
const observatoryElement = document.createElement("div");
|
const observatoryElement = document.createElement("div");
|
||||||
const root = createRoot(observatoryElement);
|
const root = createRoot(observatoryElement);
|
||||||
root.render(<GiObservatory className="text-blue-600 text-2xl drop-shadow-lg" />);
|
root.render(
|
||||||
|
<GiObservatory
|
||||||
|
className={`text-2xl drop-shadow-lg ${event.isFunctional === false ? "text-gray-400" : "text-blue-600"}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
quakeElement.appendChild(pulseElement);
|
quakeElement.appendChild(pulseElement);
|
||||||
quakeElement.appendChild(dotElement);
|
quakeElement.appendChild(dotElement);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user