上班摸鱼时间使用vue3实现哔哩哔哩滚动视差banner,快来学习吧

28 篇文章 2 订阅

上班摸鱼时间使用vue3实现哔哩哔哩滚动视差banner

效果:

20240911

上班摸鱼时间使用vue3实现哔哩哔哩滚动视差banner,快来学习吧

代码解释

模板部分

<template>
  <div>
    <h1>Home Page</h1>
    <p>Go to <router-link to="/about">About</router-link></p>
    <div class="animated-banner animated-element" ref="animatedBanner">
      <!-- Multiple layers of images and one video -->
      <div class="layer">
        <img src="../assets/1.webp" data-height="187" data-width="2000" height="187" width="2000" />
      </div>
      <!-- ...more layers... -->
      <div class="layer">
        <video loop muted src="../assets/1.webm" autoplay width="180" height="100" data-height="100" data-width="180"></video>
      </div>
      <!-- ...more layers... -->
    </div>
  </div>
</template>

<div class="layer">:动画每一层的包装器。这包括多个<img>和一个<video>标签,其中的图片可以从bilibili 使用f12即可拿到如:
上班摸鱼时间使用vue3实现哔哩哔哩滚动视差banner,快来学习吧

脚本部分

<script lang="ts" setup>
import { ref, onMounted, onUnmounted, reactive } from "vue";
const animatedBanner = ref<HTMLDivElement | null>(null);
const bannerLeft = ref<number>(0);
const bannerWidth = ref<number>(0);
let initMouseLeft = ref<number>(0);
const styleMap = reactive<any>({});

animatedBanner:对元素的引用.animated-banner
bannerLeft:横幅的初始水平偏移(用于计算鼠标移动)。
bannerWidth:横幅的宽度。
initMouseLeft:鼠标进入Banner区域时的初始鼠标X位置。
styleMap:将每个层映射到其样式和初始状态的反应对象。

生命周期钩子

onMounted(() => {
  styleMap.value = {
    0: { /* Styles for layer 1 */ },
    // More layers...
    23: { /* Styles for layer 24 */ },
  };
  if (animatedBanner?.value) {
    bannerLeft.value = animatedBanner.value.offsetLeft;
    bannerWidth.value = animatedBanner.value.offsetWidth;
  } else {
    bannerLeft.value = 0;
    bannerWidth.value = 0;
  }
  init();
  if (animatedBanner.value) {
    animatedBanner.value.addEventListener("mouseenter", handleMouseEnter);
    animatedBanner.value.addEventListener("mouseleave", handleMouseLeave);
  }
});
onUnmounted(() => {
  if (animatedBanner.value) {
    animatedBanner.value.removeEventListener("mouseenter", handleMouseEnter);
    animatedBanner.value.removeEventListener("mouseleave", handleMouseLeave);
  }
});

onMounted:安装后初始化组件。
styleMap.value:设置各层的样式和初始配置。
bannerLeft和bannerWidth:用横幅的当前位置和宽度进行初始化。
init():将初始样式应用至各个图层。添加鼠标进入和离开事件的事件监听器。
onUnmounted:当组件被销毁时清理事件监听器。

方法

init():根据 将初始样式应用于每个图层styleMap

const init = () => {
  Object.keys(styleMap.value).forEach((item) => {
    const current = styleMap.value[item];
    const initStyle = current.initialStyle;
    current.element.style = `height: ${initStyle.height}; width: ${initStyle.width}; transform: translate(${initStyle.translateX}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale}); opacity: ${initStyle.opacity}; object-fit: ${initStyle.objectFit};`;
  });
};

handleMouseLeave(event):停止事件传播并调用clearListener()重置样式。

const handleMouseLeave = (event: any) => {
  event.stopPropagation();
  clearListener();
};

handleMouseEnter(event):停止事件传播,设置初始鼠标位置,并开始监听鼠标移动。

	const handleMouseEnter = (event: any) => {
	  event.stopPropagation();
	  initMouseLeft.value = event.pageX - bannerLeft.value;
	  startListener();
	};

calcutedPosition(mouseLeft, scale):根据鼠标位置和比例计算位置偏移。

const calcutedPosition = (mouseLeft: number, scale: number) => {
  return (-(mouseLeft - initMouseLeft.value) * scale) / bannerWidth.value;
};

clearListener():鼠标离开时重置各图层的样式。动画显示样式变化并恢复到初始状态。

const clearListener = () => {
  const mouseLeft = event.pageX - bannerLeft.value;
  Object.keys(styleMap.value).forEach((item) => {
    const current = styleMap.value[item];
    const style = current.style;
    const initStyle = current.initialStyle;
    const element = current.element;
    if (current.style) {
      const offset = calcutedPosition(mouseLeft, style.scale);
      let startValue = offset;
      let endValue = 0;
      let duration = 500;
      let interval = 10;
      let steps = duration / interval;
      let stepValue = (startValue - endValue) / steps;
      let currentValue = startValue;

      let timer = setInterval(() => {
        currentValue -= stepValue;
        let styleResult = `height: ${initStyle.height}; width: ${initStyle.width}; opacity: ${initStyle.opacity}; object-fit: ${initStyle.objectFit};`;
        if (style.direction === "y") {
          styleResult += `transform: translate(${initStyle.translateX}px, ${initStyle.translateY - currentValue}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
        } else {
          styleResult += `transform: translate(${initStyle.translateX - currentValue}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
        }
        if (Math.abs(currentValue - endValue) < Math.abs(stepValue)) {
          clearInterval(timer);
          styleResult = `transform: translate(${initStyle.translateX}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
        }
        element.style = styleResult;
      }, interval);
    }
  });
};

startListener()mousemove为横幅添加事件监听器,根据鼠标移动来更新各图层的样式。

const startListener = () => {
  animatedBanner.value?.addEventListener(
    "mousemove",
    function (event: { stopPropagation: () => void; pageX: number }) {
      event.stopPropagation();
      const mouseLeft = event.pageX - bannerLeft.value;
      Object.keys(styleMap.value).forEach((item) => {
        const current = styleMap.value[item];
        if (current.style) {
          const initStyle = current.initialStyle;
          const style = current.style;
          const element = current.element;
          const offset = calcutedPosition(mouseLeft, style.scale);
          let styleResult = `height: ${initStyle.height}; width: ${initStyle.width}; opacity: ${initStyle.opacity}; object-fit: ${initStyle.objectFit};`;
          if (style.direction === "y") {
            styleResult += `transform: translate(${initStyle.translateX}px, ${initStyle.translateY - offset}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
          } else {
            styleResult += `transform: translate(${initStyle.translateX - offset}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
          }
          element.style = styleResult;
        }
      });
    }
  );
};

进行图片样式重叠

<style scope>
body {
  margin: 0;
  padding: 0;
  position: relative;
}
.animated-banner {
  position: absolute;
  top: 150px;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
  min-width: 1000px;
  min-height: 155px;
  height: 9.375vw;
}
.animated-banner > .layer {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.animated-element {
  transition: transform 2s ease, opacity 0.5s ease;
}
</style>

全部代码

<template>
  <div>
    <h1>Home Page</h1>
    <p>Go to <router-link to="/about">About</router-link></p>
    <div class="animated-banner animated-element" ref="animatedBanner">
      <div class="layer">
        <img
          src="../assets/1.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/2.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/3.webp"
          data-height="187"
          data-width="2000"
          height="224"
          width="2400"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/4.webp"
          data-height="187"
          data-width="2000"
          height="205"
          width="2200"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/5.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/6.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/7.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/8.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/9.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/10.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/11.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/12.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/13.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/14.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/15.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/16.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/17.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/18.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/19.webp"
          data-height="187"
          data-width="2000"
          height="168"
          width="1800"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/20.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/21.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
      <div class="layer">
        <video
          loop
          muted
          src="../assets/1.webm"
          autoplay
          width="180"
          height="100"
          data-height="100"
          data-width="180"
        ></video>
      </div>
      <div class="layer">
        <img
          src="../assets/22.webp"
          data-height="187"
          data-width="2000"
          height="205"
          width="2200"
        />
      </div>
      <div class="layer">
        <img
          src="../assets/23.webp"
          data-height="187"
          data-width="2000"
          height="187"
          width="2000"
        />
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, onUnmounted, reactive } from "vue";

const animatedBanner = ref<HTMLDivElement | null>(null);

const bannerLeft = ref<number>(0);

const bannerWidth = ref<number>(0);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
let initMouseLeft = ref<number>(0);

const styleMap = reactive<any>({});

onMounted(() => {
  styleMap.value = {
    0: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: null,
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(1)")
        ?.querySelector("img"),
    },
    1: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "y",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(2)")
        ?.querySelector("img"),
    },
    2: {
      initialStyle: {
        height: "224.4px",
        width: "2400px",
        translateX: 300,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: null,
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(3)")
        ?.querySelector("img"),
    },
    3: {
      initialStyle: {
        height: "205.7px",
        width: "2200px",
        translateX: 330,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 50,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(4)")
        ?.querySelector("img"),
    },
    4: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: null,
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(5)")
        ?.querySelector("img"),
    },
    5: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(6)")
        ?.querySelector("img"),
    },
    6: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(7)")
        ?.querySelector("img"),
    },
    7: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 2,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(8)")
        ?.querySelector("img"),
    },
    8: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(9)")
        ?.querySelector("img"),
    },
    9: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: null,
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(10)")
        ?.querySelector("img"),
    },
    10: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 50,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(11)")
        ?.querySelector("img"),
    },
    11: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(12)")
        ?.querySelector("img"),
    },
    12: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 30,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(13)")
        ?.querySelector("img"),
    },
    13: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 30,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(14)")
        ?.querySelector("img"),
    },
    14: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: null,
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(15)")
        ?.querySelector("img"),
    },
    15: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 20,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(16)")
        ?.querySelector("img"),
    },
    16: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(17)")
        ?.querySelector("img"),
    },
    17: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: -100,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 20,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(18)")
        ?.querySelector("img"),
    },
    18: {
      initialStyle: {
        height: "168.3px",
        width: "1800px",
        translateX: -90,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 400,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(19)")
        ?.querySelector("img"),
    },
    19: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(20)")
        ?.querySelector("img"),
    },
    20: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 10,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(21)")
        ?.querySelector("img"),
    },
    21: {
      initialStyle: {
        height: "100px",
        width: "180px",
        translateX: -245,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
        objectFit: "cover",
      },
      style: null,
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(22)")
        ?.querySelector("video"),
    },
    22: {
      initialStyle: {
        height: "205.7px",
        width: "2200px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 200,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(23)")
        ?.querySelector("img"),
    },
    23: {
      initialStyle: {
        height: "187px",
        width: "2000px",
        translateX: 0,
        translateY: 0,
        rotate: 0,
        scale: 1,
        opacity: 1,
      },
      style: {
        direction: "x",
        scale: 200,
      },
      element: animatedBanner.value
        ?.querySelector(".layer:nth-child(24)")
        ?.querySelector("img"),
    },
  };
  if (animatedBanner?.value) {
    bannerLeft.value = animatedBanner.value.offsetLeft;
  } else {
    // 设置一个默认值,例如 0
    bannerLeft.value = 0;
  }
  if (animatedBanner?.value) {
    bannerWidth.value = animatedBanner?.value?.offsetWidth;
  } else {
    // 设置一个默认值,例如 0
    bannerWidth.value = 0;
  }

  init();
  if (animatedBanner.value) {
    animatedBanner.value.addEventListener("mouseenter", handleMouseEnter);
    animatedBanner.value.addEventListener("mouseleave", handleMouseLeave);
  }
});

onUnmounted(() => {
  if (animatedBanner.value) {
    animatedBanner.value.removeEventListener("mouseenter", handleMouseEnter);
    animatedBanner.value.removeEventListener("mouseleave", handleMouseLeave);
  }
});
const init = () => {
  Object.keys(styleMap.value).forEach((item) => {
    const current = styleMap.value[item];
    const initStyle = current.initialStyle;
    current.element.style = `height: ${initStyle.height}; width: ${initStyle.width}; transform: translate(${initStyle.translateX}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale}); opacity: ${initStyle.opacity}; object-fit: ${initStyle.objectFit}`;
  });
};
const handleMouseLeave = (event: any) => {
  event.stopPropagation();
  // 还原
  clearListener();
};
const handleMouseEnter = (event: any) => {
  event.stopPropagation();
  // 计算初始鼠标 x 位置
  initMouseLeft.value = event.pageX - bannerLeft.value;
  // 开始监听偏移量
  startListener();
};
const calcutedPosition = (mouseLeft: number, scale: number) => {
  return (-(mouseLeft - initMouseLeft.value) * scale) / bannerWidth.value;
};
const clearListener = () => {
  const mouseLeft = event.pageX - bannerLeft.value;
  // // 确保样式清除或复位
  // if (animatedBanner.value) {
  //   animatedBanner.value.style.transform = `translate(0, 0) rotate(0deg) scale(1)`;
  //   animatedBanner.value.style.opacity = '1';

  // }
  Object.keys(styleMap.value).forEach((item) => {
    const current = styleMap.value[item];
    const style = current.style;
    const initStyle = current.initialStyle;
    const element = current.element;
    // if (current.element) {
    //   current.element.style.transform = `translate(${initStyle.translateX}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale})`;
    //   current.element.style.opacity = initStyle.opacity;
    // }
    if (current.style) {
      // 计算偏移
      const offset = calcutedPosition(mouseLeft, style.scale);
      let startValue = offset;
      let endValue = 0;
      let duration = 500; // 总时间,单位为毫秒
      let interval = 10; // 每次更新的间隔时间,单位为毫秒
      let steps = duration / interval; // 总步数
      let stepValue = (startValue - endValue) / steps; // 每一步的值变化量

      let currentValue = startValue;

      let timer = setInterval(() => {
        currentValue -= stepValue;
        let styleResult = `height: ${initStyle.height}; width: ${initStyle.width}; opacity: ${initStyle.opacity}; object-fit: ${initStyle.objectFit};`;

        if (style.direction === "y") {
          styleResult += `transform: translate(${initStyle.translateX}px, ${
            initStyle.translateY - currentValue
          }px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
        } else {
          styleResult += `transform: translate(${
            initStyle.translateX - currentValue
          }px, ${initStyle.translateY}px) rotate(${
            initStyle.rotate
          }deg) scale(${initStyle.scale});`;
        }

        if (Math.abs(currentValue - endValue) < Math.abs(stepValue)) {
          clearInterval(timer);
          styleResult = `transform: translate(${initStyle.translateX}px, ${initStyle.translateY}px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
        }
        element.style = styleResult;
      }, interval);
    }
  });
};
const startListener = () => {
  animatedBanner.value?.addEventListener(
    "mousemove",
    function (event: { stopPropagation: () => void; pageX: number }) {
      event.stopPropagation();
      const mouseLeft = event.pageX - bannerLeft.value;
      Object.keys(styleMap.value).forEach((item) => {
        const current = styleMap.value[item];
        if (current.style) {
          const initStyle = current.initialStyle;
          const style = current.style;
          const element = current.element;
          // 计算偏移
          const offset = calcutedPosition(mouseLeft, style.scale);
          let styleResult = `height: ${initStyle.height}; width: ${initStyle.width}; opacity: ${initStyle.opacity}; object-fit: ${initStyle.objectFit};`;

          if (style.direction === "y") {
            styleResult += `transform: translate(${initStyle.translateX}px, ${
              initStyle.translateY - offset
            }px) rotate(${initStyle.rotate}deg) scale(${initStyle.scale});`;
          } else {
            styleResult += `transform: translate(${
              initStyle.translateX - offset
            }px, ${initStyle.translateY}px) rotate(${
              initStyle.rotate
            }deg) scale(${initStyle.scale});`;
          }
          element.style = styleResult;
        }
      });
    }
  );
};
</script>
<style scope>
body {
  margin: 0;
  padding: 0;
  position: relative;
}
.animated-banner {
  position: absolute;
  top: 150px;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
  min-width: 1000px;
  min-height: 155px;
  height: 9.375vw;
}

.animated-banner > .layer {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.animated-element {
  transition: transform 2s ease, opacity 0.5s ease;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值