0

I'm currently building a website with help of React and docusaurus, and there I want to have a svg image (located in static/astronaut-light.svg) that switches color as soon as I poke it. The problem is that the onClick/onMouseEnter event gets triggered on the transparent parts too, which isn't a preferred behavior. (See video)
-> It should only switch its color if I click happens on a non transparent part.

Youtube Video: https://youtu.be/tZ2UfWE1Aw4
Website: https://agile-ts.org/
SourceCode: https://github.com/agile-ts/documentation/blob/develop/src/_pages/LandingPage/components/HeaderView/components/Astronaut/index.tsx

I tried to solve this problem by checking if the pixel is transparent or not.. but I couldn't get it to work. (See code below)

  const imageRef = useRef<HTMLImageElement>(null);
  const ctx = document.createElement('canvas').getContext('2d');

  // https://medium.com/@pdx.lucasm/canvas-with-react-js-32e133c05258
  // https://stackoverflow.com/questions/38487569/click-through-png-image-only-if-clicked-coordinate-is-transparent
  const trigger = (event: React.MouseEvent<HTMLImageElement, MouseEvent>) => {
    // Get click coordinates
    const x = event.pageX - imageRef.current.offsetLeft;
    const y = event.pageY - imageRef.current.offsetTop;
    const w = (ctx.canvas.width = imageRef.current.width);
    const h = (ctx.canvas.height = imageRef.current.height);

    // Draw image to canvas
    // and read Alpha channel value
    ctx.drawImage(imageRef.current, 0, 0, w, h);
    const alpha = ctx.getImageData(x, y, 1, 1).data[3]; // [0]R [1]G [2]B [3]A

    // If pixel is transparent,
    // retrieve the element underneath and trigger it's click event
    if (alpha === 0) {
      console.log('alpha0');
      imageRef.current.style.pointerEvents = 'none';
      const element = document.elementFromPoint(event.clientX, event.clientY);
      // element.trigger('click');
      imageRef.current.style.pointerEvents = 'auto';
    } else {
      setIsRaised(true);
      console.log('LOGO clicked!');
    }

  return (
    <div className={clsx(styles.Container, className)}>
      <animated.img
        ref={imageRef}
        onMouseEnter={trigger}
        style={animated_Astronaut}
        className={styles.Image}
        src={`img/astronaut-${dark ? 'dark' : 'light'}.svg`}
        alt={'Astronaut'}
      />
      <div className={styles.Text}>Poke me  to mutate my color State.</div>
    </div>
  );

Maybe you have an idea how to solve this problem.
Thank you :D

BennoDev
  • 1,067
  • 1
  • 6
  • 17
  • 3
    Looks like you're including the SVG via an img element. You'd need to use an object or iframe element instead or have the SVG inline on the page itself. – Robert Longson Feb 25 '21 at 07:15
  • yeah sure.. I've already tried to use 'object' and 'iframe' but both ended up in the same behavior.. and an inline svg works.. but is for me no clean option.. – BennoDev Feb 25 '21 at 07:44
  • To the Browser the SVG becomes a **square** IMG. So one (complex) workaround is to get the ``clientX,Y`` coordinates of the click, determine the IMG relative position, extract the SVG, create the SVG as (hidden) DOM element, calculate the "clicked" location. Use ``elementsFromPoint`` to determine the clicked SVG element... and you are using React.. so you probably need to do more to make it all work in the DOM. – Danny '365CSI' Engelman Feb 25 '21 at 08:41
  • @Danny'365CSI'Engelman yeah as you can see in the code snippet above I tried to solve the problem in a similar way.. but I couldn't manage to get it to work.. – BennoDev Feb 25 '21 at 10:22
  • I don't use React because I prefer real DOM over virtualDOM. I would check if your ``canvas`` method actually works in a non-React JSFiddle/CodePen – Danny '365CSI' Engelman Feb 25 '21 at 10:37
  • 1
    An inline SVG **IS** the clean way. Any other approach would be a hack. If you don't want the SVG in your static HTML, then you can always fetch and insert it into the page at run time. – Paul LeBeau Feb 25 '21 at 11:54
  • @PaulLeBeau yeah I ended up with an inline svg.. with not clean I meant to paste the svg directly into the code.. I outsourced it into a separate component.. may I ask you how to fetch and insert a svg at runtime.. thank you ;D – BennoDev Feb 25 '21 at 12:45
  • 1
    @BennoDev Since you are using React, then making it a component, as you have already done, is a good solution. – Paul LeBeau Feb 25 '21 at 15:16

0 Answers0