diff --git a/script/mapbox.js b/script/mapbox.js new file mode 100644 index 0000000..9993d45 --- /dev/null +++ b/script/mapbox.js @@ -0,0 +1,160 @@ +// script.js + +// 1️⃣ Your Mapbox access token +mapboxgl.accessToken = 'pk.eyJ1IjoiY2FiYmJ5eSIsImEiOiJjbWRpYzY1MGgwYzA5Mm1xM25udDUzbGtpIn0.RX6zv4VI6r2vpj7IWidz0w'; + +// 2️⃣ Initialize the map zoomed fully out (globe‑like) +const map = new mapboxgl.Map({ + container: 'map', + style: 'mapbox://styles/mapbox/streets-v12', + center: [0, 0], + zoom: 1, + attributionControl: false +}); + +// 3️⃣ Define your companies ([lat, lng], zoom, imageUrl, description) +const companies = [ + { + id: 'overview', + name: 'Overview', + coords: [40.14010599415405, -107.69483342077913], + zoom: 3, + imageUrl: 'images/wip.jpg', + description: ` +

Overview

+

Use the controls in the top left corner to jump to a spot!

+ ` + }, + { + id: 'tancha', + name: 'Tan‑Cha', + coords: [37.325382569107695, -122.01149036275054], + zoom: 9, + imageUrl: 'images/tancha.jpg', + description: ` +

Tan‑Cha

+

Wip.

+ ` + }, + { + id: 'pokehouse', + name: 'Poke House', + coords: [37.301113384436285, -121.9493462690826], + zoom: 9, + imageUrl: 'images/wip.jpg', + description: ` +

Poke House

+

Wip.

+ ` + }, + { + id: 'nanobase', + name: 'NanoBase', + coords: [34.68104506798117, 135.49167196036512], + zoom: 9, + imageUrl: 'images/work.jpg', + description: ` +

NanoBase

+

Developed automated payroll processing scripts in Python, reducing errors by 85%.

+ NanoBase Blog + ` + }, + { + id: 'lanner', + name: 'Lanner Electronics', + coords: [43.69824981965482, -79.62570806485385], + zoom: 9, + imageUrl: 'images/wip.jpg', + description: ` +

Lanner Electronics

+

Wip.

+ ` + } +]; + +// 4️⃣ Populate the company list +const listEl = document.getElementById('company-list'); +companies.forEach((company, idx) => { + const btn = document.createElement('button'); + btn.textContent = company.name; + btn.id = company.id; + btn.addEventListener('click', () => selectCompany(idx)); + listEl.appendChild(btn); +}); + +// Holder for mapboxgl.Marker instances +const markers = []; + +// Reference to the .map-wrapper for our fixed popups +const mapWrapper = document.querySelector('.map-wrapper'); + +/** + * Renders a fixed-position popup (does not move with the map) + */ +function showFixedPopup(company, [lng, lat]) { + // Remove any existing fixed popups + document.querySelectorAll('.fixed-popup').forEach(el => el.remove()); + + // Build the popup element + const popupEl = document.createElement('div'); + popupEl.className = 'fixed-popup'; + popupEl.innerHTML = ` + Me at ${company.name} + ${company.name} + `; + + // Attach into the map wrapper (above the map canvas) + mapWrapper.appendChild(popupEl); +} + +/** + * Handles company selection: + * - cleans up old markers/popups + * - flies the map + * - updates the blurb + * - adds marker + fixed popup (if not overview) + */ +function selectCompany(index) { + const { coords, zoom, description, id } = companies[index]; + const [lat, lng] = coords; + + // Remove existing markers + markers.forEach(m => m.remove()); + markers.length = 0; + + // Remove any existing popups + document.querySelectorAll('.mapboxgl-popup, .fixed-popup').forEach(el => el.remove()); + + // Fly to the new location + map.flyTo({ + center: [lng, lat], + zoom, + speed: 1.5, + essential: true + }); + + // Update the description blurb + document.getElementById('blurb').innerHTML = description; + + // Highlight the active button + document.querySelectorAll('.company-list button') + .forEach(b => b.classList.toggle('active', b.id === id)); + + // If it's the overview, skip marker + popup + if (index === 0) return; + + // Add a marker + const marker = new mapboxgl.Marker() + .setLngLat([lng, lat]) + .addTo(map); + markers.push(marker); + + // Show our custom fixed popup + showFixedPopup(companies[index], [lng, lat]); +} + +// Initialize on load with the overview +selectCompany(0); diff --git a/script/scroll.js b/script/scroll.js new file mode 100644 index 0000000..e5ded73 --- /dev/null +++ b/script/scroll.js @@ -0,0 +1,59 @@ +// scroll.js + +document.addEventListener('DOMContentLoaded', () => { + // ───────────────────────────────────────────────────────────────────────────── + // 1) Vertical “zoom‑in/out” on .scroll‑section using IntersectionObserver + // ───────────────────────────────────────────────────────────────────────────── + const sections = document.querySelectorAll('.scroll-section'); + const io = new IntersectionObserver((entries) => { + entries.forEach(entry => { + entry.target.classList.toggle('in-view', entry.isIntersecting); + }); + }, { threshold: 0.15 }); + sections.forEach(sec => io.observe(sec)); + + // ───────────────────────────────────────────────────────────────────────────── + // 2) Horizontal‑scroll “pin & pan” for the Hobbies section + // ───────────────────────────────────────────────────────────────────────────── + const wrapper = document.getElementById('hobbies'); + if (!wrapper) return; // no Hobbies section → exit + + // The element that will stay stuck in the viewport: + const stickySection = wrapper.querySelector('.horizontal-section'); + // The inner flex row we translate left/right: + const inner = stickySection.querySelector('.horizontal-section__inner'); + + // A) Make the section itself “sticky” at the top of the viewport + stickySection.style.position = 'sticky'; + stickySection.style.top = '3rem'; + stickySection.style.height = '100vh'; + stickySection.style.overflow = 'hidden'; + + // B) Compute how far we need to scroll to reveal all cards + let containerW = wrapper.clientWidth; // e.g. 70vw in px + let scrollableDist = inner.scrollWidth - containerW; // total horizontal span minus one screen + + // C) Stretch the wrapper’s vertical height so that + // scrolling wrapperHeight(px) → moving inner by scrollableDist(px) + wrapper.style.height = `${window.innerHeight + scrollableDist}px`; + + // D) On scroll, when within the wrapper’s “pin zone,” + // translate the inner row instead of moving vertically + window.addEventListener('scroll', () => { + const y = window.scrollY; + const top = wrapper.offsetTop; + const end = top + scrollableDist; + + if (y >= top && y <= end) { + // map vertical scroll → horizontal translate + inner.style.transform = `translateX(-${y - top}px)`; + } + }); + + // E) Recompute on resize + window.addEventListener('resize', () => { + containerW = wrapper.clientWidth; + scrollableDist = inner.scrollWidth - containerW; + wrapper.style.height = `${window.innerHeight + scrollableDist}px`; + }); +});