React TS中使用mask-image实现径向动画聚焦效果的问题
解决styled-components实现径向聚焦动画失效及替代DOM操作问题
问题分析
你的代码存在几个核心问题导致效果失效:
- 自定义CSS属性
--focal-size的@property定义未被浏览器解析:Property组件未在JSX中渲染,styled-components仅会将样式注入到已渲染的元素对应的CSS中,未渲染组件的样式不会生效。 - DOM元素获取失败:
document.querySelector("focus-zoom")无法找到<FocusZoom>组件,因为styled-components生成的是带随机类名的<div>,而非自定义标签。 - 事件监听未正确绑定:直接在组件函数内添加全局事件监听会导致重复绑定,且未在组件卸载时清理,存在内存泄漏风险。
- 组件参数错误: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
相关产品推荐
相关产品推荐

