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

一、过程分析

先上效果:

在这里插入图片描述

在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;。

效果如下:

在这里插入图片描述

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

在这里插入图片描述

.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的背景色去掉。效果如下:

在这里插入图片描述

看到这里有的人可能已经知道这个中间层用来干什么的了。没错,这不就是一个渐变高亮的效果嘛!我们再在 .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;
}

效果如下:

在这里插入图片描述

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

在这里插入图片描述

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

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

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

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

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

在这里插入图片描述

代码如下:

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>
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值