You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现鼠标追踪渐变文字的Marquee效果:避免下层堆叠上下文图片受Mix-Blend-Mode Overlay影响

实现鼠标追踪渐变文字的Marquee效果:避免下层堆叠上下文图片受Mix-Blend-Mode Overlay影响

嘿,我完全get到你这个问题的痛点了——既要让跑马灯文字hover时出现跟随鼠标的全屏渐变效果,又得保证里面的图片不受渐变干扰,还要解决动画复制内容导致的渐变断裂问题。别着急,咱们来一步步搞定它:

核心解决方案思路

  • 将渐变层移至父容器:把渐变overlay放在跑马灯的父元素(<section>)上,这样它不会被动画复制的内容带偏,始终覆盖整个跑马灯区域
  • 隔离图片的混合模式:给图片添加isolation: isolate属性,创建独立的堆叠上下文,彻底切断mix-blend-mode对图片的影响
  • 调整层级与混合逻辑:通过z-index控制元素层级,确保文字能和渐变正常混合,同时图片稳稳“浮”在渐变之上

修改后的完整代码

JavaScript(仅微调鼠标事件目标)

const marquees = document.querySelectorAll(".marquee");

// 非精简动画模式下添加跑马灯效果
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
  addMarqueeAnimation();
}

function addMarqueeAnimation() {
  marquees.forEach((marquee) => {
    marquee.setAttribute("data-animated", true);
    const scrollerContent = Array.from(marquee.children);
    scrollerContent.forEach((item) => {
      const duplicatedItem = item.cloneNode(true);
      duplicatedItem.setAttribute("aria-hidden", true);
      marquee.appendChild(duplicatedItem);
    });
  });
}

// 追踪鼠标位置,更新父容器的渐变坐标变量
const sections = document.querySelectorAll("section");
for (let i = 0; i < sections.length; i++) {
  const section = sections[i];
  const marquee = section.querySelector(".marquee");
  section.addEventListener("mousemove", (e) => {
    const { x, y } = marquee.getBoundingClientRect();
    section.style.setProperty("--x", e.clientX - x + "px");
    section.style.setProperty("--y", e.clientY - y + "px");
  });
}

CSS(关键修改部分)

body {
  margin: 0;
  font-family: system-ui;
  font-size: 2.5rem;
  background: white;
  color: black;
}
section {
  position: relative;
  /* 为渐变层提供定位基准 */
}

.imgEmoji {
  height: 2.5rem;
  width: 2.5rem;
  margin-bottom: -6px;
  position: relative;
  z-index: 999;
  /* 新增:隔离混合模式,彻底避免渐变影响图片 */
  isolation: isolate;
}

.marquee {
  --gap: 1.5rem;
  display: flex;
  gap: var(--gap);
  overflow-x: clip;
  background: white;
  /* 新增:让文字能和上层渐变正常混合 */
  mix-blend-mode: multiply;
}

.marqueeContent {
  display: flex;
  gap: calc(var(--gap));
  flex-shrink: 0;
  align-items: center;
  justify-content: space-around;
  min-width: 100%;
  padding-block: 0.75rem;
  background: inherit;
}

/* 动画逻辑保持不变 */
.marquee[data-animated="true"] .marqueeContent {
  animation: scroll var(--marquee-duration, 15.5s)
    var(--marquee-direction, forwards) linear infinite;
}
.marquee[data-animated="true"]:hover .marqueeContent {
  animation-play-state: paused;
}

@keyframes scroll {
  to {
    transform: translate(
      calc(-50% - calc(var(--gap) / 2))
    );
  }
}

.marquee[data-direction="left"] {
  --marquee-direction: forwards;
}
.marquee[data-speed="faster"] {
  --marquee-duration: 5s;
}

/* 核心修改:渐变层移至section,hover时显示 */
section:hover::after {
  content: "";
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  background: radial-gradient(
      circle at var(--x, 0) var(--y, 0),
      black 0vw,
      red 21vw,
      green 34vw,
      yellow 51vw
  );
  mix-blend-mode: screen;
  position: absolute;
  z-index: 2;
  /* 新增:避免渐变层阻挡鼠标事件 */
  pointer-events: none;
}

HTML(无需修改)

<section>
  <div class="marquee" data-speed="faster">
    <div class="marqueeContent">
      <a target="blank">Happy Holidays</a>
      <span>
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
      </span>
      <span aria-hidden="true">Happy Holidays</span>
      <span aria-hidden="true">
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
      </span>
      <span aria-hidden="true">Happy Holidays</span>
      <span aria-hidden="true">
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
      </span>
      <span aria-hidden="true">Happy Holidays</span>
      <span aria-hidden="true">
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
        <img class="imgEmoji" src="https://shorturl.at/kzntl" />
      </span>
    </div>
  </div>
</section>

关键修改说明

  1. 渐变层移至section:彻底避免了marquee动画复制内容导致的渐变断裂问题,渐变始终覆盖整个跑马灯区域
  2. 图片添加isolation: isolate:给图片创建独立堆叠上下文,让mix-blend-mode的混合效果无法渗透,完美保护图片不受渐变影响
  3. marquee设置mix-blend-mode: multiply:配合section渐变层的screen混合模式,让文字和渐变正常混合出预期的色彩效果
  4. 渐变层添加pointer-events: none:防止渐变层阻挡鼠标事件,确保跑马灯的hover暂停和鼠标追踪功能正常工作

这样调整后,你想要的效果就完全实现了:hover时渐变跟随鼠标移动、覆盖跑马灯全宽,图片不受任何影响,也不会出现渐变异常~

备注:内容来源于stack exchange,提问作者Warkus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:19:37