Skip to content

无限视差滚动

展示

在图上滚动鼠标混轮试试 图片由 AI 生成

代码实现

点击展开
vue
<template>
  <div ref="scrollContainer" class="scroll-container"></div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
const scrollContainer = ref<HTMLElement>();
const images: string[] = [
  "/assets/demo/scenery-1.jpg",
  "/assets/demo/scenery-2.jpg",
  "/assets/demo/scenery-3.jpg",
  "/assets/demo/scenery-4.jpg",
  "/assets/demo/scenery-5.jpg",
  "/assets/demo/scenery-6.jpg",
  "/assets/demo/scenery-7.jpg",
  "/assets/demo/scenery-8.jpg",
];
let currentIndex: number = 0;
const createImg = (index: number) => {
  const imgUrl = images[index];
  const item = document.createElement("div");
  item.classList.add("item");
  item.innerHTML = `<img src="${imgUrl}" />`;
  scrollContainer.value?.appendChild(item);
  return item;
};
const resetElements = () => {
  if (!scrollContainer.value) return;
  scrollContainer.value.innerHTML = "";
  const prevIndex = currentIndex - 1 < 0 ? images.length - 1 : currentIndex - 1;
  const nextIndex = currentIndex + 1 > images.length - 1 ? 0 : currentIndex + 1;
  createImg(prevIndex).classList.add("prev");
  createImg(currentIndex).classList.add("current");
  createImg(nextIndex).classList.add("next");
};
onMounted(() => {
  resetElements();
  let isAnimation = false;
  scrollContainer.value?.addEventListener("wheel", (event: WheelEvent) => {
    console.log(event);

    event.preventDefault();
    if (!event.deltaY) return;
    if (isAnimation) return;
    isAnimation = true;
    if (event.deltaY > 0) {
      scrollContainer.value?.classList.add("scroll-down");
      currentIndex =
        currentIndex + 1 > images.length - 1 ? 0 : currentIndex + 1;
    } else {
      scrollContainer.value?.classList.add("scroll-up");
      currentIndex =
        currentIndex - 1 < 0 ? images.length - 1 : currentIndex - 1;
    }
  });
  scrollContainer.value?.addEventListener("transitionend", () => {
    isAnimation = false;
    scrollContainer.value?.classList.remove("scroll-down");
    scrollContainer.value?.classList.remove("scroll-up");
    resetElements();
  });
});
</script>

<style lang="scss" scoped>
.scroll-container {
  --width: 400px;
  --height: 700px;

  @media (max-width: 768px) {
    --width: 250px;
    --height: 500px;
  }

  width: var(--width);
  height: var(--height);
  position: relative;
  overflow: hidden;
  cursor: n-resize;

  ::v-deep(.item) {
    position: absolute;
    width: var(--width);
    height: var(--height);
    overflow: hidden;
    transition: 1s ease-in-out;

    img {
      position: absolute;
      width: var(--width);
      height: var(--height);
      transition: 1s ease-in-out;
    }

    &.prev,
    &.next {
      z-index: 1;
      height: 0;
    }

    &.next {
      bottom: 0;

      img {
        bottom: 0;
        transform: translateY(10%);
      }
    }

    &.prev {
      img {
        transform: translateY(-10%);
      }
    }
  }
}

.scroll-up {
  ::v-deep(.item) {
    &.prev {
      height: 700px;

      img {
        transform: translateY(0);
      }
    }

    &.current {
      img {
        transform: translateY(10%);
      }
    }
  }
}

.scroll-down {
  ::v-deep(.item) {
    &.next {
      height: 700px;

      img {
        transform: translateY(0);
      }
    }

    &.current {
      img {
        transform: translateY(-10%);
      }
    }
  }
}
</style>

视频讲解