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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:53:23