Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Svg pointer coordinates action #78

Open
iolyd opened this issue Apr 1, 2023 · 2 comments
Open

proposal: Svg pointer coordinates action #78

iolyd opened this issue Apr 1, 2023 · 2 comments

Comments

@iolyd
Copy link

iolyd commented Apr 1, 2023

This is simply to propose the addition of a new action for the community, feel free to close or delete this issue if you think this is not a worthy proposal.

I recently had to work with pointer events and svg elements and faced the annoying case where the auto-scaling resulting from the svg's viewBox meant the pointer coordinates retrieved from native events were misaligned with the svg's coordinates space. This is just a question of reversing the transformation, and luckily could be done rather easily with methods already available. Here's the action I came up with:

const SVG_POINTER_EVENT = {
  Move: 'svg.pointermove',
  Down: 'svg.pointerdown',
  Up: 'svg.pointerup',
  Click: 'svg.click',
} as const;

interface SvgPointerEventData<E extends MouseEvent | PointerEvent> {
  x: number;
  y: number;
  originalEvent: E;
}

declare global {
  namespace svelteHTML {
    interface SVGAttributes<T> {
      'on:svg.pointermove'?: (event: CustomEvent<SvgPointerEventData<PointerEvent>>) => unknown;
      'on:svg.pointedown'?: (event: CustomEvent<SvgPointerEventData<PointerEvent>>) => unknown;
      'on:svg.pointerup'?: (event: CustomEvent<SvgPointerEventData<PointerEvent>>) => unknown;
      'on:svg.click'?: (event: CustomEvent<SvgPointerEventData<MouseEvent>>) => unknown;
    }
  }
}

/**
 * Facilitates retrieving the pointer's position as transformed by the host viewboxed svg element's
 * auto-scaling.
 *
 * @see https://stackoverflow.com/questions/10298658/mouse-position-inside-autoscaled-svg
 */
export default function svgPointer(svg: SVGSVGElement, {}: {} = {}) {
  const pt = svg.createSVGPoint();

  function getSvgPointer<E extends MouseEvent | PointerEvent>(e: E) {
    pt.x = e.clientX;
    pt.y = e.clientY;
    const transformed = pt.matrixTransform(svg.getScreenCTM()?.inverse());
    return {
      x: transformed.x,
      y: transformed.y,
      originalEvent: e,
    } satisfies SvgPointerEventData<E>;
  }

  function handlePointerMove(e: PointerEvent) {
    svg.dispatchEvent(new CustomEvent(SVG_POINTER_EVENT.Move, { detail: getSvgPointer(e) }));
  }
  function handlePointerDown(e: PointerEvent) {
    svg.dispatchEvent(new CustomEvent(SVG_POINTER_EVENT.Down, { detail: getSvgPointer(e) }));
  }
  function handlePointerUp(e: PointerEvent) {
    svg.dispatchEvent(new CustomEvent(SVG_POINTER_EVENT.Up, { detail: getSvgPointer(e) }));
  }
  function handleClick(e: MouseEvent) {
    svg.dispatchEvent(new CustomEvent(SVG_POINTER_EVENT.Click, { detail: getSvgPointer(e) }));
  }

  svg.addEventListener('pointermove', handlePointerMove);
  svg.addEventListener('pointerdown', handlePointerDown);
  svg.addEventListener('pointerup', handlePointerUp);
  svg.addEventListener('click', handleClick);

  return {
    update(args) {},
    destroy() {
      svg.removeEventListener('pointermove', handlePointerMove);
      svg.removeEventListener('pointerdown', handlePointerDown);
      svg.removeEventListener('pointerup', handlePointerUp);
      svg.removeEventListener('click', handleClick);
    },
  } satisfies SvelteActionReturnType;
}

If you think this could be an interesting addition, dont hesitate to adapt it so it better fits svelte-legos's apis.

@ankurrsinghal
Copy link
Owner

Thanks @iolyd looks good to me I will do a sanity check and add it to the docs.

@ankurrsinghal
Copy link
Owner

Hi @iolyd can you also provide a demo of the above action, may be raise PR for this action with respect to how other actions are setup?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants