{"id":16810311,"url":"https://github.com/personalizedrefrigerator/js-draw","last_synced_at":"2025-04-05T08:03:46.261Z","repository":{"id":59105132,"uuid":"527429487","full_name":"personalizedrefrigerator/js-draw","owner":"personalizedrefrigerator","description":"Draw pictures using a pen, touchscreen, or mouse! JS-draw is a freehand drawing library for JavaScript and TypeScript.","archived":false,"fork":false,"pushed_at":"2025-03-26T21:06:53.000Z","size":9978,"stargazers_count":139,"open_issues_count":19,"forks_count":15,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-29T07:02:02.062Z","etag":null,"topics":["drawing","drawing-app","freehand-drawing","infinite-zoom","javascript","svg","touchscreen","typescript"],"latest_commit_sha":null,"homepage":"https://personalizedrefrigerator.github.io/js-draw/typedoc/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/personalizedrefrigerator.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-22T05:51:54.000Z","updated_at":"2025-03-26T21:06:57.000Z","dependencies_parsed_at":"2023-12-24T02:21:10.614Z","dependency_job_id":"670d8d94-c3c0-4644-a4e2-6460ff505d11","html_url":"https://github.com/personalizedrefrigerator/js-draw","commit_stats":{"total_commits":1577,"total_committers":3,"mean_commits":525.6666666666666,"dds":0.003170577045022216,"last_synced_commit":"92744d929ab96ac1688c13ae6049c2852194b0b3"},"previous_names":[],"tags_count":63,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/personalizedrefrigerator%2Fjs-draw","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/personalizedrefrigerator%2Fjs-draw/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/personalizedrefrigerator%2Fjs-draw/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/personalizedrefrigerator%2Fjs-draw/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/personalizedrefrigerator","download_url":"https://codeload.github.com/personalizedrefrigerator/js-draw/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247305930,"owners_count":20917207,"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":["drawing","drawing-app","freehand-drawing","infinite-zoom","javascript","svg","touchscreen","typescript"],"created_at":"2024-10-13T10:15:01.499Z","updated_at":"2025-04-05T08:03:46.243Z","avatar_url":"https://github.com/personalizedrefrigerator.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\n    \u003cimg height=\"190\" src=\"docs/img/readme-images/logo.svg\" alt=\"js-draw logo\"/\u003e\n\u003c/div\u003e\n\n\u003ch1 align=\"center\"\u003ejs-draw\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\n\n[NPM package](https://www.npmjs.com/package/js-draw) | [GitHub](https://github.com/personalizedrefrigerator/js-draw) | [Documentation](https://personalizedrefrigerator.github.io/js-draw/typedoc/modules/js-draw.html) | [Try it!](https://personalizedrefrigerator.github.io/js-draw/example/example.html)\n\n\u003c/div\u003e\n\n![](docs/img/readme-images/js-draw.png)\n\nFor example usage, see [one of the examples](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/examples.md) or read [the documentation](https://personalizedrefrigerator.github.io/js-draw/typedoc/).\n\n# Features\n\n## Very large zoom range\n\nA core feature of `js-draw` is its [large zoom range](https://personalizedrefrigerator.github.io/js-draw/typedoc/interfaces/js-draw.EditorSettings.html#maxZoom) (from roughly 10⁻¹⁰x to 10¹⁰x).\n\n\u003cdetails open\u003e\u003csummary\u003e\u003cstrong\u003eDemo\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cvideo src=\"https://github.com/personalizedrefrigerator/js-draw/assets/46334387/f1c4afea-d7c8-4c36-835b-e01f2a646424\" alt=\"Video: Shows zooming out further and further\" controls\u003e\u003c/video\u003e\n\n\u003c/details\u003e\n\nApplications using `js-draw` can adjust this zoom range with custom [EditorSettings](https://personalizedrefrigerator.github.io/js-draw/typedoc/interfaces/js-draw.EditorSettings.html).\n\n## Touchscreen and stylus support\n\n`js-draw` supports touchscreen pinch zoom and rotate gestures. To simplify editing, screen rotation snaps to multiples of 90 degrees.\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eDemo\u003c/strong\u003e\u003c/summary\u003e\n    \n\u003cvideo src=\"https://github.com/personalizedrefrigerator/js-draw/assets/46334387/1f4ebeb1-9d2f-4884-9410-9fb1d5e455ee\" alt=\"Video: Shows canvas being rotated\" controls\u003e\u003c/video\u003e\n\n\u003c/details\u003e\n\nIt's also possible to disable touch drawing. This can be useful when drawing with a stylus and can be done with either [PanZoomTool.setMode](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.PanZoomTool.html#setMode) or, by a user, with the \"hand\" tool menu:\n\n\u003cimg alt=\"screenshot: Hand tool menu at the bottom of the screen includes 'lock rotation' and 'touchscreen panning'. 'Touchscreen panning' is highlighted.\" src=\"https://github.com/personalizedrefrigerator/js-draw/assets/46334387/d96e2df5-6132-4122-954d-d25402754bc2\" width=\"400\"/\u003e\n\n## User-configurable tools\n\nWith the default toolbar, users can change the pen style, color, and more:\n\n\u003cimg alt=\"screenshot: Pen configuration menu includes pen size, color, type, stabilization, autocorrect,...\" src=\"https://github.com/personalizedrefrigerator/js-draw/assets/46334387/476ad0e4-1f95-43bf-925d-549292d141e3\" width=\"400\"/\u003e\n\nIt's possible for applications using `js-draw` to [add custom pen types](https://personalizedrefrigerator.github.io/js-draw/typedoc/modules/Additional_Documentation.CustomizingTools__.html) that can also be customized in this way. It's also possible to [save the toolbar state](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/examples/example-save-restore-toolbar-state/example.ts) and restore it after reloading the app.\n\n## More features\n\n`js-draw` also supports:\n\n- \u003cdetails\u003e\u003csummary\u003ePartial stroke erasing\u003c/summary\u003e\n\n  \u003cvideo src=\"https://github.com/personalizedrefrigerator/js-draw/assets/46334387/c8c2b8d5-5537-4df8-a8b5-899c2d7ea5ce\"\u003e\u003c/video\u003e\n\n  \u003c/details\u003e\n\n- [Collaborative editing](https://github.com/personalizedrefrigerator/js-draw/tree/main/docs/examples/example-collaborative)\n- Saving to and loading from a subset of SVG\n\n# API\n\n## Creating an `Editor`\n\n### With a bundler that supports importing `.css` files\n\nTo create a new `Editor` and add it as a child of `document.body`, use the [Editor](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#constructor) constructor:\n\n```ts\nimport Editor from 'js-draw';\nimport 'js-draw/styles';\n\nconst editor = new Editor(document.body);\n```\n\nThe `import js-draw/styles` step requires a bundler that can import `.css` files. For example, [`webpack` with `css-loader`.](https://webpack.js.org/loaders/css-loader/)\n\n### With a bundler that doesn't support importing `.css` files\n\nImport the pre-bundled version of the editor to apply CSS after loading the page.\n\n```ts\nimport Editor from 'js-draw';\nimport 'js-draw/bundledStyles';\n\nconst editor = new Editor(document.body);\n```\n\n`js-draw/bundledStyles` is a version of the editor's stylesheets pre-processed by `es-build`. As such, `import`ing or including it with a `\u003cscript src=\"...\"\u003e\u003c/script\u003e` tag applies editor-specific CSS to the document.\n\n### Without a bundler\n\nIf you're not using a bundler, consider using the pre-bundled editor:\n\n```html\n\u003c!-- Replace 1.0.0 with the latest version of js-draw --\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/js-draw@1.0.0/dist/bundle.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const editor = new jsdraw.Editor(document.body);\n  editor.addToolbar();\n  editor.getRootElement().style.height = '600px';\n\u003c/script\u003e\n```\n\n**Note**: To ensure the CDN-hosted version of `js-draw` hasn't been tampered with, consider [including an `integrity=\"...\"` attribute](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). [Read more about using SRI with JSDelivr](https://www.jsdelivr.com/using-sri-with-dynamic-files).\n\n## Adding a toolbar\n\nTo create a toolbar with buttons for the default tools:\n\n```ts\nconst toolbar = editor.addToolbar();\n```\n\nSave and exit buttons can be added with the [`.addSaveButton`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.AbstractToolbar.html#addSaveButton) and [`.addExitButton`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.AbstractToolbar.html#addExitButton) methods:\n\n```ts\ntoolbar.addSaveButton(() =\u003e {\n  const svgElem = editor.toSVG();\n  console.log('The saved SVG:', svgElem.outerHTML);\n});\n\ntoolbar.addExitButton(() =\u003e {\n  // Save here?\n\n  // Removes the editor from the document.\n  editor.remove();\n});\n```\n\nCustom actions can also be added to the toolbar. For example,\n\n```ts\ntoolbar.addActionButton('Custom', () =\u003e {\n  // When the action button is pressed\n});\n```\n\nor alternatively, with an icon,\n\n```ts\ntoolbar.addActionButton(\n  {\n    label: 'Custom',\n    icon: editor.icons.makeSaveIcon(),\n  },\n  () =\u003e {\n    // Do something here\n  },\n);\n```\n\n## Loading from SVG\n\n```ts\neditor.loadFromSVG(`\n    \u003csvg\n        viewBox=\"156 74 200 150\"\n        width=\"200\" height=\"150\"\n    \u003e\n        \u003cpath d=\"M156,150Q190,190 209,217L213,215Q193,187 160,148M209,217Q212,218 236,178L232,176Q210,215 213,215M236,178Q240,171 307,95L305,93Q237,168 232,176M307,95Q312,90 329,78L327,74Q309,87 305,93\" fill=\"#07a837\"\u003e\u003c/path\u003e\n    \u003c/svg\u003e\n`);\n```\n\n**Note**: While `js-draw` supports only a small subset of the SVG markup language, it tries to preserve unrecognised SVG elements.\n\nFor example, although `js-draw` doesn't support `\u003ccircle/\u003e` elements,\n\n```xml\n\u003csvg\n    viewBox=\"156 74 200 150\"\n    width=\"200\" height=\"150\"\n\u003e\n    \u003cpath d=\"M156,150Q190,190 209,217L213,215Q193,187 160,148M209,217Q212,218 236,178L232,176Q210,215 213,215M236,178Q240,171 307,95L305,93Q237,168 232,176M307,95Q312,90 329,78L327,74Q309,87 305,93\" fill=\"#07a837\"\u003e\u003c/path\u003e\n    \u003ccircle cx=200 cy=100 r=40 fill='red'/\u003e\n\u003c/svg\u003e\n```\n\nrenders as\n\n\u003cimg alt=\"screenshot of the image editor, displaying a green checkmark. The circle is invisible\" src=\"docs/img/readme-images/unsupported-elements--in-editor.png\" width=\"600\"/\u003e\n\nbut exports to\n\n```xml\n\u003csvg viewBox=\"156 74 200 150\" width=\"200\" height=\"150\" version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cg\u003e\u003cpath d=\"M156,150M156,150Q190,190 209,217L213,215Q193,187 160,148M209,217M209,217Q212,218 236,178L232,176Q210,215 213,215M236,178M236,178Q240,171 307,95L305,93Q237,168 232,176M307,95M307,95Q312,90 329,78L327,74Q309,87 305,93\" fill=\"#07a837\"\u003e\u003c/path\u003e\u003c/g\u003e\u003ccircle cx=\"200\" cy=\"100\" r=\"40\" fill=\"red\"\u003e\u003c/circle\u003e\u003c/svg\u003e\n```\n\nwhich **does** contain the `\u003ccircle/\u003e` element.\n\n## Customizing the background\n\nThe background color and style can be customized with [editor.setBackgroundStyle](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#setBackgroundStyle). For example,\n\n```ts\nimport { Editor, Color4, BackgroundComponentBackgroundType } from 'js-draw';\nconst editor = new Editor(document.body);\n\neditor.dispatch(\n  editor.setBackgroundStyle({\n    color: Color4.orange,\n    type: BackgroundComponentBackgroundType.Grid,\n  }),\n);\n```\n\nAbove, we use `editor.dispatch` because `setBackgroundStyle` returns a [`Command`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Command.html), rather than changing the background style directly. `js-draw` uses `Command`s to track actions that can be undone and redone.\n\nBy default, `.dispatch` adds `Command`s to the undo stack. To avoid this, pass `false` for the second parameter to [`.dispatch`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#dispatch):\n\n```ts\nconst addToHistory = false;\neditor.dispatch(\n  editor.setBackgroundStyle({\n    color: Color4.orange,\n    type: BackgroundComponentBackgroundType.Grid,\n  }),\n  addToHistory,\n);\n```\n\n### Making the background fill the screen\n\nBy default, the background has a fixed size and marks the region that will be saved by [`.toSVG`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#toSVG) or [`.toDataURL`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#toDataURL). It's possible to make the background auto-resize to the content of the image with [`editor.image.setAutoresizeEnabled(true)`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.EditorImage.html#setAutoresizeEnabled):\n\n```ts\nconst editor = new Editor(document.body);\n\nconst addToHistory = false;\neditor.dispatch(editor.image.setAutoresizeEnabled(true), addToHistory);\n\n// Alternatively, using .setBackgroundStyle:\neditor.dispatch(editor.setBackgroundStyle({ autoresize: true }), addToHistory);\n```\n\n## Saving\n\nTo save as an SVG, use [`editor.toSVG()`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#toSVG), which returns an `HTMLSVGElement`. Alternatively, if working with very large images that need to be saved in the background, consider using [`editor.toSVGAsync()`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#toSVG).\n\nIt's also possible to render the editor to a PNG or JPEG data URL. This can be done with [`editor.toDataURL()`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#toDataURL).\n\nThe region of the image that will be saved can be changed by calling [`editor.image.setImportExportRect`](https://personalizedrefrigerator.github.io/js-draw/typedoc/classes/js-draw.Editor.html#setImportExportRect) or\n\n## Settings/configuration\n\n### Disabling touchpad panning\n\nTouchpad/mousewheel pan gestures can conflict with gestures used to scroll the document. To turn off touchpad pan gestures (and scrolling the editor with the mousewheel),\n\n```ts\nconst editor = new Editor(document.body, {\n  wheelEventsEnabled: false,\n});\n```\n\nAlternatively, to only enable touchpad panning when the editor has focus,\n\n```ts\nconst editor = new Editor(document.body, {\n  wheelEventsEnabled: 'only-if-focused',\n});\n```\n\n### Localization\n\nIf a user's language is available in [src/localizations/](packages/js-draw/src/localizations) (as determined by `navigator.languages`), that localization will be used.\n\nTo override the default language, use `getLocalizationTable([ 'custom locale here' ])`. For example,\n\n```ts\nconst editor = new Editor(document.body, {\n  // Force the Spanish (Español) localizaiton\n  localization: getLocalizationTable(['es']),\n});\n```\n\n\u003cdetails\u003e\u003csummary\u003eCreating a custom localization\u003c/summary\u003e\n\nSee [src/localization.ts](packages/js-draw/src/localization.ts) for a list of strings that can be translated.\n\nMany of the default strings in the editor might be overridden like this:\n\n```ts\nconst editor = new Editor(document.body, {\n    // Example partial Spanish localization\n    localization: {\n        // Not all translated strings need to be specified. If a string isn't given,\n        // the English (default) localization will be used\n\n        // Strings for the main editor interface\n        // (see packages/js-draw/src/localization.ts)\n        loading: (percentage: number) =\u003e `Cargando: ${percentage}%...`,\n        imageEditor: 'Editor de dibujos',\n\n        undoAnnouncement: (commandDescription: string) =\u003e `${commandDescription} fue deshecho`,\n        redoAnnouncement: (commandDescription: string) =\u003e `${commandDescription} fue rehecho`,\n\n        // Strings for the toolbar\n        // (see src/toolbar/localization.ts)\n        pen: 'Lapiz',\n        eraser: 'Borrador',\n        select: 'Selecciona',\n        thicknessLabel: 'Tamaño: ',\n        colorLabel: 'Color',\n\n        ...\n    },\n});\n```\n\n\u003c/details\u003e\n\n### Setting the minimum and maximum zoom\n\nBy default, the editor's minimum and maximum zoom are very large (2·10\u003csup\u003e-10\u003c/sup\u003ex and 10\u003csup\u003e12\u003c/sup\u003ex, respectively). These are configurable by the `minZoom` and `maxZoom` settings. For example,\n\n```ts\nconst editor = new Editor(document.body, {\n  minZoom: 0.5,\n  maxZoom: 2,\n});\n```\n\n## Changing the editor's color theme\n\nThe editor's color theme is specified using CSS. Its default theme looks like this:\n\n```css\n.imageEditorContainer {\n  /* Deafult colors for the editor -- light mode */\n\n  /* Used for unselected buttons and dialog text. */\n  --background-color-1: white;\n  --foreground-color-1: black;\n\n  /* Used for some menu/toolbar backgrounds. */\n  --background-color-2: #f5f5f5;\n  --foreground-color-2: #2c303a;\n\n  /* Used for other menu/toolbar backgrounds. */\n  --background-color-3: #e5e5e5;\n  --foreground-color-3: #1c202a;\n\n  /* Used for selected buttons. */\n  --selection-background-color: #cbdaf1;\n  --selection-foreground-color: #2c303a;\n\n  /* Used for dialog backgrounds */\n  --background-color-transparent: rgba(105, 100, 100, 0.5);\n\n  /* Used for shadows */\n  --shadow-color: rgba(0, 0, 0, 0.5);\n\n  /* Color used for some button/input foregrounds */\n  --primary-action-foreground-color: #15b;\n}\n\n@media (prefers-color-scheme: dark) {\n  .imageEditorContainer {\n    /* Default colors for the editor -- dark mode */\n    --background-color-1: #151515;\n    --foreground-color-1: white;\n\n    --background-color-2: #222;\n    --foreground-color-2: #efefef;\n\n    --background-color-3: #272627;\n    --foreground-color-3: #eee;\n\n    --selection-background-color: #607;\n    --selection-foreground-color: white;\n    --shadow-color: rgba(250, 250, 250, 0.5);\n    --background-color-transparent: rgba(50, 50, 50, 0.5);\n\n    --primary-action-foreground-color: #7ae;\n  }\n}\n```\n\nTo override it, use a more specific CSS selector to set the theme variables. For example,\n\n```css\n/* Notice the \"body\" below -- the selector needs to be more specific than what's in js-draw */\nbody .imageEditorContainer {\n  --background-color-1: green;\n  --foreground-color-1: black;\n\n  /* For this theme, use the same secondary and tertiary colors\n       (it's okay for them to be the same). */\n  --background-color-2: lime;\n  --foreground-color-2: black;\n  --background-color-3: lime;\n  --foreground-color-3: black;\n\n  --background-color-transparent: rgba(255, 240, 200, 0.5);\n  --shadow-color: rgba(0, 0, 0, 0.5);\n\n  --selection-background-color: yellow;\n  --selection-foreground-color: black;\n}\n```\n\ndisables the dark theme and creates a theme that primarily uses yellow/green colors.\n\nSee also [adjustEditorThemeForContrast](https://personalizedrefrigerator.github.io/js-draw/typedoc/functions/js-draw.adjustEditorThemeForContrast.html).\n\n# Examples and resources\n\n- [Examples](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/examples.md)\n- [How to add a custom pen type](https://personalizedrefrigerator.github.io/js-draw/typedoc/modules/Additional_Documentation.CustomizingTools__.html#md:adding-a-new-pen-type)\n- [A material icon theme for js-draw](https://personalizedrefrigerator.github.io/js-draw/typedoc/modules/_js-draw_material-icons.html#md:js-drawmaterial-icons)\n- [More documentation](https://personalizedrefrigerator.github.io/js-draw/typedoc/modules/Guides.html)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpersonalizedrefrigerator%2Fjs-draw","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpersonalizedrefrigerator%2Fjs-draw","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpersonalizedrefrigerator%2Fjs-draw/lists"}