实现鼠标追踪渐变文字的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>
关键修改说明
- 渐变层移至section:彻底避免了marquee动画复制内容导致的渐变断裂问题,渐变始终覆盖整个跑马灯区域
- 图片添加isolation: isolate:给图片创建独立堆叠上下文,让mix-blend-mode的混合效果无法渗透,完美保护图片不受渐变影响
- marquee设置mix-blend-mode: multiply:配合section渐变层的
screen混合模式,让文字和渐变正常混合出预期的色彩效果 - 渐变层添加pointer-events: none:防止渐变层阻挡鼠标事件,确保跑马灯的hover暂停和鼠标追踪功能正常工作
这样调整后,你想要的效果就完全实现了:hover时渐变跟随鼠标移动、覆盖跑马灯全宽,图片不受任何影响,也不会出现渐变异常~
备注:内容来源于stack exchange,提问作者Warkus
相关产品推荐
相关产品推荐

