跳转到主要内容

crayonxiaoxin

CSS 网格背景与鼠标联动:从静态到动态的技术实现

在 Cyber Crayon 主题中,全站背景使用了一层 CSS 网格作为视觉基底。最近我们为它增加了两层交互效果——视差偏移和局部高亮,让原本单纯的装饰网格变得”活”了起来。本文记录这个实现过程。

问题背景

最初的网格背景是纯 CSS 实现的:两个方向重复的线性渐变交叉形成网格,配合一个 20 秒的 translateY 循环动画让网格缓慢滚动。功能没问题,但太”安静”了——它和用户没有任何交互。

.lc-tech-bg__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(74, 108, 244, 0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(74, 108, 244, 0.03) 1px, transparent 1px);
  background-size: 50px 50px;
  animation: lc-tech-grid-move 20s linear infinite;
}

最终方案

两层网格结构

核心思路:底层保持原本的透明度(3%),上层使用更高的透明度(12%),并通过 CSS mask-image 用径向渐变只露出鼠标附近区域。

.lc-tech-bg__grid--highlight {
  background-image:
    linear-gradient(rgba(74, 108, 244, 0.12) 1px, transparent 1px),
    linear-gradient(90deg, rgba(74, 108, 244, 0.12) 1px, transparent 1px);
  mask-image: radial-gradient(
    150px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
    black 50%,
    transparent 100%
  );
}

mask-image 以鼠标位置为中心画一个径向渐变,从完全不透明(black)渐变到完全透明,让高亮网格只在鼠标附近可见。鼠标位置通过 CSS 自定义属性 --mouse-x--mouse-y 由 JavaScript 实时更新。

JavaScript 鼠标跟踪

(function initTechBgMouse() {
  var grid = document.querySelector('.lc-tech-bg__grid');
  var highlight = document.querySelector('.lc-tech-bg__grid--highlight');
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

  var currentX = 0, currentY = 0;
  var targetX = 0, targetY = 0;
  var ticking = false;

  function update() {
    currentX += (targetX - currentX) * 0.08;
    var tx = 'translate(' + currentX + 'px, ' + currentY + 'px)';
    grid.style.transform = tx;
    if (highlight) highlight.style.transform = tx;
    ticking = false;
  }

  document.addEventListener('mousemove', function (e) {
    targetX = (e.clientX / window.innerWidth - 0.5) * 40;
    targetY = (e.clientY / window.innerHeight - 0.5) * 40;

    if (highlight) {
      highlight.style.setProperty('--mouse-x', (e.clientX / window.innerWidth * 100) + '%');
      highlight.style.setProperty('--mouse-y', (e.clientY / window.innerHeight * 100) + '%');
    }

    if (!ticking) {
      requestAnimationFrame(update);
      ticking = true;
    }
  });
})();

几个关键点

  • 性能requestAnimationFrame + ticking 锁,避免 mousemove 高频触发
  • 平滑:lerp 线性插值让偏移过渡自然
  • 同步:两层网格共享同一个 transform,偏移始终对齐
  • 可访问性prefers-reduced-motion 时完全禁用
@media (prefers-reduced-motion: reduce) {
  .lc-tech-bg__grid--highlight {
    display: none;
  }
}

总结

这个实现的核心架构是:两层网格 + mask-image + CSS 自定义属性 + requestAnimationFrame lerp。整个过程没有依赖任何第三方库,纯 CSS + 原生 JS 实现。最终的视觉效果是:网格随着鼠标移动微微浮动,鼠标扫过的区域网格线自然亮起,像是背景在”呼吸”。

讨论

还没有留言,来留下第一条评论吧!

留下足迹