Anne-Charlotte's picture
Create script.js
d78a473 verified
raw
history blame
13.8 kB
// Assembly Steps Data
const YOUTUBE_VIDEO_ID = "_r0cHySFbeY";
const stepsData = [
{ timestamp: "00:00:00", title: "Apply the foot pads." },
{ timestamp: "00:01:14", title: "Install the USB extension cable." },
{ timestamp: "00:02:55", title: "Mount the body PCB." },
{ timestamp: "00:04:30", title: "Connect the cables to the body PCB." },
{ timestamp: "00:05:05", title: "Insert the ball bearing." },
{ timestamp: "00:05:44", title: "Fit the body down assembly to the foot assembly." },
{ timestamp: "00:06:14", title: "Mount the base foot motor to the body turning unit." },
{ timestamp: "00:07:14", title: "Position the body turning assembly on the body down assembly." },
{ timestamp: "00:08:25", title: "Secure the body turning assembly to the body down assembly." },
{ timestamp: "00:10:15", title: "Screw the Stewart main plate in place." },
{ timestamp: "00:13:10", title: "Connect the base foot motor." },
{ timestamp: "00:14:13", title: "Screw the link rods onto the motor arms." },
{ timestamp: "00:18:05", title: "Connect Motors 1 and 2." },
{ timestamp: "00:18:44", title: "Connect Motors 2 and 3." },
{ timestamp: "00:19:31", title: "Connect Motors 4 and 5." },
{ timestamp: "00:20:19", title: "Connect Motors 5 and 6." },
{ timestamp: "00:20:53", title: "Insert all motors into the Stewart main plate." },
{ timestamp: "00:22:55", title: "Clip the motor cables into the body down assembly." },
{ timestamp: "00:24:46", title: "Screw the speaker into the tricap." },
{ timestamp: "00:25:39", title: "Position the tricap." },
{ timestamp: "00:27:01", title: "Connect Motors 3 and 4." },
{ timestamp: "00:27:44", title: "Route the cables." },
{ timestamp: "00:28:04", title: "Secure the tricap with screws." },
{ timestamp: "00:30:47", title: "Screw the bottom head onto the link rods." },
{ timestamp: "00:35:17", title: "Route the cables through the bottom head." },
{ timestamp: "00:35:49", title: "Route the cables through the head PCB." },
{ timestamp: "00:36:10", title: "Screw the head PCB in place." },
{ timestamp: "00:37:38", title: "Position the top shell." },
{ timestamp: "00:38:42", title: "Screw the top shell in place." },
{ timestamp: "00:41:08", title: "Place the lenses in the glasses holder." },
{ timestamp: "00:41:38", title: "Insert the fisheye lenses into the caps." },
{ timestamp: "00:42:14", title: "Snap the fisheye lenses into the glasses holder." },
{ timestamp: "00:43:07", title: "Position the Arducam camera." },
{ timestamp: "00:43:35", title: "Screw the Arducam camera in place." },
{ timestamp: "00:44:08", title: "Screw the glasses assembly onto the front head shell." },
{ timestamp: "00:45:51", title: "Plug in USB-C." },
{ timestamp: "00:46:06", title: "Attach the cases to the antenna motors." },
{ timestamp: "00:46:46", title: "Mount the motor assembly to the back head shell." },
{ timestamp: "00:50:11", title: "Connect the antenna motors." },
{ timestamp: "00:51:13", title: "Slide the back-head assembly onto the Reachy Mini body." },
{ timestamp: "00:51:45", title: "Screw the back head in place." },
{ timestamp: "00:53:35", title: "Attach the cable holder." },
{ timestamp: "00:54:29", title: "Connect the speaker and motor cables." },
{ timestamp: "00:55:22", title: "Connect the power and USB extension cables." },
{ timestamp: "00:55:48", title: "Connect the flexible printed cable to the top head PCB." },
{ timestamp: "00:56:23", title: "Slide the top-head assembly onto the back head." },
{ timestamp: "00:56:31", title: "Connect the flexible printed cable to the head PCB." },
{ timestamp: "00:57:18", title: "Plug the USB-C cable into the head PCB." },
{ timestamp: "00:58:09", title: "Attach the front head." },
{ timestamp: "00:59:20", title: "Assemble the antennas." },
{ timestamp: "01:00:00", title: "Attach the antennas to the head." }
];
// Parse timestamp to seconds
function parseTimestamp(ts) {
const parts = ts.split(':').map(Number);
return parts[0] * 3600 + parts[1] * 60 + parts[2];
}
// Create assembly steps with parsed timestamps
const assemblySteps = stepsData.map((step, index) => ({
id: index + 1,
title: step.title,
timestamp: step.timestamp,
timestampSeconds: parseTimestamp(step.timestamp)
}));
const TOTAL_STEPS = assemblySteps.length;
// Available step images
const availableImages = {
1: "assets/step1.jpg",
2: "assets/step2.jpg"
};
function getStepImage(stepId) {
return availableImages[stepId] || null;
}
// App State
let currentStep = 1;
let isFullscreen = false;
let scale = 1;
let position = { x: 0, y: 0 };
let isDragging = false;
let dragStart = { x: 0, y: 0 };
// DOM Elements
const stepCounterText = document.getElementById('step-counter-text');
const stepImage = document.getElementById('step-image');
const placeholder = document.getElementById('placeholder');
const placeholderNumber = document.getElementById('placeholder-number');
const imageWrapper = document.getElementById('image-wrapper');
const youtubeIframeDesktop = document.getElementById('youtube-iframe-desktop');
const youtubeIframeMobile = document.getElementById('youtube-iframe-mobile');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const stepIndicators = document.getElementById('step-indicators');
const progressBar = document.getElementById('progress-bar');
const fullscreenBtn = document.getElementById('fullscreen-btn');
const fullscreenModal = document.getElementById('fullscreen-modal');
const closeFullscreenBtn = document.getElementById('close-fullscreen-btn');
const fullscreenStepTitle = document.getElementById('fullscreen-step-title');
const fullscreenImage = document.getElementById('fullscreen-image');
const fullscreenPlaceholder = document.getElementById('fullscreen-placeholder');
const fullscreenPlaceholderNumber = document.getElementById('fullscreen-placeholder-number');
const fullscreenImageContainer = document.getElementById('fullscreen-image-container');
const fullscreenYoutubeIframeDesktop = document.getElementById('fullscreen-youtube-iframe-desktop');
const fullscreenYoutubeIframeMobile = document.getElementById('fullscreen-youtube-iframe-mobile');
const fullscreenPrevBtn = document.getElementById('fullscreen-prev-btn');
const fullscreenNextBtn = document.getElementById('fullscreen-next-btn');
const fullscreenStepIndicators = document.getElementById('fullscreen-step-indicators');
const fullscreenProgressBar = document.getElementById('fullscreen-progress-bar');
const zoomInBtn = document.getElementById('zoom-in-btn');
const zoomOutBtn = document.getElementById('zoom-out-btn');
const zoomLevel = document.getElementById('zoom-level');
// Update YouTube embed
function updateYouTubeEmbed(timestampSeconds) {
const embedUrl = `https://www.youtube.com/embed/${YOUTUBE_VIDEO_ID}?start=${timestampSeconds}&rel=0&autoplay=1&mute=1`;
youtubeIframeDesktop.src = embedUrl;
youtubeIframeMobile.src = embedUrl;
fullscreenYoutubeIframeDesktop.src = embedUrl;
fullscreenYoutubeIframeMobile.src = embedUrl;
}
// Render step indicators
function renderStepIndicators(containerId, currentStep, onClick) {
const container = document.getElementById(containerId);
container.innerHTML = '';
const groupStart = Math.floor((currentStep - 1) / 10) * 10 + 1;
for (let i = 0; i < 10; i++) {
const stepNum = groupStart + i;
if (stepNum > TOTAL_STEPS) break;
const isActive = stepNum === currentStep;
const button = document.createElement('button');
button.className = `step-indicator ${isActive ? 'step-indicator-active' : 'step-indicator-inactive'}`;
button.setAttribute('aria-label', `Go to step ${stepNum}`);
button.addEventListener('click', () => onClick(stepNum));
container.appendChild(button);
}
}
// Update UI
function updateUI() {
const step = assemblySteps[currentStep - 1];
const imageSrc = getStepImage(step.id);
// Update step counter
stepCounterText.textContent = `Step ${step.id}/${TOTAL_STEPS}`;
// Update image
if (imageSrc) {
stepImage.src = imageSrc;
stepImage.alt = `Assembly step ${step.id}`;
stepImage.classList.remove('hidden');
placeholder.classList.add('hidden');
} else {
stepImage.classList.add('hidden');
placeholder.classList.remove('hidden');
placeholderNumber.textContent = step.id;
}
// Update YouTube embed
updateYouTubeEmbed(step.timestampSeconds);
// Update buttons
prevBtn.disabled = currentStep === 1;
nextBtn.disabled = currentStep === TOTAL_STEPS;
// Update step indicators
renderStepIndicators('step-indicators', currentStep, goToStep);
// Update progress bar
const progress = (currentStep / TOTAL_STEPS) * 100;
progressBar.style.width = `${progress}%`;
// Update fullscreen UI
updateFullscreenUI();
}
// Update fullscreen UI
function updateFullscreenUI() {
const step = assemblySteps[currentStep - 1];
const imageSrc = getStepImage(step.id);
fullscreenStepTitle.textContent = `Step ${step.id}/${TOTAL_STEPS} - ${step.title}`;
if (imageSrc) {
fullscreenImage.src = imageSrc;
fullscreenImage.alt = `Step ${step.id}`;
fullscreenImage.classList.remove('hidden');
fullscreenPlaceholder.classList.add('hidden');
} else {
fullscreenImage.classList.add('hidden');
fullscreenPlaceholder.classList.remove('hidden');
fullscreenPlaceholderNumber.textContent = step.id;
}
fullscreenPrevBtn.disabled = currentStep === 1;
fullscreenNextBtn.disabled = currentStep === TOTAL_STEPS;
renderStepIndicators('fullscreen-step-indicators', currentStep, goToStepFullscreen);
const progress = (currentStep / TOTAL_STEPS) * 100;
fullscreenProgressBar.style.width = `${progress}%`;
updateZoomDisplay();
}
// Navigation functions
function goToPrevious() {
if (currentStep > 1) {
currentStep--;
updateUI();
}
}
function goToNext() {
if (currentStep < TOTAL_STEPS) {
currentStep++;
updateUI();
}
}
function goToStep(step) {
if (step >= 1 && step <= TOTAL_STEPS) {
currentStep = step;
updateUI();
}
}
function goToStepFullscreen(step) {
resetZoom();
goToStep(step);
}
function goToPreviousFullscreen() {
resetZoom();
goToPrevious();
}
function goToNextFullscreen() {
resetZoom();
goToNext();
}
// Fullscreen functions
function openFullscreen() {
isFullscreen = true;
fullscreenModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
updateFullscreenUI();
}
function closeFullscreen() {
isFullscreen = false;
fullscreenModal.classList.add('hidden');
document.body.style.overflow = '';
resetZoom();
}
// Zoom functions
function zoomIn() {
scale = Math.min(scale + 0.5, 4);
updateZoomDisplay();
}
function zoomOut() {
scale = Math.max(scale - 0.5, 0.5);
updateZoomDisplay();
}
function resetZoom() {
scale = 1;
position = { x: 0, y: 0 };
updateZoomDisplay();
}
function updateZoomDisplay() {
zoomLevel.textContent = `${Math.round(scale * 100)}%`;
fullscreenImage.style.transform = `translate(${position.x}px, ${position.y}px) scale(${scale})`;
}
// Drag functions for fullscreen image
function handleMouseDown(e) {
if (scale > 1) {
isDragging = true;
dragStart = {
x: e.clientX - position.x,
y: e.clientY - position.y
};
fullscreenImageContainer.style.cursor = 'grabbing';
}
}
function handleMouseMove(e) {
if (isDragging && scale > 1) {
position = {
x: e.clientX - dragStart.x,
y: e.clientY - dragStart.y
};
updateZoomDisplay();
}
}
function handleMouseUp() {
isDragging = false;
fullscreenImageContainer.style.cursor = 'grab';
}
function handleWheel(e) {
e.preventDefault();
const delta = e.deltaY > 0 ? -0.2 : 0.2;
scale = Math.max(0.5, Math.min(4, scale + delta));
updateZoomDisplay();
}
// Event Listeners
prevBtn.addEventListener('click', goToPrevious);
nextBtn.addEventListener('click', goToNext);
fullscreenBtn.addEventListener('click', openFullscreen);
stepImage.addEventListener('click', openFullscreen);
closeFullscreenBtn.addEventListener('click', closeFullscreen);
fullscreenPrevBtn.addEventListener('click', goToPreviousFullscreen);
fullscreenNextBtn.addEventListener('click', goToNextFullscreen);
zoomInBtn.addEventListener('click', zoomIn);
zoomOutBtn.addEventListener('click', zoomOut);
fullscreenImageContainer.addEventListener('mousedown', handleMouseDown);
fullscreenImageContainer.addEventListener('mousemove', handleMouseMove);
fullscreenImageContainer.addEventListener('mouseup', handleMouseUp);
fullscreenImageContainer.addEventListener('mouseleave', handleMouseUp);
fullscreenImageContainer.addEventListener('wheel', handleWheel, { passive: false });
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (isFullscreen) {
if (e.key === 'Escape') closeFullscreen();
if (e.key === '+' || e.key === '=') zoomIn();
if (e.key === '-') zoomOut();
if (e.key === '0') resetZoom();
if (e.key === 'ArrowLeft' && currentStep > 1) goToPreviousFullscreen();
if (e.key === 'ArrowRight' && currentStep < TOTAL_STEPS) goToNextFullscreen();
} else {
if (e.key === 'ArrowRight') goToNext();
if (e.key === 'ArrowLeft') goToPrevious();
}
});
// Initialize
updateUI();