使用Framer Motion时如何处理透明背景的光标触发范围问题
解决PNG透明区域hover触发Cursor效果的问题
提问(翻译自英文)
我正在为开发的网站设计UI,使用了一张带透明背景的PNG资源。目前已成功实现图片背景透明,但使用Cursor效果时,hitbox包含了透明区域。我希望仅当光标悬停在图像主体(非透明像素)上时才触发该效果,请问是否需要使用其他库?以下是我的现有代码:
import React from "react"; import { Cursor } from "./cursorHover"; type ImageProps = React.ImgHTMLAttributes<HTMLImageElement> & { alt: string; width?: number; height?: number; hover?: JSX.Element; }; const Image: React.FC<ImageProps> = ( { alt, width, height, hover, ...props }: ImageProps, ref ) => { return ( <div style={{ position: "relative" }}> {hover && ( <Cursor attachToParent variants={{ initial: { height: 0, opacity: 0, scale: 0.3 }, animate: { height: "auto", opacity: 1, scale: 1 }, exit: { height: 0, opacity: 0, scale: 0.3 }, }} transition={{ type: "spring", duration: 0.3, bounce: 0.1, }} className="overflow-hidden" > {typeof hover === "boolean" ? <div>hi</div> : hover} </Cursor> )} <img {...props} alt={alt} width={width} height={height} /> <canvas ref={ref} style={{ display: "none" }} /> </div> ); }; export default Image;
不需要额外引入库,通过Canvas检测像素透明度就能实现精准的hover判定,以下是修改后的实现:
核心思路
- 将图片绘制到隐藏的Canvas中
- 监听鼠标在图片上的移动事件,计算光标对应的图片相对坐标
- 通过Canvas的
getImageData获取该坐标的像素透明度,判断是否触发Cursor效果
修改后的完整代码
import React, { useRef, useEffect, useState } from "react"; import { Cursor } from "./cursorHover"; type ImageProps = React.ImgHTMLAttributes<HTMLImageElement> & { alt: string; width?: number; height?: number; hover?: JSX.Element; }; const Image: React.FC<ImageProps> = ({ alt, width, height, hover, src, ...props }) => { const canvasRef = useRef<HTMLCanvasElement>(null); const imgRef = useRef<HTMLImageElement>(null); const [isHoveringOpaque, setIsHoveringOpaque] = useState(false); useEffect(() => { const img = imgRef.current; const canvas = canvasRef.current; if (!img || !canvas || !src) return; // 图片加载完成后绘制到Canvas const loadHandler = () => { canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext("2d"); ctx?.drawImage(img, 0, 0); }; if (img.complete) { loadHandler(); } else { img.addEventListener("load", loadHandler); return () => img.removeEventListener("load", loadHandler); } }, [src]); const handleMouseMove = (e: React.MouseEvent<HTMLImageElement>) => { const img = imgRef.current; const canvas = canvasRef.current; if (!img || !canvas) return; // 计算光标在图片上的相对坐标(适配缩放后的显示尺寸) const rect = img.getBoundingClientRect(); const x = Math.floor((e.clientX - rect.left) * (img.width / rect.width)); const y = Math.floor((e.clientY - rect.top) * (img.height / rect.height)); const ctx = canvas.getContext("2d"); if (!ctx) return; // 获取像素数据,第四个值为透明度(0=完全透明,255=完全不透明) const pixelAlpha = ctx.getImageData(x, y, 1, 1).data[3]; setIsHoveringOpaque(pixelAlpha > 0); }; const handleMouseLeave = () => { setIsHoveringOpaque(false); }; return ( <div style={{ position: "relative" }}> {/* 仅当hover在非透明区域时渲染Cursor */} {hover && isHoveringOpaque && ( <Cursor attachToParent variants={{ initial: { height: 0, opacity: 0, scale: 0.3 }, animate: { height: "auto", opacity: 1, scale: 1 }, exit: { height: 0, opacity: 0, scale: 0.3 }, }} transition={{ type: "spring", duration: 0.3, bounce: 0.1, }} className="overflow-hidden" > {typeof hover === "boolean" ? <div>hi</div> : hover} </Cursor> )} <img ref={imgRef} {...props} src={src} alt={alt} width={width} height={height} onMouseMove={handleMouseMove} onMouseLeave={handleMouseLeave} /> <canvas ref={canvasRef} style={{ display: "none" }} /> </div> ); }; export default Image;
关键说明
- 利用Canvas的像素读取能力,精准判断光标是否落在非透明区域
- 适配了图片缩放后的显示尺寸,确保坐标计算准确
- 通过
isHoveringOpaque状态控制Cursor组件的显示/隐藏,只在符合条件时触发动画
内容的提问来源于stack exchange,提问作者H I
相关产品推荐
相关产品推荐

