Skip to content

Preview for the pointer with strokeWidth #169

Open
@mung3477

Description

@mung3477

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

First, thank you for implementing this package. It was the only one among canvas drawing packages that supported various devices.

To enhance the user experience, I hope to see where the cursor is positioned and current stroke width.
Something like this!

Example

Describe the solution you'd like
A clear and concise description of what you want to happen.

To solve this problem, I made a custom hook to implement this.
It quite works well, and I think this feature would make this package better!

import { forwardRef, useEffect, useRef, useState } from "react";

interface StrokePreviewProps {
  top: string;
  left: string;
  strokeWidth: number;
}
const StrokePreview = forwardRef<HTMLDivElement, StrokePreviewProps>(
  ({ top, left, strokeWidth }, ref) => (
    <div
      style={{
        top,
        left,
        width: `${strokeWidth}px`,
        height: `${strokeWidth}px`,
        transform: "translate(-50%, -50%)"
      }}
      className="absolute rounded-full bg-[rgba(182,146,246,0.75)]"
      ref={ref}
    />
  )
);

function useStrokePreview() {
  const [top, setTop] = useState<string>("0px");
  const [left, setLeft] = useState<string>("0px");
  const canvasContainerRef = useRef<HTMLDivElement>(null);
  const cursorFollowerRef = useRef<HTMLDivElement>(null);
  const timeoutId = useRef<ReturnType<typeof setTimeout>>();

  useEffect(() => {
    const positionCursorFollower = (e: MouseEvent | PointerEvent) => {
      timeoutId.current = setTimeout(() => {
        if (canvasContainerRef.current) {
          const { offsetHeight, offsetWidth } = canvasContainerRef.current;

          setTop(`${Math.min(offsetHeight, Math.max(0, e.offsetY))}px`);
          setLeft(`${Math.min(offsetWidth, Math.max(0, e.offsetX))}px`);
        }
      }, 1);
    };

    if (canvasContainerRef.current) {
      canvasContainerRef.current.addEventListener(
        "mousemove",
        positionCursorFollower
      );
      canvasContainerRef.current.addEventListener(
        "pointermove",
        positionCursorFollower
      );
    }

    return () => {
      if (timeoutId.current) {
        clearTimeout(timeoutId.current);
      }
      canvasContainerRef?.current?.removeEventListener(
        "mousemove",
        positionCursorFollower
      );
      canvasContainerRef?.current?.removeEventListener(
        "pointermove",
        positionCursorFollower
      );
    };
  }, [!!canvasContainerRef.current]);

  return { top, left, canvasContainerRef, cursorFollowerRef };
}

export default StrokePreview;
export { useStrokePreview };

<div className="relative">
          <StrokePreview
            top={strokeTop}
            left={strokeLeft}
            strokeWidth={strokeWidth}
            ref={cursorFollowerRef}
          />
          <div className="mb-[20px] flex flex-col" ref={canvasContainerRef}>
            <img src={imgSrc} alt="original" />
            <ReactSketchCanvas
              style={{
                position: "absolute",
                top: 0,
                left: 0,
                cursor: "none"
              }}
              canvasColor="transparent"
              strokeColor="rgba(182, 146, 246, 0.75)"
              strokeWidth={strokeWidth}
              eraserWidth={strokeWidth}
              onStroke={onChange}
              ref={skectchCanvasRef}
            />
          </div>
</div>

And the result seems like this.
Screenshot 2024-08-27 at 4 42 45 PM

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

As I am novice in handling interactions with devices like tablets and mobile phones, I think I may have missed sth.
Thank you for reading!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions