-
Notifications
You must be signed in to change notification settings - Fork 8
Feature/new react sketch pad component #45
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
Merged
brandongregoryscott
merged 16 commits into
rsm-hcd:master
from
KevinBusch:feature/new-react-sketch-pad-component
Aug 31, 2020
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
e0a28b2
This is a test
dda27fa
Initial add of all the sketchpad files
8c6c85a
Merge branch 'master' of https://github.com/AndcultureCode/Andculture…
6daaaf0
Added a core utils object with bindAll in order to bind to all objec…
5c9b11c
Removed unnecessary console logs. Updated canvas sketch's coreutil.b…
e92ac0f
Cleanup
52c1b88
Added border feature
a9089e4
Filled in some defaults for storybook
5c5a206
Updated pencil tool to capture data in more semantic way
20c0337
Added JSDocs
231294c
Added JSDocs to the sketchpad component
154ba27
Refactored effect hooks to track changes on a more granular scale
d9aeaef
Made canvas sketch's redrawSketch public and updated react component …
88b9983
Removed unnecessary console log
e0def85
Moved props in story to local variables so they aren't reinitialized …
c02142e
Removed console logs
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export enum CanvasObjectType { | ||
| image = "image", | ||
| line = "line", | ||
| path = "path", | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| export enum CanvasToolType { | ||
| image = "Image-Draw-Tool", | ||
| line = "Line-Canvas-Draw-Tool", | ||
| pan = "Pan-Canvas-Object", | ||
| pencil = "Pencil-Canvas-Draw-Tool", | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| export interface PointerPosition { | ||
| x: number; | ||
| y: number; | ||
| }; |
42 changes: 42 additions & 0 deletions
42
src/atoms/forms/canvas-sketch/react-canvas-sketch.stories.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import React from "react"; | ||
| import { text, number, select, boolean } from "@storybook/addon-knobs"; | ||
| import { ReactCanvasSketch, ReactCanvasSketchValue } from "./react-canvas-sketch"; | ||
| import { CanvasDrawToolSettings } from "./tools/base-canvas-draw-tool"; | ||
| import { CanvasToolType } from "./enums/canvas-tool-type"; | ||
|
|
||
| export default { | ||
| component: ReactCanvasSketch, | ||
| title: "Atoms | Forms / Canvas Sketch", | ||
| }; | ||
|
|
||
| const value: ReactCanvasSketchValue = { | ||
| currentObjectIndex: -1, | ||
| objects: [] | ||
| } | ||
|
|
||
| const onAddedStroke: (strokeSettings: CanvasDrawToolSettings) => void = | ||
| (strokeSettings: CanvasDrawToolSettings) => { console.log(`STORYBOOK MESSAGE - onAddedStroke: ${JSON.stringify(strokeSettings)}`) }; | ||
|
|
||
| const canvasToolTypes = [ | ||
| CanvasToolType.line, | ||
| CanvasToolType.pan, | ||
| CanvasToolType.pencil, | ||
| ]; | ||
|
|
||
| export const reactCanvasSketch = () => ( | ||
| <ReactCanvasSketch | ||
| backgroundImageUrl={text("Background Image URL", "https://rlv.zcache.com/yellow_emoji_birthday_party_happy_face_symbol_classic_round_sticker-r821c9ad7d35943228f0f0e973050e063_0ugmm_8byvr_704.jpg")} | ||
| canvasHeight={number("Canvas Height", 720)} | ||
| canvasWidth={number("Canvas Width", 1000)} | ||
| className={text("Class Name", "")} | ||
| containerHeight={number("Container Height", 700)} | ||
| containerWidth={number("Container Width", 700)} | ||
| onAddedStroke={onAddedStroke} | ||
| redrawIncrement={number("Redraw Trigger Increment", 1)} | ||
| canvasToolType={select("Tool Type", canvasToolTypes, CanvasToolType.pencil)} | ||
| showCanvasBorder={boolean("Show Border", true)} | ||
| toolWidth={number("Tool Width", 1)} | ||
| toolColor={text("Tool Color", "#000000")} | ||
| value={value} | ||
| /> | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,300 @@ | ||
| import { | ||
| CanvasSketch, | ||
| CanvasSketchConfig | ||
| } from "./canvas-sketch"; | ||
| import * as React from "react"; | ||
| import { CanvasToolType } from "./enums/canvas-tool-type"; | ||
| import { CanvasDrawToolSettings } from "./tools/base-canvas-draw-tool"; | ||
| import { useEffect } from "react"; | ||
|
|
||
| // ------------------------------------------------------------------------------------------------- | ||
| // #region Interfaces | ||
| // ------------------------------------------------------------------------------------------------- | ||
|
|
||
| /** | ||
| * Represents the objects containing all data necessary to redraw the sketch canvas at a point in | ||
| * the history of the stack of drawing objects | ||
| */ | ||
| export interface ReactCanvasSketchValue { | ||
| /** | ||
| * The current point/index in the history stack of drawing objects | ||
| */ | ||
| currentObjectIndex: number; | ||
| /** | ||
| * The drawn objects stack | ||
| */ | ||
| objects: CanvasDrawToolSettings[]; | ||
| } | ||
|
|
||
| /** | ||
| * Represents all properties supported by the <ReactCanvasSketch> component | ||
| */ | ||
| export interface ReactCanvasSketchProps { | ||
| /** | ||
| * The URL of the background image to be drawn to the sketchpad | ||
| */ | ||
| backgroundImageUrl: string; | ||
| /** | ||
| * The height of the canvas element | ||
| */ | ||
| canvasHeight: number; | ||
| /** | ||
| * The width of the canvas element | ||
| */ | ||
| canvasWidth: number; | ||
| /** | ||
| * The className that will be appended to the sketchpad's outer most container | ||
| */ | ||
| className: string; | ||
| /** | ||
| * The height of the container wrapping the sketchpad | ||
| * NOTE: This can be smaller than the canvasHeight value since the sketchpad supports panning | ||
| */ | ||
| containerHeight: number; | ||
| /** | ||
| * The width of the container wrapping the sketchpad | ||
| * NOTE: This can be smaller than the canvasWidth value since the sketchpad supports panning | ||
| */ | ||
| containerWidth: number; | ||
| /** | ||
| * Handler to allow caller to track added stroke settings whenever a tool draws a new object | ||
| * to the drawn objects stack | ||
| */ | ||
| onAddedStroke: (strokeSettings: CanvasDrawToolSettings) => void; | ||
| /** | ||
| * Allows the caller to redraw the sketchpad by changing the numeric value provided | ||
| */ | ||
| redrawIncrement: number; | ||
| /** | ||
| * The tool to be selected by the canvas sketch library | ||
| */ | ||
| canvasToolType: CanvasToolType; | ||
| /** | ||
| * When true, displays a dashed border around the canvas to identify drawable area. When false, | ||
| * hides the dashed border around the canvas. | ||
| */ | ||
| showCanvasBorder: boolean; | ||
| /** | ||
| * The width of the selected tool for use by the selected tool | ||
| */ | ||
| toolWidth: number; | ||
| /** | ||
| * The color of the selected tool for use by the selected tool | ||
| */ | ||
| toolColor: string; | ||
| /** | ||
| * The object literal containing all information necessary to redraw the objects containing all | ||
| * data necessary to redraw the sketch canvas at a point in the history of the stack of drawing | ||
| * objects | ||
| */ | ||
| value: ReactCanvasSketchValue; | ||
| } | ||
|
|
||
| // #endregion Interfaces | ||
|
|
||
|
|
||
| // ------------------------------------------------------------------------------------------------- | ||
| // #region Component | ||
| // ------------------------------------------------------------------------------------------------- | ||
|
|
||
| const ReactCanvasSketch: React.FunctionComponent<ReactCanvasSketchProps> = ( | ||
| props: React.PropsWithChildren<ReactCanvasSketchProps> | ||
| ) => { | ||
|
|
||
| // track the ref of the canvas elements | ||
| const htmlCanvasBackgroundImage = React.createRef<HTMLCanvasElement>(); | ||
| const htmlCanvasSketch = React.createRef<HTMLCanvasElement>(); | ||
|
|
||
| // initialize state | ||
| const canvasSketchDefaultInstance: CanvasSketch = null as any; | ||
| const [isInitialized, setIsInitialized] = React.useState(false); | ||
| const [canvasSketch, setCanvasSketch] = React.useState(canvasSketchDefaultInstance); | ||
|
|
||
| // --------------------------------------------------------------------------------------------- | ||
| // #region Effect Hooks | ||
| // --------------------------------------------------------------------------------------------- | ||
|
|
||
| // initialization of canvas sketch - NOTE: Must be before other effects using canvasSketch! | ||
| useEffect(() => { | ||
| if (isInitialized) { | ||
| // already initialized, bail | ||
| return; | ||
| } | ||
| const { | ||
| backgroundImageUrl, | ||
| value, | ||
| } = { ...props }; | ||
|
|
||
| // set up the default options for the background image | ||
| const canvasSketchConfig: CanvasSketchConfig = { | ||
| backgroundImage: { src: backgroundImageUrl }, | ||
| backgroundImageCanvas: htmlCanvasBackgroundImage.current as HTMLCanvasElement, | ||
| currentObjectIndex: value.currentObjectIndex, | ||
| objectStack: value.objects, | ||
| panX: 0, | ||
| panY: 0, | ||
| scaleFactor: 1, | ||
| sketchCanvas: htmlCanvasSketch.current as HTMLCanvasElement, | ||
| }; | ||
|
|
||
| // initialize the canvas sketch object | ||
| const newCanvasSketch = new CanvasSketch(canvasSketchConfig); | ||
|
|
||
| // set state | ||
| setCanvasSketch(newCanvasSketch); | ||
| setIsInitialized(true); | ||
| }, [ | ||
| canvasSketch, | ||
| htmlCanvasBackgroundImage, | ||
| htmlCanvasSketch, | ||
| isInitialized, | ||
| props | ||
| ]); | ||
|
|
||
| // cleanup of canvas sketch when unmounting component | ||
| useEffect(() => { | ||
| return () => { | ||
| // cleanup on unmount | ||
| if (canvasSketch != null) { | ||
| canvasSketch.dispose(); | ||
| } | ||
| } | ||
| }, [canvasSketch]); | ||
|
|
||
| // redraws current state when dimensions of container or canvas change | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.redrawCurrentState(); | ||
| }, [ | ||
| canvasSketch, | ||
| props.canvasHeight, | ||
| props.canvasWidth, | ||
| props.containerHeight, | ||
| props.containerWidth, | ||
| ]); | ||
|
|
||
| // redraw the sketch canvas when current object index or objects changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.redrawSketchAt(props.value.objects, props.value.currentObjectIndex); | ||
| }, [ | ||
| canvasSketch, | ||
| props.value.currentObjectIndex, | ||
| props.value.objects, | ||
| ]); | ||
|
|
||
| // redraw the sketch canvas when redraw increment changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.redrawSketch(); | ||
| }, [ | ||
| canvasSketch, | ||
| props.redrawIncrement, | ||
| ]); | ||
|
|
||
| // redraw the background image when it changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.redrawBackgroundImageUsing(props.backgroundImageUrl); | ||
| }, [ | ||
| props.backgroundImageUrl, | ||
| canvasSketch | ||
| ]); | ||
|
|
||
| // set on added stroke when changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.setOnAddedToolStroke(props.onAddedStroke); | ||
| }, [ | ||
| props.onAddedStroke, | ||
| canvasSketch | ||
| ]); | ||
|
|
||
| // set tool color when it changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.setToolColor(props.toolColor); | ||
| }, [ | ||
| props.toolColor, | ||
| canvasSketch | ||
| ]); | ||
|
|
||
| // set tool width when it changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.setToolWidth(props.toolWidth); | ||
| }, [ | ||
| props.toolWidth, | ||
| canvasSketch | ||
| ]); | ||
|
|
||
| // set selected tool when it changes | ||
| useEffect(() => { | ||
| if (canvasSketch == null) { | ||
| return; | ||
| } | ||
| canvasSketch.setSelectedTool(props.canvasToolType); | ||
| }, [ | ||
| props.canvasToolType, | ||
| canvasSketch | ||
| ]); | ||
|
|
||
| // #endregion Effect Hooks | ||
|
|
||
|
|
||
| // configure styles for elemtns | ||
| const canvasContainerStyles: React.CSSProperties = { | ||
| height: props.canvasHeight, | ||
| position: "relative", | ||
| width: props.canvasWidth, | ||
| }; | ||
| const sketchStyles: React.CSSProperties = { | ||
| height: props.containerHeight, | ||
| width: props.containerWidth, | ||
| }; | ||
| const canvasStyles: React.CSSProperties = { | ||
| position: "absolute", | ||
| }; | ||
| if (props.showCanvasBorder) { | ||
| canvasStyles.border = "1px dashed black"; | ||
| } | ||
|
|
||
| return ( | ||
| <div | ||
| className={`c-react-canvas-sketch ${props.className}`} | ||
| style={sketchStyles}> | ||
| <div className="c-react-canvas-sketch__canvas-container" style={canvasContainerStyles}> | ||
| <canvas | ||
| className="c-react-canvas-sketch__background-image" | ||
| height={props.canvasHeight} | ||
| ref={htmlCanvasBackgroundImage} | ||
| style={{ ...canvasStyles, zIndex: 0 }} | ||
| width={props.canvasWidth} /> | ||
| <canvas | ||
| className="c-react-canvas-sketch__field" | ||
| height={props.canvasHeight} | ||
| ref={htmlCanvasSketch} | ||
| style={{ ...canvasStyles, zIndex: 1 }} | ||
| width={props.canvasWidth}> | ||
| Sorry, Canvas HTML5 element is not supported by your browser :( | ||
| </canvas> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export { ReactCanvasSketch }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would recommend adding some JSDoc commentary around these props (same goes for the component above)