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

React TS中使用mask-image实现径向动画聚焦效果的问题

解决styled-components实现径向聚焦动画失效及替代DOM操作问题

问题分析

你的代码存在几个核心问题导致效果失效:

  1. 自定义CSS属性--focal-size的@property定义未被浏览器解析:Property组件未在JSX中渲染,styled-components仅会将样式注入到已渲染的元素对应的CSS中,未渲染组件的样式不会生效。
  2. DOM元素获取失败:document.querySelector("focus-zoom")无法找到<FocusZoom>组件,因为styled-components生成的是带随机类名的<div>,而非自定义标签。
  3. 事件监听未正确绑定:直接在组件函数内添加全局事件监听会导致重复绑定,且未在组件卸载时清理,存在内存泄漏风险。
  4. 组件参数错误:React组件的props应为对象,而非直接接收bool参数。

修复方案及代码实现

1. 正确定义自定义CSS属性

使用createGlobalStyle将@property注入全局样式,确保浏览器能识别自定义动画属性。

2. 使用useRef替代手动DOM操作

React中推荐使用useRef获取组件实例,避免直接操作DOM。

3. 用useEffect管理事件监听

在组件挂载时添加事件监听,卸载时移除,避免内存泄漏。

修复后的完整代码:

import React, { useRef, useEffect } from "react";
import styled, { createGlobalStyle } from "styled-components";

// 全局定义自定义CSS属性
const GlobalStyles = createGlobalStyle`
  @property --focal-size {
    syntax: "<length-percentage>";
    initial-value: 100%;
    inherits: false;
  }
`;

const FocusZoom = styled.div`
  --mouse-x: center;
  --mouse-y: center;
  --backdrop-color: hsl(200 50% 0% / 50%);
  --backdrop-blur-strength: 10px;
  
  position: fixed;
  touch-action: none;
  inset: 0;
  background-color: var(--backdrop-color);
  backdrop-filter: blur(var(--backdrop-blur-strength));
  
  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y), 
    transparent var(--focal-size), 
    black 0%
  );
  
  transition: --focal-size .3s ease;
`;

function App() {
  // 使用useRef获取FocusZoom元素
  const zoomRef = useRef<HTMLDivElement>(null);

  const toggleSpotlight = (bool: boolean) => {
    if (zoomRef.current) {
      zoomRef.current.style.setProperty("--focal-size", bool ? "15vmax" : "100%");
    }
  };

  const handlePointerMove = (e: PointerEvent) => {
    if (zoomRef.current) {
      zoomRef.current.style.setProperty("--mouse-x", `${e.clientX}px`);
      zoomRef.current.style.setProperty("--mouse-y", `${e.clientY}px`);
    }
  };

  useEffect(() => {
    // 添加事件监听
    window.addEventListener("pointermove", handlePointerMove);
    window.addEventListener("keydown", (e) => toggleSpotlight(e.altKey));
    window.addEventListener("keyup", (e) => toggleSpotlight(e.altKey));
    window.addEventListener("touchstart", () => toggleSpotlight(true));
    window.addEventListener("touchend", () => toggleSpotlight(false));

    // 组件卸载时移除监听
    return () => {
      window.removeEventListener("pointermove", handlePointerMove);
      window.removeEventListener("keydown", (e) => toggleSpotlight(e.altKey));
      window.removeEventListener("keyup", (e) => toggleSpotlight(e.altKey));
      window.removeEventListener("touchstart", () => toggleSpotlight(true));
      window.removeEventListener("touchend", () => toggleSpotlight(false));
    };
  }, []);

  return (
    <>
      <GlobalStyles />
      <h1>
        Press <kbd>Opt/Alt</kbd> or touch for a spotlight effect
      </h1>
      {/* 将ref绑定到FocusZoom组件 */}
      <FocusZoom ref={zoomRef}></FocusZoom>
    </>
  );
}

export default App;

关键修复点说明

  • 全局自定义属性:通过createGlobalStyle注入@property,确保浏览器能识别--focal-size的动画特性。
  • useRef获取元素:zoomRef直接绑定到<FocusZoom>组件,无需手动查询DOM,符合React的数据流规范。
  • 事件监听管理:useEffect的清理函数确保组件卸载时移除所有事件监听,避免内存泄漏。
  • 类型安全:为useRef指定HTMLDivElement类型,符合TypeScript的类型要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:48:38