I’m working on a slider that has 4 stages of content. I am trying to translate the content in and out of the container based on the slider’s position on the x-axis. I need the content to move with the slider, not transition or animate automatically. I have been working on this for days and I cannot figure it out. I’m working with svelte, typescript, and tailwind css, which I’m relatively new to svelte and typescript. I’m not sure if this would be easier to achieve with GSAP? (which I have a little bit of experience with) Here is my current code:
<script lang="ts">
import { onMount } from "svelte";
import { writable } from "svelte/store";
import { projects } from "../../data/project";
import Header from "../Header.svelte";
import { MoveRight, MoveLeft, ArrowLeft } from "lucide-svelte";
function navigateHome() {
window.location.href = "/";
}
const currentStage = writable(0);
let left = 0;
let moving = false;
let button: HTMLElement;
let h2Stage2: HTMLDivElement;
let h2Stage3: HTMLDivElement;
let imgStage2: HTMLDivElement;
let imgStage3: HTMLDivElement;
let stage1Content: HTMLDivElement;
let stage4Content: HTMLDivElement;
function onMouseDown() {
moving = true;
}
function onMouseMove(e: MouseEvent) {
if (moving) {
const container = document.querySelector(
".draggable-container"
) as HTMLElement;
if (!container) return;
const containerWidth = container.offsetWidth;
const buttonWidth = button.offsetWidth;
const maxDraggableWidth = containerWidth * 0.9;
let newPosition =
e.clientX - container.getBoundingClientRect().left - buttonWidth / 2;
newPosition = Math.min(
Math.max(0, newPosition),
maxDraggableWidth - buttonWidth
);
const percentage = (newPosition / maxDraggableWidth) * 100;
currentStage.set(calculateStage(percentage));
left = percentage;
updateStagePositions(percentage);
}
}
function onMouseUp() {
moving = false;
}
function calculateStage(percentage: number): number {
if (percentage <= 25) {
return 0;
} else if (percentage <= 50) {
return 1;
} else if (percentage <= 75) {
return 2;
} else {
return 3;
}
}
function updateStagePositions(percentage: number) {
const translateValue = (100 - percentage * 4).toFixed(2); // calculate translate value
const reverseTranslateValue = (percentage * 4 - 100).toFixed(2); // reverse translate value
if ($currentStage === 0) {
stage1Content.style.transform = `translateY(${translateValue}px)`;
} else if ($currentStage === 1) {
h2Stage2.style.transform = `translateY(${translateValue}px)`;
imgStage2.style.transform = `translateY(${reverseTranslateValue}px)`;
} else if ($currentStage === 2) {
h2Stage3.style.transform = `translateY(${translateValue}px)`;
imgStage3.style.transform = `translateY(${reverseTranslateValue}px)`;
} else if ($currentStage === 3) {
stage4Content.style.transform = `translateY(${translateValue}px)`;
}
}
onMount(() => {
window.scrollTo(0, 0);
});
export let slug: string;
interface Project {
image: string | null | undefined;
title: string;
description: string;
heading: string;
subhead1: string;
subhead2: string;
info: string;
featuredImage1: string;
featuredImage2: string;
featuredImage3: string;
highlights: string;
highlightList: string[];
media: string;
pcoeTeam: string;
tags: string[];
team: string[];
}
let project: Project | null = null;
onMount(() => {
project = projects.find((p) => p.slug === slug) || null;
});
</script>
<Header />
<div
class="projectDescription grid gap-2 justify-enter text-white bg-black text-center uppercase"
>
<h2 class="text-6xl font-thin">Meta</h2>
<h3 class="text-3xl font-medium">Pursuit Innovation Exhibit</h3>
<button
class="absolute cursor-pointer flex gap-2 uppercase top-48 left-80 text-white bg-black"
on:click={navigateHome}
tabindex="0"
on:keydown={(event) => {
if (event.key === "Enter") {
navigateHome();
}
}}
><ArrowLeft />Back
</button>
</div>
<div class="main bg-black py-12">
<div
class="draggable-container h-[70vh] relative flex justify-center w-[60vw] m-auto overflow-hidden"
>
{#if $currentStage === 0}
<div
class="absolute grid grid-cols-2 top-0 left-0 right-0 bottom-0 rounded-2xl pl-16 py-12 pr-12 bg-cover bg-no-repeat"
style="background-image: url('../../../public/slide.jpg');"
>
<div
class="tag-container absolute flex flex-row gap-6 pt-8 pl-16 text-[#34F0FF] uppercase font-semibold"
>
{#each project?.tags ?? [] as tag}
<div class="tag">{tag}</div>
{/each}
</div>
<div class="stage1-left pt-16" bind:this={stage1Content}>
<h2 class="text-6xl">{project?.heading}</h2>
<h3 class="text-2xl font-bold uppercase pt-6 w-96">
{project?.subhead1}
</h3>
</div>
</div>
{:else if $currentStage === 1}
<div
class="absolute grid grid-cols-2 top-0 left-0 right-0 bottom-0 rounded-2xl pl-16 py-12 pr-12 bg-cover bg-no-repeat"
style="background-image: linear-gradient(to top right, #A8F9FF 55%, #34F0FF);"
>
<div
class="tag-container absolute flex flex-row gap-6 pt-8 pl-16 text-[#34F0FF] uppercase font-semibold opacity-30"
>
<div class="tag">Tag 1</div>
<div class="tag">Tag 2</div>
<div class="tag">Tag 3</div>
<div class="tag">Tag 4</div>
</div>
<div class="stage2-left w-[400px] pt-16" bind:this={h2Stage2}>
<h2 class="text-3xl font-black uppercase pb-2">
{project?.subhead2}
</h2>
<p>{project?.info}</p>
</div>
<div class="stage2-right" bind:this={imgStage2}>
<img src={project?.featuredImage1} alt="Meta" class="w-full mt-12" />
</div>
</div>
{:else if $currentStage === 2}
<div
class="absolute grid grid-cols-2 top-0 left-0 right-0 bottom-0 rounded-2xl pl-16 py-12 pr-12 bg-cover bg-no-repeat"
style="background-image: linear-gradient(to top right, #A8F9FF 55%, #34F0FF);"
>
<div
class="tag-container absolute flex flex-row gap-6 pt-8 pl-16 text-[#34F0FF] uppercase font-semibold opacity-30"
>
{#each project?.tags ?? [] as tag}
<div class="tag">{tag}</div>
{/each}
</div>
<div class="stage3-left pt-16" bind:this={h2Stage3}>
<div class="image-container grid gap-2">
<img src={project?.featuredImage2} alt="Meta" class="px-5" />
<img src={project?.featuredImage3} alt="Meta" class="w-fit" />
</div>
</div>
<div
class="stage3-right grid pt-16 justify-center grid-rows-[50px_1fr_1fr_1fr_1fr"
bind:this={imgStage3}
>
<h2 class="text-3xl font-black uppercase w-full text-center">
{project?.highlights}
</h2>
<ul class="highlights grid gap-2 w-full px-14 text-sm">
{#each project?.highlightList ?? [] as highlight}
<li class="highlights">{highlight}</li>
{/each}
</ul>
</div>
</div>
{:else if $currentStage === 3}
<div
class="absolute grid grid-cols-2 top-0 left-0 right-0 bottom-0 rounded-2xl pl-16 py-12 pr-12 bg-cover bg-no-repeat"
style="background-image: linear-gradient(to top right, #A8F9FF 55%, #34F0FF);"
>
<div
class="tag-container absolute flex flex-row gap-6 pt-8 pl-16 text-[#34F0FF] uppercase font-semibold opacity-30"
>
{#each project?.tags ?? [] as tag}
<div class="tag">{tag}</div>
{/each}
</div>
<div class="stage4-left grid content-center" bind:this={stage4Content}>
<div class="image-container pl-12">
<img src={project?.media} alt="Meta" />
</div>
</div>
<div
class="stage4-right ml-24 grid gap-2 content-center justify-end border-l-2 border-y-2 border-white pr-12 rounded-l-xl"
>
<h2 class="font-black uppercase text-black text-xl">
{project?.pcoeTeam}
</h2>
<ul>
{#each project?.team ?? [] as team}
<li>{team}</li>
{/each}
</ul>
<button
class="uppercase flex items-center gap-2 text-sm text-black font-black mt-16 border-white rounded-full border-2 px-6 py-1"
>
Access Files<MoveRight />
</button>
</div>
</div>
{/if}
<section
class="draggable absolute bottom-5 h-16 flex items-center"
style="left: {left}%"
bind:this={button}
on:mousedown={onMouseDown}
role="button"
tabindex="0"
>
<button
class="w-auto h-12 font-black uppercase bg-[#34F0FF] border-solid border-white border-2 flex gap-2 px-2 items-center justify-center rounded-full cursor-pointer ml-6 z-10"
>
<MoveLeft size="30" />Drag<MoveRight size="30" />
</button>
</section>
<div class="dragLine absolute bottom-12 w-[50vw] h-[2px] bg-white"></div>
</div>
</div>
<svelte:window on:mouseup={onMouseUp} on:mousemove={onMouseMove} />
<style lang="postcss">
.tag {
background: #0d4246;
padding: 6px 27px;
border-radius: 0 18px;
}
.highlights li {
background: #34f0ff;
border-radius: 0 18px;
padding: 10px 16px;
}
</style>
Is there any other way to achieve this or am I even going in the right direction? Any feedback, suggestions, or help is greatly appreciated.