diff --git a/.gitignore b/.gitignore index 1eaa024..090ec99 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .DS_Store node_modules .cache +.idea dist # Cypress cypress/screenshots/ diff --git a/README.md b/README.md index fc34d03..076cae7 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ const Canvas = class extends React.Component { ## List of Props | Props | Expected datatype | Default value | Description | -| ---------------------------------- | ----------------- | --------------------- | --------------------------------------------------------------------------------------------------- | +|------------------------------------|-------------------| --------------------- |-----------------------------------------------------------------------------------------------------| | width | PropTypes.string | 100% | canvas width (em/rem/px) | | height | PropTypes.string | 100% | canvas width (em/rem/px) | | id | PropTypes.string | "react-sketch-canvas" | ID field to uniquely identify a SVG canvas (Supports multiple canvases in a single page) | @@ -136,6 +136,7 @@ const Canvas = class extends React.Component { | allowOnlyPointerType | PropTypes.string | all | allow pointer type ("all"/"mouse"/"pen"/"touch") | | onChange | PropTypes.func | | Returns the current sketch path in `CanvasPath` type on every path change | | onStroke | PropTypes.func | | Returns the the last stroke path and whether it is an eraser stroke on every pointer up event | +| scrollOnTouch | PropTypes.bool | false | Scroll overflowing component instead of sketching if pointerType === touch | | style | PropTypes.object | false | Add CSS styling as CSS-in-JS object | | svgStyle | PropTypes.object | {} | Add CSS styling as CSS-in-JS object for the SVG | | withTimestamp | PropTypes.bool | false | Add timestamp to individual strokes for measuring sketching time | diff --git a/packages/react-sketch-canvas/src/Canvas/index.tsx b/packages/react-sketch-canvas/src/Canvas/index.tsx index 8dd3bec..e8192b0 100644 --- a/packages/react-sketch-canvas/src/Canvas/index.tsx +++ b/packages/react-sketch-canvas/src/Canvas/index.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-array-index-key */ import * as React from "react"; -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; import Paths, { SvgPath } from "../Paths"; import { CanvasPath, ExportImageType, Point } from "../types"; @@ -35,6 +35,7 @@ export interface CanvasProps { paths: CanvasPath[]; isDrawing: boolean; onPointerDown: (point: Point, isEraser?: boolean) => void; + scrollOnTouch: boolean; onPointerMove: (point: Point) => void; onPointerUp: () => void; className?: string; @@ -59,6 +60,7 @@ export const Canvas = React.forwardRef((props, ref) => { const { paths, isDrawing, + scrollOnTouch, onPointerDown, onPointerMove, onPointerUp, @@ -79,6 +81,7 @@ export const Canvas = React.forwardRef((props, ref) => { } = props; const canvasRef = React.useRef(null); + const currentPointerType = React.useRef<"pen" | "mouse" | "touch" | null>(null); // Converts mouse coordinates to relative coordinate based on the absolute position of svg const getCoordinates = useCallback( @@ -104,10 +107,20 @@ export const Canvas = React.forwardRef((props, ref) => { const handlePointerDown = useCallback( (event: React.PointerEvent): void => { - // Allow only chosen pointer type + currentPointerType.current = event.pointerType; - if ( - allowOnlyPointerType !== "all" && + if (scrollOnTouch && event.pointerType === "touch") { + return; + } + + if (event.pointerType === 'pen') { + event.preventDefault(); + event.stopPropagation(); + } + + // Allow only chosen pointer type + if ( + allowOnlyPointerType !== "all" && event.pointerType !== allowOnlyPointerType ) { return; @@ -129,9 +142,13 @@ export const Canvas = React.forwardRef((props, ref) => { (event: React.PointerEvent): void => { if (!isDrawing) return; - // Allow only chosen pointer type - if ( - allowOnlyPointerType !== "all" && + if (scrollOnTouch && event.pointerType === "touch") { + return; + } + + // Allow only chosen pointer type + if ( + allowOnlyPointerType !== "all" && event.pointerType !== allowOnlyPointerType ) { return; @@ -147,6 +164,9 @@ export const Canvas = React.forwardRef((props, ref) => { const handlePointerUp = useCallback( (event: React.PointerEvent | PointerEvent): void => { if (event.pointerType === "mouse" && event.button !== 0) return; + if (scrollOnTouch && event.pointerType === "touch") { + return; + } // Allow only chosen pointer type if ( @@ -291,13 +311,31 @@ release drawing even when point goes out of canvas */ ); }, [paths]); + // avoid pen from scrolling if scrollOnTouch + useEffect(() => { + const listener = function( + this: HTMLDivElement, + event: TouchEvent + ): void { + if (currentPointerType.current === "pen") { + event.preventDefault(); + event.stopPropagation(); + } + }; + + if (scrollOnTouch) { + canvasRef.current?.addEventListener("touchstart", listener, { passive: false }); + } + return () => canvasRef.current?.removeEventListener("touchstart", listener); + }, []); + return (
undefined, onStroke = (_path: CanvasPath, _isEraser: boolean): void => undefined, withTimestamp = false, + scrollOnTouch = false, } = props; const svgCanvas = React.createRef(); @@ -262,6 +264,7 @@ export const ReactSketchCanvas = React.forwardRef< svgStyle={svgStyle} paths={currentPaths} isDrawing={isDrawing} + scrollOnTouch={scrollOnTouch} onPointerDown={handlePointerDown} onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}