一个鼠标滑过的样式~

🌐 原地址 👉 h3

🫰 demo 🫰

🧐 思路分析 🧐

这样看是不是一目了然呢~ 😏

如上👆gif👆效果可以理解为👉 以鼠标位置为圆心,产生的背景圆,与box的间隙产生的交叉

❓ 这么实现会不会有问题呢 ❓

  • 效果只在boxes区域出现,是不是需要判断鼠标位置来添加粉色背景圆呢 ❓
  • 而且这个只有在接触到 box 才会有 粉色背景圆,box 以外的部分是没有颜色的,这个又如何解决呢 ❓
  • 或许也能实现,应该会麻烦些 :)

🧐 不妨换个思路 🧐

给每个box添加背景圆,背景圆位置 根据鼠标位置变化,👇 如下所示 👇

背景圆大小固定(比如200px),圆心位置如何确定呢?

👉 初始位置 (0,0) ,参照系则是参考box左上角;

👉 动态变化的位置取(clientX - left, clientY - top)。left 和 top 即 box 元素相对浏览器视口的位置,通过 getBoundingClientRect 方法获取

👉 取差值(clientX - left, clientY - top)也很好理解,因为伪元素位置是参照box左上的位置变化,这样就能在 差值(绝对值) < 半径 的时候出现在 box间隙内

🌟 关键点 🌟

  • 盒子元素 box 添加伪元素 before,设置伪元素宽高均大于父元素,效果上类似于伪元素覆盖了box,同时设置偏移量 inset为负值,实现 "居中覆盖"(这样就能留出一个"空隙", 即👆gif👆中粉色圆填充before 与 box 中间空白的部分)
  • 给伪元素背景设置背景色,demo中用的是 径向渐变,渐变的形状为200px 的圆形,圆心位置记为 --x 和 --y,通过css变量传入,颜色自定义即可(demo中采用的是rgba(245,158,11,.7) 到 transparent 的渐变)不用粉色了🤣

👀 关于--x 和 --y 的获取 👀

  • 记录鼠标位置 (mouseX, mouseY)
  • calBoxesPosition方法获取每个box 的位置 (left,top) 并记录差值 (mouseX - left, mouseY - top)
  • (mouseX, mouseY) 变动的时候重新触发 calBoxesPosition 方法即可

🚀 关于一些优化 🚀

  • 第一次页面加载调用 calBoxesPosition 后,在不滑动页面的情况下,每个box位置相对固定,可以缓存下来位置信息,避免该函数内部频繁调用 getBoundingClientRect 引发的性能问题造成卡顿
  • 在滑动页面的时候,可以将记录box位置信息的字段重置为(0,0),再移动鼠标重新触发 calBoxesPosition 即可

👨‍💻代码(vue3实现)👨‍💻

PS: 不太会使用掘金的代码片段,不知道如何引入第三方库😅,如果验证代码, @vueuse/core 和 tailwindcss请自行安装🫠

(等我查一下怎么使用,再回来贴个代码片段~ ⏰@ 4-17 14:56 )

  1. template 结构
js 复制代码
<template>
  <div
    class="w-full h-full p-8 bg-white dynamic-bg-page overflow-auto"
    ref="pageRef"
  >
    <!-- 顶部元素(测试滚动) -->
    <div class="w-full h-[200px] bg-slate-500"></div>

    <!-- 鼠标位置 -->
    <p class="text-center mb-4 font-bold text-xl">
      mouseX: {{ mouseX }}, mouseY: {{ mouseY }}
    </p>

    <!-- boxes -->
    <div
      class="box-container grid grid-cols-3 gap-8"
      :style="{
        '--border-color': 'rgb(229,229,229)',
        '--bg-color': 'rgba(245,158,11,.7)',
      }"
    >
      <div
        v-for="(_, i) in boxes"
        :key="i"
        :id="_.name"
        class="box flex items-center justify-center p-[1px] rounded-xl"
        :style="{
          '--x': `${_.elementX}px`,
          '--y': `${_.elementY}px`,
        }"
      >
        <div
          class="box_inner w-full h-full bg-white rounded-xl hover:bg-opacity-70 transition-[background-opacity]"
        ></div>
      </div>
    </div>
    <!-- 底部元素(测试滚动) -->
    <div class="w-full h-[1000px] bg-slate-500"></div>
  </div>
</template>
  1. css样式
js 复制代码
<style lang="scss" scoped>
.box {
  width: 100%;
  height: 200px;
  border: 1px solid var(--border-color);
  border-radius: 12px;

  &::before {
    position: absolute;
    display: block;
    content: '';
    width: calc(100% + 4px);
    height: calc(100% + 4px);
    border-radius: 12px;
    inset: -2px;
    background: radial-gradient(
      200px circle at var(--x) var(--y),
      var(--bg-color) 0,
      transparent 100%
    );
    will-change: background;
  }
}
</style>
  1. js 部分
js 复制代码
<script setup lang="ts">
import { useMouse, useElementBounding } from '@vueuse/core'
import { nextTick, onBeforeMount, onMounted, ref, watch } from 'vue'

const NUM_BOXES = 10
const boxes = ref<any[]>([])
const pageRef = ref<HTMLElement | null>(null)

const { x: mouseX, y: mouseY } = useMouse()

const getBoxPosition = (box: HTMLElement) => {
  const { left, top } = useElementBounding(box)
  return { left, top }
}

const calBoxesPosition = () => {
  boxes.value.forEach((_, i) => {
    const box = document.getElementById(_.name) as HTMLElement
    if (_.top && _.left) {
      // 🚀 cached
    } else {
      const { left, top } = getBoxPosition(box)
      // 🚀 缓存下元素的位置信息
      _.left = left.value
      _.top = top.value
    }
    boxes.value[i] = {
      ..._,
      elementX: mouseX.value - _.left,
      elementY: mouseY.value - _.top,
    }
  })
}
watch([mouseX, mouseY], () => {
  calBoxesPosition()
})
onBeforeMount(() => {
  boxes.value = Array.from({ length: NUM_BOXES }, (_, i) => {
    return {
      name: `box-${i + 1}`,
      elementX: 0, // --x
      elementY: 0, // --y
      left: 0, // element bounding left
      top: 0, // element bounding top
    }
  })
})
onMounted(() => {
  nextTick(() => {
    calBoxesPosition()
  })
  pageRef.value?.addEventListener('scroll', () => {
    boxes.value.forEach((_, i) => {
      _.left = 0
      _.top = 0
    })
  })
})
</script>
相关推荐
王大师王文峰19 小时前
HTML 入门到进阶:语义化标签、表单与多媒体一篇打通【语义化布局、表单校验、video/audio、Canvas、无障碍与 SEO】
前端·css·node.js·html·css3·jquery·html5
晓得迷路了2 天前
栗子前端技术周刊第 150 期 - Stack Overflow 开发者调查结果、TanStack Start 1.0...
javascript·css·后端
ShineWinsu3 天前
对于CSS:弹性布局(Flexbox)的解析
前端·css·面试·浏览器·web·网页·弹性布局
云老大-阿里云国际站代理商3 天前
华为云国际站渠道代理商:CSS查询很慢但CPU不高?磁盘I/O、JVM与分片问题怎么排查
css·jvm·华为云
定栓4 天前
Webpack迁到Vite第三节:Less 与 CSS 处理
css·webpack·less
ShineWinsu4 天前
对于JavaScript:初识JavaScript以及输入输出
java·开发语言·前端·javascript·css·面试·html
码银4 天前
【web】基于HTML/CSS,JavaScript + Three.js做3D流程图
前端·css·html
keyipatience4 天前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
酷炫码神4 天前
Day 2|CSS 盒模型:把昨天的骨架,变成能看的页面
前端·css
oicola5 天前
Ant Design v5 vs Tailwind CSS:组件库与样式框架的终极对决
前端·css