{"id":13567826,"url":"https://github.com/ascorbic/react-artboard","last_synced_at":"2025-04-15T05:56:00.696Z","repository":{"id":39406331,"uuid":"330155189","full_name":"ascorbic/react-artboard","owner":"ascorbic","description":"A realistic paint component","archived":false,"fork":false,"pushed_at":"2022-04-09T10:07:35.000Z","size":3041,"stargazers_count":95,"open_issues_count":0,"forks_count":9,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-15T05:55:55.589Z","etag":null,"topics":["paint-application","painting","react"],"latest_commit_sha":null,"homepage":"https://react-artboard.netlify.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ascorbic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-16T12:14:29.000Z","updated_at":"2025-04-14T16:33:50.000Z","dependencies_parsed_at":"2022-09-08T09:42:48.415Z","dependency_job_id":null,"html_url":"https://github.com/ascorbic/react-artboard","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Freact-artboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Freact-artboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Freact-artboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Freact-artboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ascorbic","download_url":"https://codeload.github.com/ascorbic/react-artboard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249016320,"owners_count":21198832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["paint-application","painting","react"],"created_at":"2024-08-01T13:02:45.021Z","updated_at":"2025-04-15T05:56:00.678Z","avatar_url":"https://github.com/ascorbic.png","language":"TypeScript","readme":"# react-artboard\n\nA freeform sketching component for React.\n[Try the demo](https://react-artboard.netlify.app/)\n\n![artboard](https://raw.githubusercontent.com/ascorbic/react-artboard/main/artboard.png)\n\n![shading](https://raw.githubusercontent.com/ascorbic/react-artboard/main/images/shading.gif)\n\nreact-artboard includes an `Artboard` component and several tools, including a\nrealistic paintbrush, a marker pen and airbrush, as well as the abstract shading\ntool. Tools are implemented as custom hooks, so you can add your own brushes and\nother tools.\n\n[Try the demo](https://react-artboard.netlify.app/)\n\n- [react-artboard](#react-artboard)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n    - [`Artboard`](#artboard)\n      - [Props](#props)\n    - [Paintbrush](#paintbrush)\n      - [Options](#options)\n    - [Shading](#shading)\n      - [Options](#options-1)\n    - [Watercolor](#watercolor)\n      - [Options](#options-2)\n    - [Marker pen](#marker-pen)\n      - [Options](#options-3)\n    - [Airbrush](#airbrush)\n      - [Options](#options-4)\n  - [Custom brushes](#custom-brushes)\n  - [History](#history)\n  - [Sources](#sources)\n\n## Installation\n\n```shell\nnpm install react-artboard\n```\n\n## Usage\n\nFor a full usage example, see\n[this file](https://github.com/ascorbic/react-artboard/blob/main/example/App.tsx).\nThe simplest usage of the component is like this:\n\n```jsx\nimport React from \"react\";\nimport { useBrush, Artboard } from \"react-artboard\";\nexport function App() {\n  const brush = useBrush({ color: \"#663399\", strokeWidth: 40 });\n\n  return \u003cArtboard tool={brush} style={{ width: 800, height: 600 }} /\u003e;\n}\n```\n\nYou probably want to allow users to change the colors and size of the brush.\nHere is an example that uses native `color` and `range` inputs:\n\n```jsx\nimport React, { useState } from \"react\";\nimport { useBrush, Artboard } from \"react-artboard\";\nexport function App() {\n  const [color, setColor] = useState(\"#993366\");\n  const [strokeWidth, setStrokeWidth] = useState(40);\n  const brush = useBrush({ color, strokeWidth });\n\n  return (\n    \u003cmain\u003e\n      \u003cdiv\u003e\n        \u003cinput\n          type=\"color\"\n          value={color}\n          onInput={(evt) =\u003e setColor(evt.currentTarget.value)}\n        /\u003e\n        \u003cinput\n          type=\"range\"\n          min={5}\n          max={50}\n          value={strokeWidth}\n          onInput={(evt) =\u003e setStrokeWidth(parseInt(evt.currentTarget.value))}\n        /\u003e\n      \u003c/div\u003e\n      \u003cArtboard tool={brush} style={{ width: 800, height: 600 }} /\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\nYou could use a custom component instead of these inputs if you want more\ncontrol over them, as long as they return a number for the brush size and a\nstring for the color.\n\nIf you want to export your creations or clear the canvas, you can use the ref\nlike this:\n\n```jsx\nimport React, { useState } from \"react\";\nimport { useBrush, Artboard } from \"react-artboard\";\nexport function App() {\n  const [color, setColor] = useState(\"#993366\");\n  const [strokeWidth, setStrokeWidth] = useState(40);\n  const brush = useBrush({ color, strokeWidth });\n\n  const [artboardRef, setArtboardRef] = useState();\n\n  return (\n    \u003cmain\u003e\n      \u003cdiv\u003e\n        \u003cbutton onClick={() =\u003e artboardRef?.download()}\u003eDownload\u003c/button\u003e\n        \u003cbutton onClick={() =\u003e artboardRef?.clear()}\u003eClear\u003c/button\u003e\n        \u003cinput\n          type=\"color\"\n          value={color}\n          onInput={(evt) =\u003e setColor(evt.currentTarget.value)}\n        /\u003e\n        \u003cinput\n          type=\"range\"\n          min={5}\n          max={50}\n          value={strokeWidth}\n          onInput={(evt) =\u003e setStrokeWidth(parseInt(evt.currentTarget.value))}\n        /\u003e\n      \u003c/div\u003e\n      \u003cArtboard tool={brush} style={{ width: 800, height: 600 }} /\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\n## API\n\n### `Artboard`\n\n#### Props\n\n- **`tool`**\n\n  This is the tool returned by the `useBrush()` hook. You can also implement\n  your own tools and pass them in here.\n\n- **`ref`**\n\n  This accepts a callback that will be passed a ref that you can use to make the\n  following calls:\n\n  - **`download`**: `(filename: string, type: string) =\u003e void`\n\n    Downloads the canvas as an image. You can pass in a filename (default\n    \"image.png\"), and a mimetype (default \"image/png\"). If you pass an\n    unsupported type it will fallback to PNG.\n\n  - **`getImageAsDataUri`**: `(type: string) =\u003e string | undefined`\n\n    Returns the image as a data URI, which can be displayed in an `\u003cimg\u003e` tag\n    for example.\n\n  - **`clear`**: `() =\u003e void`\n\n    Clears the image\n\n  - **`context`**: `CanvasRenderingContext2D | null | undefined`\n\n    Canvas rendering context\n\n- **`onStartStroke`**: `(point: Point) =\u003e void`\n\n  Callback at the start of a stroke\n\n- **`onContinueStroke`**: `(point: Point) =\u003e void`\n\n  Callback at the continuing of a stroke\n\n- **`onEndStroke`**: `() =\u003e void`\n\n  Callback at the end of a stroke\n\n### Paintbrush\n\n`useBrush(options)`\n\n![paint](https://raw.githubusercontent.com/ascorbic/react-artboard/main/images/paint.png)\n\n#### Options\n\n- **`color`** A CSS string color.\n- **`strokeWidth`** The width of the brush\n\n### Shading\n\n`useShadingBrush()`\n\nThis tools is inspired by [some blog posts](#sources), exploring the use of\n\"neighbour point\" sketching. It gives a fun, unusual effect that is similar to\npencil shading. It is highly configurable, giving quite different effects\naccording to the different parameters.\n\n![shading](https://raw.githubusercontent.com/ascorbic/react-artboard/main/images/shading.png)\n\n#### Options\n\n- **`color`** A CSS string color. _Default: `#000000`_\n- **`spreadFactor`** The length of the connecting line. A value of `1` means it\n  exactly joins the two points, while `0.5` only covers half the distance. A\n  value above `1` gives a \"fur\" effect as the line extends beyond the points.\n  _Default: 0.9_\n- **`distanceThreshold`** How near the point needs to be to join, in pixels.\n  _Default: 50_\n- **`neighbourStrokeWidth`** Width of the stroke joining the points. _Default:\n  1_\n- **`neighbourColor`** Color of the line joining the points. _Default: `color`\n  value with 0.2 alpha_\n\n### Watercolor\n\n`useWatercolor(options)`\n\n![paint](https://raw.githubusercontent.com/ascorbic/react-artboard/main/images/watercolor.png)\n\n#### Options\n\n- **`color`** A CSS string color.\n- **`strokeWidth`** The width of the brush\n\n### Marker pen\n\n`useMarker(options)`\n\n![marker](https://raw.githubusercontent.com/ascorbic/react-artboard/main/images/marker.png)\n\n#### Options\n\n- **`color`** A CSS string color.\n- **`strokeWidth`** The width of the brush\n\n### Airbrush\n\n`useAirbrush(options)`\n\n![airbrush](https://raw.githubusercontent.com/ascorbic/react-artboard/main/images/airbrush.png)\n\n#### Options\n\n- **`color`** A CSS string color.\n- **`strokeWidth`** The width of the brush\n\n## Custom brushes\n\nSee the source for `useBrush` to see how to create a brush. It must return an\nobject, with the following optional callbacks:\n\n- **`startStroke?`**:\n  `(point: Point, context: CanvasRenderingContext2D) =\u003e void`\n- **`continueStroke?`**:\n  `(point: Point, context: CanvasRenderingContext2D) =\u003e void`\n- **`endStroke?`**: `(context: CanvasRenderingContext2D) =\u003e void`\n- **`cursor?`**: `string` A CSS-compatible string for the cursor to display. You\n  can use the `circleCursor()` helper to display a resizable circle for the\n  cursor\n\n## History\n\nThe `useHistory()` hook allows undo/redo functionality. Pass it a `size` value\nto limit the size of the history stack. It returns and object with the\nfollowing:\n\n- **`history`**: The `History` object. Pass this to the `Artboard`.\n- **`undo()`**: Reverts the image to the previous state.\n- **`redo()`**: Move forward in history, if available.\n- **`canUndo`**: History is available to undo\n- **`canRedo`**: History is available to redo\n\n## Sources\n\nThese posts gave inspiration, particularly for the shading tool.\n\n- [Exploring canvas drawing techniques](http://perfectionkills.com/exploring-canvas-drawing-techniques/)\n- [Harmony brush adoption in Krita: Sketch](http://lukast.mediablog.sk/log/?p=347)\n\nInspiration for the watercolor tool:\n\n- [Generative watercolor in Processing](https://sighack.com/post/generative-watercolor-in-processing)\n- [How to hack a painting](https://tylerxhobbs.com/essays/2020/how-to-hack-a-painting)\n\n© Copyright [Matt Kane](https://mk.gg)\n([@ascorbic](https://github.com/ascorbic)) 2021. MIT Licence\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fascorbic%2Freact-artboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fascorbic%2Freact-artboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fascorbic%2Freact-artboard/lists"}