实现bilibili哔哩哔哩banner多图片基于鼠标移动(vue)

部署了一个DEMO,先来看看效果👇

二选一
https://bilibili-banner-qsvs1m9vv-ifavcodes-projects.vercel.app/
https://www.guetzjb.cn/bilibili_banner/#/Bilibili

大致思路:

  1. 鼠标移入记录一个X偏移,赋值给enterX变量
  2. 鼠标移动计算当前鼠标x位置和enterX的差值
  3. 根据算出来的差值偏移图片
  4. 设计一个偏移阈值,让多张图片移动产生视觉偏差,同时管理好图层顺序。

图片资源可以F12—网络—图片里抓取下载。

2024-07-29 banner图

定义好格式,背景和装饰元素分开,因为背景偏移少一些

{
    url: getAssetsImages('xxx.webp'),
    offsetThreshold: 10,// 偏移阈值(越大,左右移动越快)
}

图片在src/assets里面(bg1 ~ bg18)

为什么要分背景元素和装饰元素?

看下面这个图

在这里插入图片描述

背景元素比视图层大一些,因为这是要左右滑动的,而装饰元素则没有这个限制,怎么移动都可以。

看代码实现蛮简单的~包会

<script lang="ts" setup>
// 获取本地图片
function getAssetsImages(name: string) {
  return new URL(`/src/assets/${name}`, import.meta.url).href;
}

// 最外层元素
const eleRef = ref()
// 背景图,下面的会盖住上面的
const bgList = ref([
  {
    url: getAssetsImages('bg2.webp'),
    offsetThreshold: 10,
  },
  {
    url: getAssetsImages('bg7.webp'),
    offsetThreshold: 90,
  },
  {
    url: getAssetsImages('bg11.webp'),
    offsetThreshold: 95,
  },
  {
    url: getAssetsImages('bg14.webp'),
    offsetThreshold: 80,
  },
  {
    url: getAssetsImages('bg9.webp'),
    offsetThreshold: 50,
  },
  {
    url: getAssetsImages('bg1.webp'),
    offsetThreshold: 90,
  },
])

// 装饰元素
const imgList = ref([
  {
    url: getAssetsImages('bg15.webp'),
    offsetThreshold: 70,
  },
  {
    url: getAssetsImages('bg16.webp'),
    offsetThreshold: 90,
  },
  {
    url: getAssetsImages('bg17.webp'),
    offsetThreshold: 30,
  },
  {
    url: getAssetsImages('bg3.webp'),
    offsetThreshold: 150,
  },
  {
    url: getAssetsImages('bg4.webp'),
    offsetThreshold: 50,
  },
  {
    url: getAssetsImages('bg5.webp'),
    offsetThreshold: 60,
  },
  {
    url: getAssetsImages('bg6.webp'),
    offsetThreshold: 100,
  },
  {
    url: getAssetsImages('bg8.webp'),
    offsetThreshold: 80,
  },
  {
    url: getAssetsImages('bg10.webp'),
    offsetThreshold: 60,
  },
  {
    url: getAssetsImages('bg12.webp'),
    offsetThreshold: 20,
  },
  {
    url: getAssetsImages('bg13.webp'),
    offsetThreshold: 10,
  },
  {
    url: getAssetsImages('bg18.webp'),
    offsetThreshold: 30,
  },
])

// 背景平移量
const bgTransform = ref<string[]>([])
// 装饰元素平移量
const imgTransform = ref<string[]>([])
const isEnter = ref<boolean>(false) // 鼠标是否移入
// 首次进入的X坐标
let enterX = 0

const mouseEnter = (e: MouseEvent) => {
  initPosition()
  isEnter.value = true
  enterX = e.x
}

const mouseLeave = (_e: MouseEvent) => {
  isEnter.value = false
  initPosition()
}

const mouseMove = (e: MouseEvent) => {
  // (当前x坐标 - 初始x位置) / dom宽度
  const xOffset = (e.x - enterX) / eleRef.value.clientWidth * 100
  // 背景移动
  for (let i = 0; i < bgList.value.length; i++) {
    let v = xOffset * (bgList.value[i].offsetThreshold / 100) // 根据偏移阈值计算
    bgTransform.value[i] = `translateX(${-100 + v}px)` // 初始偏移量为 100,这样设置可以在[-200,0]之间移动,之后绑定到dom上
  }

  // 装饰物移动
  for (let i = 0; i < imgList.value.length; i++) {
    let v = xOffset * (imgList.value[i].offsetThreshold / 100)
    imgTransform.value[i] = `translateX(${v}px)`
  }
}

// 初始化位置
const initPosition = () => {
  bgTransform.value = new Array(bgList.value.length).fill('translateX(-100px)')
  imgTransform.value = new Array(imgList.value.length).fill('translateX(0px)')
}

</script>

<template>
  <div ref="eleRef" class="relative w-screen max-w-[2480px] h-[155px] overflow-hidden" @mouseenter="mouseEnter"
    @mouseleave="mouseLeave" @mousemove="mouseMove">
    <!-- 默认所有图片绝对定位在左上角,再利用translateX偏移位置 -->
    <!-- 背景元素的宽度比父元素大200px,用于左右偏移 -->
    <!-- isEnter为false时,引入过渡动画,还原图片原先的位置 -->
    <div :class="isEnter ? '' : 'transition-transform duration-200'" class="absolute left-0 top-0 translate-x-[-100px]"
      :style="{ transform: bgTransform[idx], width: eleRef ? eleRef.clientWidth + 200 + 'px' : '100%' }"
      v-for="(img, idx) in bgList" :key="idx">
      <img class="w-full object-cover" :src="img.url" alt="">
    </div>
	<!-- 装饰元素 -->
    <div :class="isEnter ? '' : 'transition-transform duration-200'" class="absolute left-0 top-0"
      :style="{ transform: imgTransform[idx] }" v-for="(img, idx) in imgList" :key="idx + bgList.length">
      <img :src="img.url" alt="">
    </div>
  </div>
</template>

<style scoped></style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你熬夜了吗?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值