七夏 发表于 2024-12-30 12:28:04

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

<p><strong>一、过程分析</strong></p>
<p>先上效果:</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_gif/gehCsfcwMobjlGtYP046Xj4vyrLd6cCmDGpGxB2x41ianCMDoHxQ6IZGVtLicL4kZJLIrQGPUAb7kJTWpcDnngTw/640?wx_fmt=gif&amp;random=0.5042881815225959&amp;random=0.014529392574000655&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 效果gif</p>
<p>在Windows系统里有一个很棒的细节效果,元素的渐变高亮边框是可以感知鼠标的,边框的高亮部分会跟随鼠标的移动而移动。</p>
<p>这种效果也是比较常见的,但是实现起来还是需要一点时间和思路的。</p>
<p>首先,我们先完成基础的布局。代码如下:</p>
<pre><code>&lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;inner&quot;&gt;
            &lt;p&gt;黄鹤楼送孟浩然之广陵&lt;/p&gt;
            &lt;p&gt;唐 · 李白&lt;/p&gt;
            &lt;p&gt;故人西辞黄鹤楼,烟花三月下扬州。&lt;/p&gt;
            &lt;p&gt;孤帆远影碧空尽,唯见长江天际流。&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;inner&quot;&gt;
            &lt;p&gt;送元二使安西&lt;/p&gt;
            &lt;p&gt;唐 · 王维&lt;/p&gt;
            &lt;p&gt;渭城朝雨浥轻尘,客舍青青柳色新。&lt;/p&gt;
            &lt;p&gt;劝君更尽一杯酒,西出阳关无故人。&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;inner&quot;&gt;
            &lt;p&gt;春日&lt;/p&gt;
            &lt;p&gt;宋 · 朱熹&lt;/p&gt;
            &lt;p&gt;胜日寻芳泗水滨,无边光景一时新。&lt;/p&gt;
            &lt;p&gt;等闲识得东风面,万紫千红总是春。&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;inner&quot;&gt;
            &lt;p&gt;小池&lt;/p&gt;
            &lt;p&gt;唐 · 杨万里&lt;/p&gt;
            &lt;p&gt;泉眼无声惜细流,树阴照水爱晴柔。&lt;/p&gt;
            &lt;p&gt;小荷才露尖尖角,早有蜻蜓立上头。&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;inner&quot;&gt;
            &lt;p&gt;村居&lt;/p&gt;
            &lt;p&gt;清 · 高鼎&lt;/p&gt;
            &lt;p&gt;草长莺飞二月天,拂堤杨柳醉春烟。&lt;/p&gt;
            &lt;p&gt;儿童散学归来早,忙趁东风放纸鸢。&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;inner&quot;&gt;
            &lt;p&gt;晓出净慈寺送林子方&lt;/p&gt;
            &lt;p&gt;唐 · 杨万里&lt;/p&gt;
            &lt;p&gt;毕竟西湖六月中,风光不与四时同。&lt;/p&gt;
            &lt;p&gt;接天莲叶无穷碧,映日荷花别样红。&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code>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;
}
</code></pre>
<p>接下来给 .card 加上相对定位position: relative属性,给 .inner 加上绝对定位position: absolute属性,让card盒子和inner盒子完全重叠在一起。</p>
<pre><code>.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;
}
</code></pre>
<p>为了方便观察,给 .inner 设置 inset: 10px 属性,此属性相当于 left: 10px;top: 10px;right: 10px; bottom: 10px;。</p>
<p>效果如下:</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_png/gehCsfcwMobjlGtYP046Xj4vyrLd6cCm5HbicKibuxxIicsHOoPLVD3FNZz7TzK8lz2CFfNia4tgzoRpkLDfMwnN6A/640?wx_fmt=png&amp;from=appmsg&amp;random=0.4686790933268461&amp;random=0.9332697537922849&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 效果图-1</p>
<p>想要实现高亮的效果,我们还需要在 .card 和 .inner 之间再插入一层,这一层是我们实现高亮的关键!这三层盒子一样大小。</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_png/gehCsfcwMobjlGtYP046Xj4vyrLd6cCmWSqzsZs5EicN6nlcwQX3F1iaictRLy1lUa7Sahg8WQFrib3UCLicbCdmVOA/640?wx_fmt=png&amp;from=appmsg&amp;random=0.9034532048316761&amp;random=0.7457317091936968&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 分析图-1</p>
<pre><code>.card::before {
    content: &quot;&quot;;
    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;
}
</code></pre>
<p>为了便于观察,先把.inner的背景色去掉。效果如下:</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_png/gehCsfcwMobjlGtYP046Xj4vyrLd6cCmx1EUszzduLRr40XmfPzy7FtrIPoVGdIDE4Bb7JITqLdxUvy6biayc9A/640?wx_fmt=png&amp;from=appmsg&amp;random=0.76464400824009&amp;random=0.2871741644663035&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 效果图-2</p>
<p>看到这里有的人可能已经知道这个中间层用来干什么的了。没错,这不就是一个渐变高亮的效果嘛!我们再在 .card::before 加上一个 transform属性,同时在 .card 上加上 overflow: hidden属性,观察浏览器,神奇的效果出现了!我悟了!</p>
<pre><code>.card {
    aspect-ratio: 4/2;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.2);
    position: relative;
    overflow: hidden;
}
.card::before {
    content: &quot;&quot;;
    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;
}
</code></pre>
<p>效果如下:</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_png/gehCsfcwMobjlGtYP046Xj4vyrLd6cCmLxSG5hMsiakPsP5KFalErwBDqbjJjLDN5NqMqvp3GStBibJEkY8CM2aw/640?wx_fmt=png&amp;from=appmsg&amp;random=0.589553017691788&amp;random=0.5636504798045696&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 效果图-3</p>
<p>原来如此,一个渐变高亮的边框效果不就实现了嘛!</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_jpg/gehCsfcwMobjlGtYP046Xj4vyrLd6cCmNvvcG1co2RBCaXicKYcH4gOt4cdARXWmUTlJAcX6oZUzvbGcibgfbicow/640?wx_fmt=jpeg&amp;from=appmsg&amp;random=0.3710983645297914&amp;random=0.10759875114052786&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>接下来就是用js代码实现 .card::before 中间层跟随鼠标移动即可。</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_png/gehCsfcwMoYiaFJYbiaqWiakt8l4rtdib0ibLfoXR3JSR9uS02wclNlFThUvnGgMJKFZFuaQYhHzIzLcMPWB7Eorujg/640?wx_fmt=png&amp;from=appmsg&amp;random=0.8023980876990735&amp;random=0.6805696664258039&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 分析图-2</p>
<p>假如图中的小圆圈为鼠标位置,当鼠标移动到此位置时,只需要计算把每张卡片中间层的中心偏移到鼠标的位置即可。那怎么去计算呢?</p>
<p>首先,获取到每张卡片的元素,监听鼠标移动onmousemove事件,鼠标移动时循环遍历每个卡片元素给每个卡片设置transform: translate(x, y)即可。</p>
<p>x(偏移的水平距离) = 鼠标离视口的水平距离 - 元素离视口的水平距离 - 元素宽度的一半。</p>
<p>y(偏移的垂直距离) = 鼠标离视口的垂直距离 - 元素离视口的垂直距离 - 元素高度的一半。</p>
<p><img src="https://www.3bbs.cn/index-diy/img.php?url=https://mmbiz.qpic.cn/sz_mmbiz_jpg/gehCsfcwMoYiaFJYbiaqWiakt8l4rtdib0ibLJuUxhmh2qEWQjT01BKaHlib4xrO61qHo0gLqY1pg4ZdEBQUr65ic2Mcg/640?wx_fmt=jpeg&amp;random=0.9396160109379155&amp;random=0.6828483471130933&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" /></p>
<p>▲ 分析图-3</p>
<p>代码如下:</p>
<pre><code>const container = document.querySelector('.container');
const cards = document.querySelectorAll('.card');

container.onmousemove = e =&gt; {
    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`);
    }
}
</code></pre>
<p><strong>二、完整代码</strong></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
 &lt;head&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;title&gt;b.html&lt;/title&gt;
  &lt;style type=&quot;text/css&quot;&gt;
   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: &quot;&quot;;
    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;
   }
  &lt;/style&gt;
 &lt;/head&gt;
 &lt;body&gt;
  &lt;div class=&quot;container&quot;&gt;
   &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
     &lt;p&gt;黄鹤楼送孟浩然之广陵&lt;/p&gt;
     &lt;p&gt;唐 · 李白&lt;/p&gt;
     &lt;p&gt;故人西辞黄鹤楼,烟花三月下扬州。&lt;/p&gt;
     &lt;p&gt;孤帆远影碧空尽,唯见长江天际流。&lt;/p&gt;
    &lt;/div&gt;
   &lt;/div&gt;
   &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
     &lt;p&gt;送元二使安西&lt;/p&gt;
     &lt;p&gt;唐 · 王维&lt;/p&gt;
     &lt;p&gt;渭城朝雨浥轻尘,客舍青青柳色新。&lt;/p&gt;
     &lt;p&gt;劝君更尽一杯酒,西出阳关无故人。&lt;/p&gt;
    &lt;/div&gt;
   &lt;/div&gt;
   &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
     &lt;p&gt;春日&lt;/p&gt;
     &lt;p&gt;宋 · 朱熹&lt;/p&gt;
     &lt;p&gt;胜日寻芳泗水滨,无边光景一时新。&lt;/p&gt;
     &lt;p&gt;等闲识得东风面,万紫千红总是春。&lt;/p&gt;
    &lt;/div&gt;
   &lt;/div&gt;
   &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
     &lt;p&gt;小池&lt;/p&gt;
     &lt;p&gt;唐 · 杨万里&lt;/p&gt;
     &lt;p&gt;泉眼无声惜细流,树阴照水爱晴柔。&lt;/p&gt;
     &lt;p&gt;小荷才露尖尖角,早有蜻蜓立上头。&lt;/p&gt;
    &lt;/div&gt;
   &lt;/div&gt;
   &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
     &lt;p&gt;村居&lt;/p&gt;
     &lt;p&gt;清 · 高鼎&lt;/p&gt;
     &lt;p&gt;草长莺飞二月天,拂堤杨柳醉春烟。&lt;/p&gt;
     &lt;p&gt;儿童散学归来早,忙趁东风放纸鸢。&lt;/p&gt;
    &lt;/div&gt;
   &lt;/div&gt;
   &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
     &lt;p&gt;晓出净慈寺送林子方&lt;/p&gt;
     &lt;p&gt;唐 · 杨万里&lt;/p&gt;
     &lt;p&gt;毕竟西湖六月中,风光不与四时同。&lt;/p&gt;
     &lt;p&gt;接天莲叶无穷碧,映日荷花别样红。&lt;/p&gt;
    &lt;/div&gt;
   &lt;/div&gt;
  &lt;/div&gt;
 &lt;/body&gt;
 &lt;script type=&quot;text/javascript&quot;&gt;
  const container = document.querySelector('.container');
  const cards = document.querySelectorAll('.card');
  
  container.onmousemove = e =&gt; {
   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`);
   }
  }
 &lt;/script&gt;
&lt;/html&gt;
</code></pre>
页: [1]
查看完整版本: CSS+JS实现一个鼠标移动的高亮边框效果