mirror of
https://github.com/System-End/daydream-phoenix.git
synced 2026-04-19 20:55:11 +00:00
Event-Specific Map
This commit is contained in:
parent
22ab6be86a
commit
93d4505fce
3 changed files with 449 additions and 3 deletions
42
src/routes/event-map/+page.server.js
Normal file
42
src/routes/event-map/+page.server.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { GEOCODER_API_KEY } from '$env/static/private';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
/** @type {import('./$types').PageServerLoad} */
|
||||
export async function load({ url }) {
|
||||
const location = url.searchParams.get('location');
|
||||
|
||||
if (!GEOCODER_API_KEY || !location) {
|
||||
return {
|
||||
location: null,
|
||||
geocoded: null
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Geocode the provided location
|
||||
const geocodeUrl = `https://geocoder.hackclub.com/v1/geocode?address=${encodeURIComponent(location)}&key=${GEOCODER_API_KEY}`;
|
||||
const geocodeResponse = await fetch(geocodeUrl);
|
||||
|
||||
if (geocodeResponse.ok) {
|
||||
const geocodeData = await geocodeResponse.json();
|
||||
return {
|
||||
location,
|
||||
geocoded: {
|
||||
lat: geocodeData.lat,
|
||||
lng: geocodeData.lng,
|
||||
address: location
|
||||
}
|
||||
};
|
||||
} else {
|
||||
console.error(`Failed to geocode ${location}: ${geocodeResponse.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to geocode ${location}:`, error);
|
||||
}
|
||||
|
||||
return {
|
||||
location,
|
||||
geocoded: null
|
||||
};
|
||||
}
|
||||
396
src/routes/event-map/+page.svelte
Normal file
396
src/routes/event-map/+page.svelte
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
|
||||
let mapContainer: HTMLElement;
|
||||
let map: any;
|
||||
|
||||
onMount(() => {
|
||||
if (browser) {
|
||||
// Mouse trail setup
|
||||
let mouseTrailPoints: Array<{ x: number; y: number }> = [];
|
||||
const maxTrailLength = 15;
|
||||
let animationId: number;
|
||||
|
||||
// Rainbow trail configuration - adjust these to tune the effect
|
||||
const colorChangeSpeed = 400; // Lower = faster color changes (was 800)
|
||||
const trailColorSpan = 25; // Degrees of color variation across trail length (was 15)
|
||||
const trailThicknessStart = 3; // Path thickness at tail (oldest points)
|
||||
const trailThicknessEnd = 8; // Path thickness at mouse (newest points)
|
||||
|
||||
function getHueFromIndex(index: number): number {
|
||||
return (index * 24) % 360; // Rainbow cycle through hues
|
||||
}
|
||||
|
||||
function updateMouseTrail(e: MouseEvent) {
|
||||
// Get fresh references to all SVG containers
|
||||
const svgs = [
|
||||
document.querySelector('#trail-svg-blur'),
|
||||
// document.querySelector('#trail-svg-main')
|
||||
];
|
||||
if (svgs.find(svg => !svg)) return;
|
||||
|
||||
const x = e.clientX;
|
||||
const y = e.clientY;
|
||||
|
||||
mouseTrailPoints.push({ x, y });
|
||||
|
||||
// Keep trail at max length
|
||||
if (mouseTrailPoints.length > maxTrailLength) {
|
||||
mouseTrailPoints.shift();
|
||||
}
|
||||
|
||||
// Clear existing paths and gradients
|
||||
if (mouseTrailPoints.length > 1) {
|
||||
svgs.forEach((svg: any) => {
|
||||
// Clear old elements
|
||||
const oldPaths = svg.querySelectorAll('.trail-segment');
|
||||
const oldGradients = svg.querySelectorAll('.segment-gradient');
|
||||
oldPaths.forEach((path: any) => path.remove());
|
||||
oldGradients.forEach((grad: any) => grad.remove());
|
||||
|
||||
// Ensure defs exists
|
||||
let defs = svg.querySelector('defs');
|
||||
if (!defs) {
|
||||
defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
||||
svg.appendChild(defs);
|
||||
}
|
||||
});
|
||||
|
||||
// Create individual path segments with gradient colors
|
||||
for (let i = 0; i < mouseTrailPoints.length - 1; i++) {
|
||||
const progress = i / (mouseTrailPoints.length - 1);
|
||||
const nextProgress = (i + 1) / (mouseTrailPoints.length - 1);
|
||||
|
||||
// Base color changes slowly over time, with subtle variations along trail
|
||||
const baseHue = getHueFromIndex(Date.now() / colorChangeSpeed);
|
||||
const hue1 = baseHue - (progress * trailColorSpan);
|
||||
const hue2 = baseHue - (nextProgress * trailColorSpan);
|
||||
|
||||
// Create paths in all SVGs
|
||||
svgs.forEach((svg: any, svgIndex: number) => {
|
||||
if (!svg) return;
|
||||
|
||||
const defs = svg.querySelector('defs');
|
||||
if (!defs) return;
|
||||
|
||||
// Create gradient for this segment (unique ID per SVG)
|
||||
const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
|
||||
gradient.classList.add('segment-gradient');
|
||||
gradient.setAttribute('id', `segment-gradient-${svgIndex}-${i}`);
|
||||
gradient.setAttribute('x1', '0%');
|
||||
gradient.setAttribute('y1', '0%');
|
||||
gradient.setAttribute('x2', '100%');
|
||||
gradient.setAttribute('y2', '0%');
|
||||
|
||||
const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
|
||||
stop1.setAttribute('offset', '0%');
|
||||
stop1.setAttribute('stop-color', `hsl(${hue1}, 70%, 60%)`);
|
||||
|
||||
const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
|
||||
stop2.setAttribute('offset', '100%');
|
||||
stop2.setAttribute('stop-color', `hsl(${hue2}, 70%, 60%)`);
|
||||
|
||||
gradient.appendChild(stop1);
|
||||
gradient.appendChild(stop2);
|
||||
defs.appendChild(gradient);
|
||||
|
||||
// Create path with gradient stroke - bigger at mouse (newest), smaller at tail (oldest)
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.classList.add('trail-segment');
|
||||
path.setAttribute('d', `M${mouseTrailPoints[i].x},${mouseTrailPoints[i].y} L${mouseTrailPoints[i + 1].x},${mouseTrailPoints[i + 1].y}`);
|
||||
path.setAttribute('stroke', `url(#segment-gradient-${svgIndex}-${i})`);
|
||||
path.setAttribute('stroke-width', `${trailThicknessStart + (trailThicknessEnd - trailThicknessStart) * progress}`);
|
||||
path.setAttribute('stroke-linecap', 'round');
|
||||
path.setAttribute('stroke-linejoin', 'round');
|
||||
path.setAttribute('fill', 'none');
|
||||
|
||||
svg.appendChild(path);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearMouseTrail() {
|
||||
mouseTrailPoints = [];
|
||||
const svgs = [
|
||||
document.querySelector('#trail-svg-blur'),
|
||||
// document.querySelector('#trail-svg-main')
|
||||
];
|
||||
svgs.forEach((svg: any) => {
|
||||
if (svg) {
|
||||
const oldPaths = svg.querySelectorAll('.trail-segment');
|
||||
oldPaths.forEach((path: any) => path.remove());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Trail fade animation - only remove points every few frames
|
||||
let frameCount = 0;
|
||||
function animateTrail() {
|
||||
frameCount++;
|
||||
|
||||
// Only remove points every 3 frames to slow down the fade
|
||||
if (frameCount % 3 === 0 && mouseTrailPoints.length > 0) {
|
||||
mouseTrailPoints.shift();
|
||||
|
||||
// Redraw trail segments with updated colors
|
||||
const svgs = [
|
||||
document.querySelector('#trail-svg-blur'),
|
||||
// document.querySelector('#trail-svg-main')
|
||||
];
|
||||
if (mouseTrailPoints.length > 1 && !svgs.find(svg => !svg)) {
|
||||
svgs.forEach((svg: any) => {
|
||||
// Clear old elements
|
||||
const oldPaths = svg.querySelectorAll('.trail-segment');
|
||||
const oldGradients = svg.querySelectorAll('.segment-gradient');
|
||||
oldPaths.forEach((path: any) => path.remove());
|
||||
oldGradients.forEach((grad: any) => grad.remove());
|
||||
|
||||
// Ensure defs exists
|
||||
let defs = svg.querySelector('defs');
|
||||
if (!defs) {
|
||||
defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
||||
svg.appendChild(defs);
|
||||
}
|
||||
});
|
||||
|
||||
// Redraw segments with gradients
|
||||
for (let i = 0; i < mouseTrailPoints.length - 1; i++) {
|
||||
const progress = i / (mouseTrailPoints.length - 1);
|
||||
const nextProgress = (i + 1) / (mouseTrailPoints.length - 1);
|
||||
|
||||
// Base color changes slowly over time, with subtle variations along trail
|
||||
const baseHue = getHueFromIndex(Date.now() / colorChangeSpeed);
|
||||
const hue1 = baseHue - (progress * trailColorSpan);
|
||||
const hue2 = baseHue - (nextProgress * trailColorSpan);
|
||||
|
||||
// Create paths in all SVGs
|
||||
svgs.forEach((svg: any, svgIndex: number) => {
|
||||
if (!svg) return;
|
||||
|
||||
const defs = svg.querySelector('defs');
|
||||
if (!defs) return;
|
||||
|
||||
// Create gradient
|
||||
const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
|
||||
gradient.classList.add('segment-gradient');
|
||||
gradient.setAttribute('id', `segment-gradient-${svgIndex}-${i}`);
|
||||
gradient.setAttribute('x1', '0%');
|
||||
gradient.setAttribute('y1', '0%');
|
||||
gradient.setAttribute('x2', '100%');
|
||||
gradient.setAttribute('y2', '0%');
|
||||
|
||||
const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
|
||||
stop1.setAttribute('offset', '0%');
|
||||
stop1.setAttribute('stop-color', `hsl(${hue1}, 70%, 60%)`);
|
||||
|
||||
const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
|
||||
stop2.setAttribute('offset', '100%');
|
||||
stop2.setAttribute('stop-color', `hsl(${hue2}, 70%, 60%)`);
|
||||
|
||||
gradient.appendChild(stop1);
|
||||
gradient.appendChild(stop2);
|
||||
defs.appendChild(gradient);
|
||||
|
||||
// Create path - bigger at mouse (newest), smaller at tail (oldest)
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.classList.add('trail-segment');
|
||||
path.setAttribute('d', `M${mouseTrailPoints[i].x},${mouseTrailPoints[i].y} L${mouseTrailPoints[i + 1].x},${mouseTrailPoints[i + 1].y}`);
|
||||
path.setAttribute('stroke', `url(#segment-gradient-${svgIndex}-${i})`);
|
||||
path.setAttribute('stroke-width', `${trailThicknessStart + (trailThicknessEnd - trailThicknessStart) * progress}`);
|
||||
path.setAttribute('stroke-linecap', 'round');
|
||||
path.setAttribute('stroke-linejoin', 'round');
|
||||
path.setAttribute('fill', 'none');
|
||||
|
||||
svg.appendChild(path);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
animationId = requestAnimationFrame(animateTrail);
|
||||
}
|
||||
|
||||
// Wait for DOM and get elements
|
||||
setTimeout(() => {
|
||||
const svgs = [
|
||||
document.querySelector('#trail-svg-blur'),
|
||||
// document.querySelector('#trail-svg-main')
|
||||
];
|
||||
|
||||
if (!svgs.find(svg => !svg)) {
|
||||
// Event listeners
|
||||
document.addEventListener('mousemove', updateMouseTrail);
|
||||
document.addEventListener('mouseleave', clearMouseTrail);
|
||||
|
||||
// Start animation loop
|
||||
animateTrail();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Initialize Leaflet
|
||||
import('leaflet').then((L) => {
|
||||
// Load CSS
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Initialize map
|
||||
map = L.map(mapContainer, {
|
||||
minZoom: 2,
|
||||
maxZoom: 18
|
||||
}).setView([20, 0], 2);
|
||||
|
||||
// Add tile layer with custom styling
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
||||
attribution: '© OpenStreetMap contributors © CARTO',
|
||||
detectRetina: true
|
||||
}).addTo(map);
|
||||
|
||||
// Create custom icon using map-flag.png
|
||||
const flagIcon = L.icon({
|
||||
iconUrl: '/map-flag.png',
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 32],
|
||||
popupAnchor: [0, -32]
|
||||
});
|
||||
|
||||
// Add marker for the specified location if geocoded successfully
|
||||
if (data.geocoded && data.geocoded.lat && data.geocoded.lng) {
|
||||
// Set the map view to the geocoded location with higher zoom
|
||||
map.setView([data.geocoded.lat, data.geocoded.lng], 13);
|
||||
|
||||
const marker = L.marker([data.geocoded.lat, data.geocoded.lng], { icon: flagIcon })
|
||||
.addTo(map);
|
||||
|
||||
// Create popup that shows the address
|
||||
const popup = L.popup({
|
||||
closeButton: false,
|
||||
className: 'custom-popup'
|
||||
}).setContent(data.geocoded.address);
|
||||
|
||||
marker.bindPopup(popup);
|
||||
|
||||
// Show popup on hover, hide on mouseout
|
||||
marker.on('mouseover', () => {
|
||||
marker.openPopup();
|
||||
});
|
||||
marker.on('mouseout', () => {
|
||||
marker.closePopup();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', updateMouseTrail);
|
||||
document.removeEventListener('mouseleave', clearMouseTrail);
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Event Location Map</title>
|
||||
</svelte:head>
|
||||
|
||||
<!-- Mouse trailer SVGs -->
|
||||
<!-- Background blurred layer -->
|
||||
<!-- svelte-ignore component_name_lowercase -->
|
||||
<svg id="trail-svg-blur" class="fixed top-0 left-0 w-full h-full pointer-events-none z-[9998]" style="filter: blur(10px); opacity: 0.75">
|
||||
<!-- Individual path segments will be added here dynamically -->
|
||||
</svg>
|
||||
|
||||
<!-- Foreground sharp layer -->
|
||||
<!-- svelte-ignore component_name_lowercase -->
|
||||
<svg id="trail-svg-main" class="fixed top-0 left-0 w-full h-full pointer-events-none z-[9999]" style="filter: blur(3px) saturate(0) brightness(1000); opacity: 0.25">
|
||||
<!-- Individual path segments will be added here dynamically -->
|
||||
</svg>
|
||||
|
||||
<div bind:this={mapContainer} class="w-full h-screen"></div>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Map container styling */
|
||||
:global(.leaflet-container) {
|
||||
background: linear-gradient(135deg, #CCF4FD 0%, #B8D9F8 100%) !important;
|
||||
border-radius: 16px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* Map controls styling */
|
||||
:global(.leaflet-control-zoom a) {
|
||||
background: #44DBC8 !important;
|
||||
border: 2px solid #3CC2AF !important;
|
||||
color: white !important;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 4px 12px rgba(68, 219, 200, 0.3) !important;
|
||||
transition: all 0.2s ease-in-out !important;
|
||||
}
|
||||
|
||||
:global(.leaflet-control-zoom a:hover) {
|
||||
background: #3CC2AF !important;
|
||||
transform: translateY(-1px) !important;
|
||||
box-shadow: 0 6px 16px rgba(68, 219, 200, 0.4) !important;
|
||||
}
|
||||
|
||||
:global(.leaflet-control-attribution) {
|
||||
background: rgba(252, 247, 196, 0.9) !important;
|
||||
border: 1px solid #D3B180 !important;
|
||||
border-radius: 8px !important;
|
||||
color: #78531D !important;
|
||||
font-family: 'Atkinson Hyperlegible', system-ui, sans-serif !important;
|
||||
backdrop-filter: blur(8px) !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:global(.leaflet-control-attribution a) {
|
||||
color: #4477A3 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
:global(.leaflet-control-attribution a:hover) {
|
||||
color: #44DBC8 !important;
|
||||
}
|
||||
|
||||
/* Custom popup styling */
|
||||
:global(.custom-popup .leaflet-popup-content-wrapper) {
|
||||
background: #FFFBDF !important;
|
||||
border: 1px solid #D3B180 !important;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 4px 8px rgba(211, 177, 128, 0.15) !important;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif !important;
|
||||
}
|
||||
|
||||
:global(.custom-popup .leaflet-popup-content) {
|
||||
margin: 8px 12px !important;
|
||||
color: #78531D !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 500 !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
:global(.custom-popup .leaflet-popup-tip) {
|
||||
background: #FFFBDF !important;
|
||||
border: 1px solid #D3B180 !important;
|
||||
border-top: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
/* Tile layer filter for softer appearance */
|
||||
:global(.leaflet-tile-pane) {
|
||||
filter: hue-rotate(5deg) saturate(0.8) brightness(1.1) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
// Configuration - Put your information here!
|
||||
const eventName = "Example";
|
||||
const eventLocation = "Example City";
|
||||
const eventAddress = "1600 Pennsylvania Avenue, Washington, DC 20500";
|
||||
const directionsURL = "https://www.google.com/maps/search/1600+pennsylvania+avenue/" // This is optional!
|
||||
|
||||
// Sponsors Configuration
|
||||
const sponsorsEnabled = true; // Set to false to hide the entire sponsors section
|
||||
|
|
@ -1197,7 +1199,7 @@ Mumbai`.split("\n")
|
|||
<!-- Map container with cloudy edges -->
|
||||
<div class="relative w-full h-156 overflow-hidden bg-transparent">
|
||||
<iframe
|
||||
src="/map"
|
||||
src="/event-map?location={encodeURIComponent(eventAddress)}"
|
||||
class="w-full h-full border-0 bg-[#acd4e0]"
|
||||
style="
|
||||
mask-image:
|
||||
|
|
@ -1288,14 +1290,20 @@ Mumbai`.split("\n")
|
|||
</iframe>
|
||||
</div>
|
||||
|
||||
<p class="text-center font-sans text-2xl pt-12 max-sm:text-xl text-[#60574b] z-10000">All daydream events are organized by high school students like yourself! <br> <span class="font-bold"><a class="underline hover:text-pink" href="https://forms.hackclub.com/daydream">Sign up</a> to organize now!</span></p>
|
||||
<p class="text-center font-sans text-2xl pt-12 max-sm:text-xl text-[#60574b] z-10000">
|
||||
{#if directionsURL}
|
||||
Daydream {eventName} is taking place at <a class="underline text-pink" href={directionsURL}>{eventAddress}</a>!
|
||||
{:else}
|
||||
Daydream {eventName} is taking place at <span class="underline">{eventAddress}</span>!
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute top-0 left-0 w-full h-full bg-[url('brushstroking.png')] bg-size-[100vw_100vh] bg-repeat mix-blend-overlay opacity-60 pointer-events-none"></div>
|
||||
|
||||
<!-- Macintosh Section -->
|
||||
<div class="w-full flex justify-center py-16 px-8 mt-24 pb-[clamp(232px,29vw,464px)] max-sm:w-[120vw] max-sm:-translate-x-[10vw]">
|
||||
<div class="w-full flex justify-center py-16 px-8 mt-48 pb-[clamp(232px,29vw,464px)] max-sm:w-[120vw] max-sm:-translate-x-[10vw]">
|
||||
<div class="bg-[#c5c2b1] p-4 relative max-w-4xl" style="border-radius: calc(1.5rem + 1rem);">
|
||||
<div class="bg-[#061E2D] text-[#D1E3EE] rounded-3xl py-18 md:py-16 px-10 md:px-18 relative overflow-visible" style="
|
||||
border-image: url('/macintosh.png') 128 91 464 91;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue