CSS+JS实现一个鼠标移动的高亮边框效果

[复制链接]
七夏(UID:1) 发表于 2024-12-30 12:28:04 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

一、过程分析

先上效果:

图片

▲ 效果gif

在Windows系统里有一个很棒的细节效果,元素的渐变高亮边框是可以感知鼠标的,边框的高亮部分会跟随鼠标的移动而移动。

这种效果也是比较常见的,但是实现起来还是需要一点时间和思路的。

首先,我们先完成基础的布局。代码如下:

<div class="container">
    <div class="card">
        <div class="inner">
            <p>黄鹤楼送孟浩然之广陵</p>
            <p>唐 · 李白</p>
            <p>故人西辞黄鹤楼,烟花三月下扬州。</p>
            <p>孤帆远影碧空尽,唯见长江天际流。</p>
        </div>
    </div>
    <div class="card">
        <div class="inner">
            <p>送元二使安西</p>
            <p>唐 · 王维</p>
            <p>渭城朝雨浥轻尘,客舍青青柳色新。</p>
            <p>劝君更尽一杯酒,西出阳关无故人。</p>
        </div>
    </div>
    <div class="card">
        <div class="inner">
            <p>春日</p>
            <p>宋 · 朱熹</p>
            <p>胜日寻芳泗水滨,无边光景一时新。</p>
            <p>等闲识得东风面,万紫千红总是春。</p>
        </div>
    </div>
    <div class="card">
        <div class="inner">
            <p>小池</p>
            <p>唐 · 杨万里</p>
            <p>泉眼无声惜细流,树阴照水爱晴柔。</p>
            <p>小荷才露尖尖角,早有蜻蜓立上头。</p>
        </div>
    </div>
    <div class="card">
        <div class="inner">
            <p>村居</p>
            <p>清 · 高鼎</p>
            <p>草长莺飞二月天,拂堤杨柳醉春烟。</p>
            <p>儿童散学归来早,忙趁东风放纸鸢。</p>
        </div>
    </div>
    <div class="card">
        <div class="inner">
            <p>晓出净慈寺送林子方</p>
            <p>唐 · 杨万里</p>
            <p>毕竟西湖六月中,风光不与四时同。</p>
            <p>接天莲叶无穷碧,映日荷花别样红。</p>
        </div>
    </div>
</div>
body {
    background-color: black;
}
p {
    margin: 0;
    line-height: 2;
}
.container {
    display: grid;
    width: 100%;
    margin-top: 2rem;
    color: #f0f0f0;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    text-align: center;
}
.card {
    aspect-ratio: 4/2;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.2);
}
.inner {
    background: #222;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

接下来给 .card 加上相对定位position: relative属性,给 .inner 加上绝对定位position: absolute属性,让card盒子和inner盒子完全重叠在一起。

.card {
    aspect-ratio: 4/2;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.2);
    position: relative;
}
.inner {
    position: absolute;
    inset: 10px;
    background: #222;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

为了方便观察,给 .inner 设置 inset: 10px 属性,此属性相当于 left: 10px;top: 10px;right: 10px; bottom: 10px;。

效果如下:

图片

▲ 效果图-1

想要实现高亮的效果,我们还需要在 .card 和 .inner 之间再插入一层,这一层是我们实现高亮的关键!这三层盒子一样大小。

图片

▲ 分析图-1

.card::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(closest-side circle, #fff, transparent);
}
.inner {
    position: absolute;
    inset: 10px;
    /* background: #222; */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

为了便于观察,先把.inner的背景色去掉。效果如下:

图片

▲ 效果图-2

看到这里有的人可能已经知道这个中间层用来干什么的了。没错,这不就是一个渐变高亮的效果嘛!我们再在 .card::before 加上一个 transform属性,同时在 .card 上加上 overflow: hidden属性,观察浏览器,神奇的效果出现了!我悟了!

.card {
    aspect-ratio: 4/2;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.2);
    position: relative;
    overflow: hidden;
}
.card::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(closest-side circle, #fff, transparent);
    transform: translate(70px, 80px);
}
.inner {
    position: absolute;
    inset: 10px;
    background: #222;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

效果如下:

图片

▲ 效果图-3

原来如此,一个渐变高亮的边框效果不就实现了嘛!

图片

接下来就是用js代码实现 .card::before 中间层跟随鼠标移动即可。

图片

▲ 分析图-2

假如图中的小圆圈为鼠标位置,当鼠标移动到此位置时,只需要计算把每张卡片中间层的中心偏移到鼠标的位置即可。那怎么去计算呢?

首先,获取到每张卡片的元素,监听鼠标移动onmousemove事件,鼠标移动时循环遍历每个卡片元素给每个卡片设置transform: translate(x, y)即可。

x(偏移的水平距离) = 鼠标离视口的水平距离 - 元素离视口的水平距离 - 元素宽度的一半。

y(偏移的垂直距离) = 鼠标离视口的垂直距离 - 元素离视口的垂直距离 - 元素高度的一半。

图片

▲ 分析图-3

代码如下:

const container = document.querySelector('.container');
const cards = document.querySelectorAll('.card');

container.onmousemove = e => {
    for(const card of cards) {
        const rect = card.getBoundingClientRect();
        const x = e.clientX - rect.left - rect.width / 2;
        const y = e.clientY - rect.top - rect.height / 2;
        card.style.setProperty('--x', `${x}px`);
        card.style.setProperty('--y', `${y}px`);
    }
}

二、完整代码

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>b.html</title>
  <style type="text/css">
   body {
    background-color: black;
   }
   p {
    margin: 0;
    line-height: 2;
   }
   .container {
    display: grid;
    width: 90%;
    margin: 2rem auto 0;
    color: #f0f0f0;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    text-align: center;
   }
   .card {
    aspect-ratio: 4/2;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.2);
    position: relative;
    overflow: hidden;
   }
   .card::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(closest-side circle, #fff, transparent);
    /* 偏移到看不到的地方 */
    transform: translate(var(--x, -10000px), var(--y, -10000px));
   }
   .inner {
    position: absolute;
    inset: 10px;
    background: #222;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
   }
  </style>
 </head>
 <body>
  <div class="container">
   <div class="card">
    <div class="inner">
     <p>黄鹤楼送孟浩然之广陵</p>
     <p>唐 · 李白</p>
     <p>故人西辞黄鹤楼,烟花三月下扬州。</p>
     <p>孤帆远影碧空尽,唯见长江天际流。</p>
    </div>
   </div>
   <div class="card">
    <div class="inner">
     <p>送元二使安西</p>
     <p>唐 · 王维</p>
     <p>渭城朝雨浥轻尘,客舍青青柳色新。</p>
     <p>劝君更尽一杯酒,西出阳关无故人。</p>
    </div>
   </div>
   <div class="card">
    <div class="inner">
     <p>春日</p>
     <p>宋 · 朱熹</p>
     <p>胜日寻芳泗水滨,无边光景一时新。</p>
     <p>等闲识得东风面,万紫千红总是春。</p>
    </div>
   </div>
   <div class="card">
    <div class="inner">
     <p>小池</p>
     <p>唐 · 杨万里</p>
     <p>泉眼无声惜细流,树阴照水爱晴柔。</p>
     <p>小荷才露尖尖角,早有蜻蜓立上头。</p>
    </div>
   </div>
   <div class="card">
    <div class="inner">
     <p>村居</p>
     <p>清 · 高鼎</p>
     <p>草长莺飞二月天,拂堤杨柳醉春烟。</p>
     <p>儿童散学归来早,忙趁东风放纸鸢。</p>
    </div>
   </div>
   <div class="card">
    <div class="inner">
     <p>晓出净慈寺送林子方</p>
     <p>唐 · 杨万里</p>
     <p>毕竟西湖六月中,风光不与四时同。</p>
     <p>接天莲叶无穷碧,映日荷花别样红。</p>
    </div>
   </div>
  </div>
 </body>
 <script type="text/javascript">
  const container = document.querySelector('.container');
  const cards = document.querySelectorAll('.card');
  
  container.onmousemove = e => {
   for(const card of cards) {
    const rect = card.getBoundingClientRect();
    const x = e.clientX - rect.left - rect.width / 2;
    const y = e.clientY - rect.top - rect.height / 2;
    card.style.setProperty('--x', `${x}px`);
    card.style.setProperty('--y', `${y}px`);
   }
  }
 </script>
</html>
小时候,看腻了农村的牛和马,长大后,来到了城里,才知道原来到处都是牛马!
全部回复0 显示全部楼层
暂无回复,精彩从你开始!

快速回帖

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于楼主

管理员
  • 主题

    924
  • 回答

    359
  • 积分

    2515
虚位以待,此位置招租

商务推广

    此位置招租 黑粉猫影院-免费看电影 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租