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

React中如何实现滚动触发的元素垂直上移遮罩擦除动画?

问题描述

使用React.js开发时,需要复刻指定站点的文字垂直上移擦除动画:文字从遮挡区域后方向上移动显现,滚动到对应元素位置时触发。仅复用位移动画CSS代码时,无法实现擦除效果。

参考动画效果:
垂直上移擦除文字动效

尝试使用的CSS代码片段:

transition-delay: .9s;
transition: transform 1.3s cubic-bezier(.075, .82, .165, 1);
transform: translateY(0%);
核心实现原理

之前的代码只实现了文字位移动画,缺失了遮罩裁切结构,这是擦除效果的核心:需要两层嵌套元素,外层容器设置overflow: hidden作为可视区域边界,文字初始位于外层容器可视范围下方,上移过程中超出外层容器的部分会被隐藏,自然形成从下往上擦除显现的效果。

分步实现方案

1. 基础DOM结构与CSS样式

先写两层嵌套的结构,搭配对应状态样式:

{/* 外层:遮罩裁切容器 */}
<div className="text-mask">
  {/* 内层:需要做动画的文字元素 */}
  <h1 className="sliding-text">A DIGITAL DESIGN STUDIO DRIVEN BY RESEARCH & STRATEGY</h1>
</div>

对应CSS代码:

/* 遮罩容器:固定可视范围,超出部分直接隐藏 */
.text-mask {
  overflow: hidden;
  /* 行高和容器高度匹配,避免文字漏出 */
  line-height: 1.1;
}

/* 文字初始状态:完全下移到遮罩容器外,默认不可见 */
.sliding-text {
  margin: 0;
  transform: translateY(100%);
  opacity: 0;
  /* 直接复用提取的缓动参数即可 */
  transition: transform 1.3s cubic-bezier(.075, .82, .165, 1), opacity 1.3s cubic-bezier(.075, .82, .165, 1);
  transition-delay: .9s;
}

/* 动画激活状态:文字回到遮罩容器内的正常位置 */
.text-mask.in-view .sliding-text {
  transform: translateY(0);
  opacity: 1;
}

2. 滚动触发逻辑

两种实现方式可选,不需要引入额外依赖也能实现:

  • 原生轻量方案:用浏览器原生IntersectionObserver API写一个自定义React Hook,监听元素是否进入视口,进入后给遮罩容器加上in-view类名触发动画即可,示例代码:
import { useEffect, useRef, useState } from 'react';

// 视口进入监听Hook
function useScrollIn(triggerThreshold = 0.15) {
  const targetRef = useRef(null);
  const [isInView, setIsInView] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      const [entry] = entries;
      if (entry.isIntersecting) {
        setIsInView(true);
        // 动画只触发一次,触发后取消监听
        observer.unobserve(entry.target);
      }
    }, {
      threshold: triggerThreshold
    });

    const currentTarget = targetRef.current;
    if (currentTarget) observer.observe(currentTarget);

    return () => {
      if (currentTarget) observer.unobserve(currentTarget);
    }
  }, [triggerThreshold]);

  return [targetRef, isInView];
}

// 业务组件中使用
function HeroSection() {
  const [textMaskRef, isTextInView] = useScrollIn();
  return (
    <section>
      <div 
        ref={textMaskRef} 
        className={`text-mask ${isTextInView ? 'in-view' : ''}`}
      >
        <h1 className="sliding-text">A DIGITAL DESIGN STUDIO DRIVEN BY RESEARCH & STRATEGY</h1>
      </div>
    </section>
  )
}
  • 依赖方案:如果项目中有大量滚动动画需求,可以引入react-intersection-observer简化监听逻辑,需要做复杂序列动画时搭配framer-motion可以快速实现逐字延迟、多元素联动的效果,不用手动写CSS类名切换。

3. 效果优化点

  • 如果要实现逐词/逐字擦除的效果,把文字拆分为独立的span标签,每个span外层单独套一层mask容器,给每个span的动画设置递增的transition-delay即可。
  • 遮罩容器不要加上下padding,避免初始状态下文字边缘漏出。
  • 动画延迟、时长参数可以根据页面整体节奏调整,提取的缓动曲线参数和目标效果一致,不需要修改。

内容的提问来源于stack exchange,提问作者blue

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 08:42:24