I am inspired by Rolls Royce website and want to implement the same scroll snapping feature in mine as well, I did it with the HTML default scroll-snap-type
which gives me expected behavior but creates two scrollbars, one for the container and another one for the body, which is not expected so I tried to go with the IntersectionObserver
but it causes an issue, I can travel to only adjacent slide when directly jumping from 1st slide to 3rd slide.
Here is the code sandbox link: https://codesandbox.io/s/scrollsnap-forked-pre0c?file=/pages/index.vue
Here is the code that I am working
<template>
<main class="landing">
<nav class="scroller">
<ul class="scroller__list">
<li
class="scroller__item"
v-for="(slide, index) in slides"
:key="index"
@click.prevent="scroll(slide.id)"
>
<a
class="scroller__dot"
:href="'#' + slide.id"
@click.prevent="scroll(slide.id)"
></a>
</li>
</ul>
</nav>
<div class="slides-container">
<slide
class="slide"
v-for="(slide, index) in slides"
:key="index"
:img="slide.img"
:id="slide.id"
:format="slide.format"
:filter="slide.filter"
>{{ slide.content }}</slide
>
</div>
</main>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
data() {
return {
slides: [
{
img: "car-slide-1.png",
content: "hello world",
id: "car-slide-1",
filter: "color-burn",
},
{
img: "car-slide-2.png",
// promo-video.mp4
content: "Second Car",
id: "car-slide-2",
filter: "color-burn",
// format: "video",
},
{
img: "car-slide-3.png",
content: "Third slide",
id: "car-slide-3",
filter: "color-burn",
},
],
observer: null as any as IntersectionObserver,
options: {
threshold: [0.5],
root: process.browser
? document.getElementsByClassName("slides-container")[0]
: null,
} as IntersectionObserverInit,
};
},
methods: {
scroll(id: string, who: string | null = null) {
console.log("scrolling to ", id, who ? "by " + who : "");
document.getElementById(id)?.scrollIntoView({
behavior: "smooth",
block: "start",
});
},
},
mounted() {
let scrolling = false;
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !scrolling) {
let top = entry.boundingClientRect.top;
scrolling = true;
window.scroll({ behavior: "smooth", top: window.pageYOffset + top });
}
scrolling = false;
});
}, this.options);
document
.querySelectorAll(".slide")
.forEach((slide) => this.observer.observe(slide));
},
});
</script>