All examples
This page contains an index of all runnable examples in the main documentation. Additional examples can be found on GitHub.
Part of the goal of this page is to easily find broken examples.
Example 1
From page: documents/Guides.Getting_started.html
import Editor from 'js-draw'; import 'js-draw/styles'; const editor = new Editor(document.body); editor.addToolbar();
Example 2
From page: documents/Guides.Getting_started.html
import Editor from 'js-draw'; import 'js-draw/bundledStyles'; const editor = new Editor(document.body); editor.addToolbar();
Example 3
From page: documents/Guides.Getting_started.html
<!-- Replace 1.27.1 with the latest version of js-draw, which should be 1.28.0. --> <script src="https://cdn.jsdelivr.net/npm/js-draw@1.27.1/dist/bundle.js"></script> <script> const editor = new jsdraw.Editor(document.body); editor.addToolbar(); </script>
Example 4
From page: documents/Guides.Writing_a_theme.html
/* A yellowish theme! */ :root .imageEditorContainer { /* Try changing the below values and clicking run again! */ /* Unselected buttons and dialog text. */ --background-color-1: #ffff77; --foreground-color-1: black; /* Some menu/toolbar backgrounds. */ --background-color-2: #ffff99; --foreground-color-2: #111; /* menu/toolbar backgrounds. */ --background-color-3: #ffffaa; --foreground-color-3: #121100; /* Used for selected buttons. */ --selection-background-color: #9f7; --selection-foreground-color: #00f; /* Used for dialog backgrounds */ --background-color-transparent: rgba(0, 0, 100, 0.5); /* Used for shadows */ --shadow-color: rgba(0, 0, 0, 0.5); /* Color used for some button/input foregrounds */ --primary-action-foreground-color: #f00; /* Use light mode for controls. */ color-scheme: light; } ---ts--- import { Editor, makeEdgeToolbar, makeDropdownToolbar } from 'js-draw'; import 'js-draw/styles'; import { MaterialIconProvider } from '@js-draw/material-icons'; const makeToolbar = (newToolbar: boolean, editor: Editor) => { const toolbar = newToolbar ? makeEdgeToolbar(editor) : makeDropdownToolbar(editor); toolbar.addDefaults(); toolbar.addExitButton(() => { alert('Not implemented for this editor!'); }); toolbar.addSaveButton(() => { const saveData = editor.toSVG().outerHTML; // Do something with saveData alert('Not implemented for this editor!'); }); return toolbar; }; const makeEditor = async () => { const editor = new Editor(document.body, { iconProvider: new MaterialIconProvider(), wheelEventsEnabled: 'only-if-focused', }); // Template generated with https://js-draw.web.app/ await editor.loadFromSVG(` <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <g class="js-draw-image-background js-draw-image-background-grid js-draw-image-background-grid-25"> <path d="M500,500L500,0L0,0L0,500L500,500" fill="#f1ffa4"></path> <path d="M0,0L500,0M0,25L500,25M0,50L500,50M0,75L500,75M0,100L500,100M0,125L500,125M0,150L500,150M0,175L500,175M0,200L500,200M0,225L500,225M0,250L500,250M0,275L500,275M0,300L500,300M0,325L500,325M0,350L500,350M0,375L500,375M0,400L500,400M0,425L500,425M0,450L500,450M0,475L500,475M0,500L500,500M0,0L0,500M25,0L25,500M50,0L50,500M75,0L75,500M100,0L100,500M125,0L125,500M150,0L150,500M175,0L175,500M200,0L200,500M225,0L225,500M250,0L250,500M275,0L275,500M300,0L300,500M325,0L325,500M350,0L350,500M375,0L375,500M400,0L400,500M425,0L425,500M450,0L450,500M475,0L475,500M500,0L500,500" fill="none" stroke="#0e005b33" stroke-width=".7"></path> </g> </svg> `); let isNewToolbar = true; let toolbar = makeToolbar(isNewToolbar, editor); const toolbarSelector = document.createElement('button'); toolbarSelector.innerText = 'Change toolbar type'; document.body.appendChild(toolbarSelector); toolbarSelector.onclick = () => { isNewToolbar = !isNewToolbar; toolbar.remove(); toolbar = makeToolbar(isNewToolbar, editor); }; }; makeEditor();
Example 5
From page: documents/Guides.Components.Adding_and_modifying_components.html
import { Editor, EditorImage, Stroke, Path, Color4, } from 'js-draw'; // 1. const editor = new Editor(document.body); editor.addToolbar(); // Create path data that we'll use to make the stroke. const path = Path.fromString('m0,0 l0,40 l40,4 l0,-48 z'); // 2. const stroke = Stroke.fromFilled( path, Color4.red, ); // 3. const command = editor.image.addComponent(stroke); // 4. editor.dispatch(command);
Example 6
From page: documents/Guides.Components.Adding_and_modifying_components.html
import { Editor, EditorImage, Stroke, Path, Color4, uniteCommands, } from 'js-draw'; const editor = new Editor(document.body); editor.addToolbar(); // 1. const commands = []; for (let x = 0; x < 100; x += 10) { for (let y = 0; y < 100; y += 10) { // 2. Try changing these! const strokeWidth = 3; const strokeColor = Color4.orange; // 3. const stroke = Stroke.fromStroked( // A path that starts at (i,i) then moves three units to the right `m${x},${y} l3,0`, { width: strokeWidth, color: strokeColor }, ); const command = editor.image.addComponent(stroke); commands.push(command); } } // 4. const compoundCommand = uniteCommands(commands); editor.dispatch(compoundCommand);
Example 7
From page: documents/Guides.Components.Adding_and_modifying_components.html
---use-previous--- ---visible--- // This example starts by running the code from the previous example -- // make sure the previous example compiles! import { Rect2 } from 'js-draw'; // 1. const components = editor.image.getComponentsIntersecting( new Rect2( // a 2D rectangle 5, // x 6, // y 60, // width 30, // height ) ); // 2. const styleCommands = []; for (const component of components) { // Only process Strokes -- there **are** other types of components. if (!(component instanceof Stroke)) continue; const command = component.updateStyle({ color: Color4.red }); styleCommands.push(command); } // 3. editor.dispatch(uniteCommands(styleCommands));
Example 8
From page: documents/Guides.Components.Adding_and_modifying_components.html
---use-previous--- ---visible--- // This example starts by running the code from the previous example -- // make sure the previous example compiles! import { Mat33, Vec2 } from 'js-draw'; const transformCommands = []; for (const component of components) { const command = component.transformBy( Mat33.translation(Vec2.of(45, 0)) ); transformCommands.push(command); } editor.dispatch(uniteCommands(transformCommands));
Example 9
From page: documents/Guides.Components.Adding_and_modifying_components.html
---use-previous--- ---visible--- // This example starts by running the code from the previous example -- // make sure the previous example compiles! import { Erase } from 'js-draw'; // Deletes all components found in the previous steps const eraseCommand = new Erase(components); editor.dispatch(eraseCommand);
Example 10
From page: documents/Guides.Components.Custom_components.html
import { Editor } from 'js-draw'; const editor = new Editor(document.body); editor.addToolbar();
Example 11
From page: documents/Guides.Components.Custom_components.html
---use-previous--- ---visible--- import { LineSegment2, Mat33, Rect2, Color4 } from '@js-draw/math'; import { AbstractRenderer, AbstractComponent } from 'js-draw'; const componentId = 'example'; class ExampleComponent extends AbstractComponent { protected contentBBox: Rect2; public constructor() { super(componentId); // For now, create a 50x50 bounding box centered at (0,0). // We'll update this later: this.contentBBox = new Rect2(0, 0, 50, 50); } public override render(canvas: AbstractRenderer, _visibleRect?: Rect2): void { // Be sure that everything drawn between .startObject and .endObject is within contentBBox. // Content outside contentBBox may not be drawn in all cases. canvas.startObject(this.contentBBox); // _visibleRect is the part of the image that's currently visible. We can // ignore it for now. canvas.fillRect(this.contentBBox, Color4.red); // Ends the object and attaches any additional metadata attached by an image loader // (e.g. if this object was created by SVGLoader). canvas.endObject(this.getLoadSaveData()); } // Must be implemented by all components, used for things like erasing and selection. public intersects(line: LineSegment2) { // TODO: For now, our component won't work correctly if the user tries to select it. // We'll implement this later. return false; } protected applyTransformation(transformation: Mat33): void { // TODO: Eventually, this should move the component. We'll implement this later. } protected createClone(): AbstractComponent { return new ExampleComponent(); } public description(): string { // This should be a brief description of the component's content (for // accessibility tools) return 'a red box'; } protected serializeToJSON() { return JSON.stringify({ // Some data to save (for collaborative editing) }); } } // data: The data serialized by serlailzeToJSON AbstractComponent.registerComponent(componentId, data => { // TODO: Deserialize the component from [data]. This is used if collaborative // editing is enabled. return new ExampleComponent(); }); // Add the component editor.dispatch(editor.image.addComponent(new ExampleComponent()));
Example 12
From page: documents/Guides.Components.Custom_components.html
import { Editor } from 'js-draw'; const editor = new Editor(document.body); editor.addToolbar(); ---visible--- import { LineSegment2, Mat33, Rect2, Color4 } from '@js-draw/math'; import { AbstractRenderer, AbstractComponent } from 'js-draw'; const componentId = 'example'; class ExampleComponent extends AbstractComponent { protected contentBBox: Rect2; // NEW: Stores the scale/rotation/position. "Transform" is short for "transformation". private transform: Mat33; // NEW: Stores the untransformed bounding box of the component. If the component hasn't // been moved/scaled yet, initialBBox should completely contain the component's content. private initialBBox: Rect2; public constructor(transform: Mat33) { super(componentId); this.transform = transform; this.initialBBox = new Rect2(0, 0, 50, 50); this.updateBoundingBox(); } // NEW: Updates this.contentBBox. Should be called whenever this.transform changes. private updateBoundingBox() { this.contentBBox = this.initialBBox.transformedBoundingBox( this.transform, ); } public override render(canvas: AbstractRenderer, _visibleRect?: Rect2): void { canvas.startObject(this.contentBBox); // Everything between .pushTransform and .popTransform will be scaled/rotated by this.transform. // Try removing the .pushTransform and .popTransform lines! canvas.pushTransform(this.transform); canvas.fillRect(this.initialBBox, Color4.red); canvas.popTransform(); // After the call to .popTransform, this.transform is no longer transforming the canvas. // Try uncommenting the following line: // canvas.fillRect(this.initialBBox, Color4.orange); // What happens when the custom component is selected and moved? // What happens to the orange rectangle when the red rectangle is moved offscreen? canvas.endObject(this.getLoadSaveData()); } public intersects(line: LineSegment2) { // Our component is currently just a rectangle. As such (for some values of this.transform), // we can use the Rect2.intersectsLineSegment method here: const intersectionCount = this.contentBBox.intersectsLineSegment(line).length; return intersectionCount > 0; // What happpens if you always return `true` here? } protected applyTransformation(transformUpdate: Mat33): void { // `.rightMul`, "right matrix multiplication" combines two transformations. // The transformation on the left is applied **after** the transformation on the right. // As such, `transformUpdate.rightMul(this.transform)` means that `this.transform` // will be applied **before** the `transformUpdate`. this.transform = transformUpdate.rightMul(this.transform); this.updateBoundingBox(); } protected createClone(): AbstractComponent { const clone = new ExampleComponent(this.transform); return clone; } public description(): string { return 'a red box'; } protected serializeToJSON() { return JSON.stringify({ // TODO: Some data to save (for collaborative editing) }); } } // data: The data serialized by serlailzeToJSON AbstractComponent.registerComponent(componentId, data => { // TODO: Deserialize the component from [data]. This is used if collaborative // editing is enabled. return new ExampleComponent(Mat33.identity); }); // Add the component const initialTransform = Mat33.identity; editor.dispatch(editor.image.addComponent(new ExampleComponent(initialTransform)));
Example 13
From page: documents/Guides.Components.Custom_components.html
import { Editor, SVGLoaderPlugin, Stroke } from 'js-draw'; import { Color4 } from '@js-draw/math'; let nextX = 0; const testPlugin: SVGLoaderPlugin = { async visit(node, loader) { if (node.tagName.toLowerCase() === 'text') { const testComponent = Stroke.fromFilled( `m${nextX},0 l50,0 l0,50 z`, Color4.red, ); nextX += 100; loader.addComponent(testComponent); return true; } // Return false to do the default image loading return false; } }; const editor = new Editor(document.body, { svg: { loaderPlugins: [ testPlugin ], } }); editor.addToolbar(); // With the loader plugin, text objects are converted to red triangles. editor.loadFromSVG(` <svg> <text>test</text> <text y="50">test 2</text> <text y="100">test 3</text> </svg> `);
Example 14
From page: documents/Guides.Components.Custom_components.html
import { Editor } from 'js-draw'; import { LineSegment2, Mat33, Rect2, Color4 } from '@js-draw/math'; import { AbstractRenderer, AbstractComponent } from 'js-draw'; const componentId = 'example'; class ExampleComponent extends AbstractComponent { protected contentBBox: Rect2; private transform: Mat33; private initialBBox: Rect2; public constructor(transform: Mat33) { super(componentId); this.transform = transform; this.initialBBox = new Rect2(0, 0, 50, 50); this.updateBoundingBox(); } private updateBoundingBox() { this.contentBBox = this.initialBBox.transformedBoundingBox( this.transform, ); } public override render(canvas: AbstractRenderer, _visibleRect?: Rect2): void { canvas.startObject(this.contentBBox); canvas.pushTransform(this.transform); canvas.fillRect(this.initialBBox, Color4.red); canvas.popTransform(); const containerClassNames = ['comp--example-component']; canvas.endObject(this.getLoadSaveData(), containerClassNames); } public intersects(line: LineSegment2) { // Our component is currently just a rectangle. As such (for some values of this.transform), // we can use the Rect2.intersectsLineSegment method here: const intersectionCount = this.contentBBox.intersectsLineSegment(line).length; return intersectionCount > 0; // What happpens if you always return `true` here? } protected applyTransformation(transformUpdate: Mat33): void { // `.rightMul`, "right matrix multiplication" combines two transformations. // The transformation on the left is applied **after** the transformation on the right. // As such, `transformUpdate.rightMul(this.transform)` means that `this.transform` // will be applied **before** the `transformUpdate`. this.transform = transformUpdate.rightMul(this.transform); this.updateBoundingBox(); } protected createClone(): AbstractComponent { const clone = new ExampleComponent(this.transform); return clone; } public description(): string { return 'a red box'; } protected serializeToJSON() { return JSON.stringify({ // TODO: Some data to save (for collaborative editing) }); } } AbstractComponent.registerComponent(componentId, data => { // TODO: return new ExampleComponent(Mat33.identity); }); const plugin: SVGLoaderPlugin = { async visit(node, loader) { if (node.classList.contains('comp--example-component')) { // TODO: Set the transformation matrix correctly -- get this information // from the `node`. This isn't too important for copy/paste support. const customComponent = new ExampleComponent(Mat33.identity); loader.addComponent(customComponent); return true; } // Return false to do the default image loading return false; }, }; const editor = new Editor(document.body, { svg: { loaderPlugins: [ plugin ], }, }); editor.addToolbar(); // Add the component const initialTransform = Mat33.identity; editor.dispatch(editor.image.addComponent(new ExampleComponent(initialTransform)));
Example 15
From page: documents/Guides.Components.Custom_components.html
import { Editor, invertCommand, SerializableCommand, EditorEventType } from 'js-draw'; const editor1 = new Editor(document.body); // Store the toolbar in a variable -- we'll use it later const toolbar = editor1.addToolbar(); const editor2 = new Editor(document.body); editor2.addToolbar(); const applySerializedCommand = (serializedCommand: any, editor: Editor) => { const command = SerializableCommand.deserialize(serializedCommand, editor); command.apply(editor); }; const applyCommandsToOtherEditor = (sourceEditor: Editor, otherEditor: Editor) => { sourceEditor.notifier.on(EditorEventType.CommandDone, (evt) => { // Type assertion. if (evt.kind !== EditorEventType.CommandDone) { throw new Error('Incorrect event type'); } if (evt.command instanceof SerializableCommand) { const serializedCommand = evt.command.serialize(); applySerializedCommand(serializedCommand, otherEditor); } else { console.log('Nonserializable command'); } }); sourceEditor.notifier.on(EditorEventType.CommandUndone, (evt) => { // Type assertion. if (evt.kind !== EditorEventType.CommandUndone) { throw new Error('Incorrect event type'); } if (evt.command instanceof SerializableCommand) { const serializedCommand = invertCommand(evt.command).serialize(); applySerializedCommand(serializedCommand, otherEditor); } else { console.log('Nonserializable command'); } }); }; applyCommandsToOtherEditor(editor1, editor2); applyCommandsToOtherEditor(editor2, editor1);
Example 16
From page: documents/Guides.Components.Custom_components.html
---use-previous--- import { Editor } from 'js-draw'; import { LineSegment2, Mat33, Rect2, Color4 } from '@js-draw/math'; import { AbstractRenderer, AbstractComponent } from 'js-draw'; const componentId = 'example'; class ExampleComponent extends AbstractComponent { protected contentBBox: Rect2; private transform: Mat33; private initialBBox: Rect2; public constructor(transform: Mat33) { super(componentId); this.transform = transform; this.initialBBox = new Rect2(0, 0, 50, 50); this.updateBoundingBox(); } private updateBoundingBox() { this.contentBBox = this.initialBBox.transformedBoundingBox( this.transform, ); } public override render(canvas: AbstractRenderer, _visibleRect?: Rect2): void { canvas.startObject(this.contentBBox); canvas.pushTransform(this.transform); canvas.fillRect(this.initialBBox, Color4.red); canvas.popTransform(); const containerClassNames = ['comp--example-component']; canvas.endObject(this.getLoadSaveData(), containerClassNames); } public intersects(line: LineSegment2) { // Our component is currently just a rectangle. As such (for some values of this.transform), // we can use the Rect2.intersectsLineSegment method here: const intersectionCount = this.contentBBox.intersectsLineSegment(line).length; return intersectionCount > 0; // What happpens if you always return `true` here? } protected applyTransformation(transformUpdate: Mat33): void { // `.rightMul`, "right matrix multiplication" combines two transformations. // The transformation on the left is applied **after** the transformation on the right. // As such, `transformUpdate.rightMul(this.transform)` means that `this.transform` // will be applied **before** the `transformUpdate`. this.transform = transformUpdate.rightMul(this.transform); this.updateBoundingBox(); } protected createClone(): AbstractComponent { const clone = new ExampleComponent(this.transform); return clone; } public description(): string { return 'a red box'; } ---visible--- // ...other component logic... protected serializeToJSON() { return JSON.stringify({ // NEW: Save the transform matrix: transform: this.transform.toArray(), }); } } AbstractComponent.registerComponent(componentId, data => { const transformArray = JSON.parse(data).transform; // NEW: Validation if (!Array.isArray(transformArray)) { throw new Error('data.transform must be an array'); } for (const entry of transformArray) { if (!isFinite(entry)) { throw new Error(`Non-finite entry in transform: ${entry}`); } } // NEW: Create and return the component from the data const transform = new Mat33(...transformArray); return new ExampleComponent(transform); }); // Make a button that adds the component function makeAddIcon() { const container = document.createElement('div'); container.textContent = '+'; return container; } toolbar.addActionButton({ icon: makeAddIcon(), label: 'Add test component', }, () => { const initialTransform = Mat33.identity; const component = new ExampleComponent(initialTransform); // The addAndCenterComponents method automatically selects, // centers, and adds the provided components to the editor. // // We could also add the component using // editor.dispatch(editor.image.addComponent(component)); editor1.addAndCenterComponents([ component ]); });
Example 17
From page: documents/Guides.Components.Custom_components.html
import { Editor, CanvasRenderer, SVGRenderer } from 'js-draw'; import { LineSegment2, Mat33, Rect2, Color4 } from '@js-draw/math'; import { AbstractRenderer, AbstractComponent } from 'js-draw'; const componentId = 'example'; class ExampleComponent extends AbstractComponent { protected contentBBox: Rect2; private transform: Mat33; private initialBBox: Rect2; public constructor(transform: Mat33) { super(componentId); this.transform = transform; this.initialBBox = new Rect2(0, 0, 50, 50); this.updateBoundingBox(); } private updateBoundingBox() { this.contentBBox = this.initialBBox.transformedBoundingBox( this.transform, ); } public override render(canvas: AbstractRenderer, _visibleRect?: Rect2): void { canvas.startObject(this.contentBBox); canvas.pushTransform(this.transform); if (canvas instanceof CanvasRenderer) { canvas.drawWithRawRenderingContext(ctx => { ctx.strokeStyle = 'green'; // Draw a large number of rectangles const rectSize = 20; const maximumX = this.initialBBox.width - rectSize; for (let x = 0; x < maximumX; x += 2) { ctx.strokeRect(x, 0, rectSize, rectSize); } }); } else if (canvas instanceof SVGRenderer) { canvas.drawWithSVGParent(parent => { // Draw some text. Note that this can also // be done with canvas.drawText const text = document.createElementNS( 'http://www.w3.org/2000/svg', 'text', ); text.textContent = 'Text in an SVG element!'; text.setAttribute('x', '50'); text.setAttribute('y', '25'); text.style.fill = 'red'; parent.appendChild(text); }); } else { // Fallback for other renderers canvas.fillRect(this.initialBBox, Color4.red); } canvas.popTransform(); const containerClassNames = ['comp--example-component']; canvas.endObject(this.getLoadSaveData(), containerClassNames); } public intersects(line: LineSegment2) { return false; // TODO (see above sections for implementation) } protected applyTransformation(transformUpdate: Mat33): void { this.transform = transformUpdate.rightMul(this.transform); this.updateBoundingBox(); } protected createClone(): AbstractComponent { const clone = new ExampleComponent(this.transform); return clone; } public description(): string { return 'a red box'; // TODO (see examples above) } protected serializeToJSON() { return JSON.stringify({ // TODO: Some data to save (for collaborative editing) }); } } AbstractComponent.registerComponent(componentId, data => { // TODO: See above examples for how to implement this // Needed for collaborative editing throw new Error('Not implemented'); }); const editor = new Editor(document.body); editor.addToolbar(); // Add the component const initialTransform = Mat33.identity; editor.dispatch(editor.image.addComponent(new ExampleComponent(initialTransform))); // Preview the SVG output document.body.appendChild(editor.toSVG());
Example 18
From page: documents/Guides.Updating_the_viewport.html
import { Editor, Viewport, Mat33 } from 'js-draw'; const editor = new Editor(document.body); // 1 editor.addToolbar(); const command = Viewport.transformBy(Mat33.scaling2D(1/4)); // 2 editor.dispatch(command); // 3
Example 19
From page: documents/Guides.Updating_the_viewport.html
import { Editor, Viewport, Mat33 } from 'js-draw'; const editor = new Editor(document.body); // 1 editor.addToolbar(); ---visible--- const command = Viewport.transformBy(Mat33.scaling2D(1/4)); // 2 editor.dispatch(command, false); // false: Don't add to history // Alternatively, // command.apply(editor);
Example 20
From page: documents/Guides.Updating_the_viewport.html
import { Editor } from 'js-draw'; // Create an editor with a toolbar: const editor = new Editor(document.body); editor.addToolbar();
Example 21
From page: documents/Guides.Updating_the_viewport.html
---use-previous--- ---visible--- import { Color4, BackgroundComponentBackgroundType } from 'js-draw'; editor.dispatch( editor.setBackgroundStyle({ color: Color4.orange, type: BackgroundComponentBackgroundType.Grid, // Make the background autoresize so that it's always // visible: autoresize: true, }), );
Example 22
From page: documents/Guides.Updating_the_viewport.html
---use-previous--- ---visible--- import { Viewport } from 'js-draw'; import { Mat33, Vec2 } from '@js-draw/math'; // When moveLeftUpdate is applied to the viewport, it moves the // canvas to the left by 1 unit. This is the same as moving the viewport // to the right by one unit. const moveLeftUpdate = Mat33.translation(Vec2.of(-1, 0)); function update() { const moveLeftCommand = Viewport.transformBy(moveLeftUpdate); moveLeftCommand.apply(editor); requestAnimationFrame(update); } update();
Example 23
From page: documents/Guides.Updating_the_viewport.html
import { Editor } from 'js-draw'; import { Color4, BackgroundComponentBackgroundType } from 'js-draw'; const editor = new Editor(document.body); editor.addToolbar(); editor.dispatch( editor.setBackgroundStyle({ color: Color4.orange, type: BackgroundComponentBackgroundType.Grid, autoresize: true, }), ); ---visible--- import { Viewport } from 'js-draw'; import { Mat33, Vec2 } from '@js-draw/math'; let lastTime = performance.now(); function update() { // Get how long many milliseconds have elapsed since the last update. const nowTime = performance.now(); const millisecondsElapsed = nowTime - lastTime; const seconds = millisecondsElapsed / 1000; lastTime = nowTime; const moveLeftRate = -10; // units/second const moveLeftAmount = Vec2.of(moveLeftRate * seconds, 0); const moveLeftUpdate = Mat33.translation(moveLeftAmount); const moveLeftCommand = Viewport.transformBy( moveLeftUpdate ); moveLeftCommand.apply(editor); requestAnimationFrame(update); } update();
Example 24
From page: documents/Guides.Customizing_existing_tools.html
import { Editor, PenTool, Color4 } from 'js-draw'; import 'js-draw/styles'; const editor = new Editor(document.body, { wheelEventsEnabled: 'only-if-focused', }); // The toolbar can be added either before or after changing the tool. editor.addToolbar(); // Get all tools of type PenTool (we could also do this with an EraserTool). const penTools = editor.toolController.getMatchingTools(PenTool); // Get the second pen tool (somewhat fragile -- js-draw might change // the default toolbar configuration in a future major release). const secondPen = penTools[1]; secondPen.setThickness(200); secondPen.setColor(Color4.red);
Example 25
From page: documents/Guides.Customizing_existing_tools.html
import { Editor, PenTool, Color4, makeOutlinedCircleBuilder, } from 'js-draw'; import 'js-draw/styles'; const editor = new Editor(document.body, { wheelEventsEnabled: 'only-if-focused', }); const toolController = editor.toolController; const originalPenTools = toolController.getMatchingTools(PenTool); // Add a new pen after the existing const penStyle: PenStyle = { color: Color4.red, // Draw circles by default factory: makeOutlinedCircleBuilder, thickness: 4, }; const newPen = new PenTool(editor, 'My custom pen', penStyle); // Add after the first pre-existing pen tool toolController.insertToolsAfter(originalPenTools[0], [ newPen ]); // Remove the previous pen tools toolController.removeAndDestroyTools(originalPenTools); // Make the new pen a primary tool -- it disables other primary tools // when the user first enables it (like the default eraser/text tool/pens) toolController.addPrimaryTool(newPen); // Must be done after changing the tools: editor.addToolbar();
Example 26
From page: documents/Guides.Customizing_existing_tools.html
import { Editor, makePolylineBuilder, makeOutlinedCircleBuilder, ComponentBuilderFactory } from 'js-draw'; // A pen factory's job is to return a ComponentBuilder when starting a new stroke. // Below, we randomly choose between a circle ComponentBuilder and a polyline pen ComponentBuilder. // // Parameters: // startPoint - the first point on the stroke. // viewport - information about the current rotation/scale of the drawing canvas. const customPenFactory: ComponentBuilderFactory = (startPoint, viewport) => { // Randomly choose either a polyline or a circle for this stroke. if (Math.random() < 0.5) { return makePolylineBuilder(startPoint, viewport); } else { return makeOutlinedCircleBuilder(startPoint, viewport); } }; const editor = new Editor(document.body, { pens: { // The polyline is already present by default -- additionalPenTypes: [{ name: 'Polyline pen', id: 'custom-polyline', factory: customPenFactory, // The pen doesn't create fixed shapes (e.g. squares, rectangles, etc) // and so should go under the "pens" section. isShapeBuilder: false, }], }, }); editor.addToolbar();
Example 27
From page: documents/Guides.Customizing_existing_tools.html
---use-previous--- ---visible--- import { PenTool } from 'js-draw'; const firstPen = editor.toolController.getMatchingTools(PenTool)[0]; firstPen.setStrokeFactory(customPenFactory);
Example 28
From page: documents/Guides.Customizing_existing_tools.html
import { pathToRenderable, Path, Stroke, ComponentBuilderFactory, Point2, Vec2, Rect2, Color4, Viewport, StrokeDataPoint, RenderingStyle, PathCommandType, ComponentBuilder, AbstractRenderer } from 'js-draw'; /// /// The custom ComponentBuilder /// /// This class handles conversion between input data (for example, as generated /// by a mouse) and AbstractComponents that will be added to the drawing. /// class CustomBuilder implements ComponentBuilder { private path: Path; private renderingStyle: RenderingStyle; private lastPoint: Point2; public constructor( startPoint: StrokeDataPoint, // We'll use sizeOfScreenPixelOnCanvas later, to round points // based on the current zoom level. private sizeOfScreenPixelOnCanvas: number ) { // Round points based on the current zoom to prevent the saved SVG // from having large decimals. const startPosition = this.roundPoint(startPoint.pos); // Initially, just a point: this.path = new Path(startPosition, []); this.renderingStyle = { // No fill fill: Color4.transparent, stroke: { color: startPoint.color, // For now, the custom pen has a constant width based on the first // point. width: startPoint.width, }, }; this.lastPoint = startPosition; } // Returns the bounding box of the stroke drawn so far. This box should contain // all points in the stroke. public getBBox(): Rect2 { return this.path.bbox; } // Called to build the final version of the stroke. public build(): Stroke { return new Stroke([ pathToRenderable(this.path, this.renderingStyle) ]); } // Called while building the stroke. This is separate from .build() to // allow for greater efficiency (.build creates the final version, .preview // can be a fast preview). public preview(renderer: AbstractRenderer) { // For simplicity, use the same final shape as the preview. const stroke = this.build(); stroke.render(renderer); } private roundPoint(point: Point2): Point2 { // Because js-draw supports a very large zoom range, we round differently // at different zoom levels. sizeOfScreenPixelOnCanvas is based on the current zoom level. return Viewport.roundPoint(point, this.sizeOfScreenPixelOnCanvas); } // .addPoint is called when a new point of input data has been received. // newPoint contains color, pressure, and position information. public addPoint(newPoint: StrokeDataPoint) { // Create a new point based on the input data, plus some randomness! const size = newPoint.width * 4; const newPos = newPoint.pos.plus( Vec2.of(Math.random() * size - size/2, Math.random() * size - size/2) ); // Round the point to prevent long decimal values when saving to SVG. const roundedPoint = this.roundPoint(newPos); this.path = new Path(this.path.startPoint, [ ...this.path.parts, { kind: PathCommandType.LineTo, point: roundedPoint, }, ]); } } /// /// The custom ComponentBuilderFactory /// // A ComponentBuilderFactory is responsible for creating instances of a // ComponentBuilder. It's what we'll provide to js-draw. export const makeCustomBuilder: ComponentBuilderFactory = (initialPoint: StrokeDataPoint, viewport: Viewport) => { const sizeOfScreenPixelOnCanvas = viewport.getSizeOfPixelOnCanvas(); return new CustomBuilder(initialPoint, sizeOfScreenPixelOnCanvas); }; /// /// The editor /// import { Editor } from 'js-draw'; const editor = new Editor(document.body, { pens: { additionalPenTypes: [{ name: 'Wavy pen', id: 'wavy-lines', factory: makeCustomBuilder, // Put under the "pens" section. isShapeBuilder: false, }], }, }); editor.addToolbar(); /// /// Select our custom pen by default. /// import { PenTool } from 'js-draw'; const firstPen = editor.toolController.getMatchingTools(PenTool)[0]; firstPen.setStrokeFactory(makeCustomBuilder);
Example 29
From page: documents/Guides.Positioning_an_element_above_the_editor.html
import { Editor } from 'js-draw'; // Adds the editor to document.body: const editor = new Editor(document.body); // 1 editor.addToolbar();
Example 30
From page: documents/Guides.Positioning_an_element_above_the_editor.html
---use-previous--- ---visible--- const button = document.createElement('button'); button.textContent = 'Example!'; button.style.position = 'absolute';
Example 31
From page: documents/Guides.Positioning_an_element_above_the_editor.html
---use-previous--- ---visible--- import { Mat33, Vec2 } from '@js-draw/math'; const positioning = Mat33.translation(Vec2.of(10, 20)); const anchor = editor.anchorElementToCanvas(button, positioning);
Example 32
From page: documents/Guides.Positioning_an_element_above_the_editor.html
---use-previous--- ---visible--- button.onclick = () => { anchor.remove(); };
Example 33
From page: documents/Migrations.Migrating_to_version_1.html
:root .imageEditorContainer { /* Used for unselected buttons and dialog text. */ --background-color-1: white; --foreground-color-1: black; /* Used for some menu/toolbar backgrounds. */ --background-color-2: #f5f5f5; --foreground-color-2: #2c303a; /* Used for other menu/toolbar backgrounds. */ --background-color-3: #e5e5e5; --foreground-color-3: #1c202a; /* Used for selected buttons. */ --selection-background-color: #cbdaf1; --selection-foreground-color: #2c303a; /* Used for dialog backgrounds */ --background-color-transparent: rgba(105, 100, 100, 0.5); /* Used for shadows */ --shadow-color: rgba(0, 0, 0, 0.5); /* Color used for some button/input foregrounds */ --primary-action-foreground-color: #15b; } ---ts--- import { Editor, makeEdgeToolbar, makeDropdownToolbar } from 'js-draw'; import 'js-draw/styles'; import { MaterialIconProvider } from '@js-draw/material-icons'; const makeToolbar = (newToolbar: boolean, editor: Editor) => { const toolbar = newToolbar ? makeEdgeToolbar(editor) : makeDropdownToolbar(editor); toolbar.addDefaults(); toolbar.addExitButton(() => { alert('Not implemented for this editor!'); }); toolbar.addSaveButton(() => { const saveData = editor.toSVG().outerHTML; // Do something with saveData alert('Not implemented for this editor!'); }); return toolbar; }; const makeEditor = async () => { const editor = new Editor(document.body, { iconProvider: new MaterialIconProvider(), wheelEventsEnabled: 'only-if-focused', }); // Loads from SVG data await editor.loadFromSVG(` <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style> <path d="M500,500L500,0L0,0L0,500L500,500" fill="#e3e3e3" class="js-draw-image-background"></path> <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: sans-serif; font-size: 32px; fill: rgb(0, 0, 0);">Testing...</text> </svg> `); let isNewToolbar = true; let toolbar = makeToolbar(isNewToolbar, editor); const toolbarSelector = document.createElement('button'); toolbarSelector.innerText = 'Change toolbar type'; document.body.appendChild(toolbarSelector); toolbarSelector.onclick = () => { isNewToolbar = !isNewToolbar; toolbar.remove(); toolbar = makeToolbar(isNewToolbar, editor); }; }; makeEditor();
Example 34
From page: documents/Migrations.Migrating_to_version_1.html
import { Editor, makeEdgeToolbar, makeDropdownToolbar, AbstractToolbar } from 'js-draw'; import 'js-draw/styles'; // Also use the new icon pack: import { MaterialIconProvider } from '@js-draw/material-icons'; let toolbar: AbstractToolbar|null = null let isDropdownToolbar: boolean = true; const makeToolbar = (editor: Editor) => { // Remove the old toolbar (if any). if (toolbar) { toolbar.remove(); } // Create the new toolbar if (isDropdownToolbar) { toolbar = makeDropdownToolbar(editor); } else { toolbar = makeEdgeToolbar(editor); } // Add the default action buttons to the toolbar toolbar.addDefaults(); // Add a toggle button toolbar.addActionButton({ // An icon that looks similar to an arrow: icon: editor.icons.makeDropdownIcon(), label: 'Change toolbar type' }, () => { isDropdownToolbar = !isDropdownToolbar; makeToolbar(editor); }); // Optional: Add save/exit buttons: // toolbar.addExitButton(() => { }); // toolbar.addSaveButton(() => { }); return toolbar; }; // Creates the edior and adds it to the document const makeEditor = () => { const editor = new Editor(document.body, { iconProvider: new MaterialIconProvider(), wheelEventsEnabled: 'only-if-focused', }); makeToolbar(editor); }; makeEditor();
Example 35
From page: documents/Migrations.Migrating_to_version_1.html
import { Editor, PenTool, PenStyle, Color4, makeOutlinedCircleBuilder, makeFreehandLineBuilder } from 'js-draw'; import 'js-draw/styles'; const editor = new Editor(document.body, { wheelEventsEnabled: 'only-if-focused', }); const penStyle: PenStyle = { color: Color4.red, // Try changing this to makeFreehandLineBuilder factory: makeOutlinedCircleBuilder, thickness: 4, }; editor.toolController.addPrimaryTool( new PenTool(editor, 'Some description here', penStyle), ); // Add the toolbar **after** adding the new tool. editor.addToolbar();
Example 36
From page: modules/js-draw.html
import { Editor, Vec3, Mat33, EditorSettings, ShortcutManager } from 'js-draw'; // Use the Material Icon pack. import { MaterialIconProvider } from '@js-draw/material-icons'; // Apply js-draw CSS import 'js-draw/styles'; // If your bundler doesn't support the above, try // import 'js-draw/bundledStyles'; (async () => { // All settings are optional! Try commenting them out. const settings: EditorSettings = { // Use a non-default set of icons iconProvider: new MaterialIconProvider(), // Only capture mouse wheel events if the editor has focus. This is useful // when the editor is part of a larger, scrolling page. wheelEventsEnabled: 'only-if-focused', }; // Create the editor! const editor = new Editor(document.body, settings); // Adds the defualt toolbar const toolbar = editor.addToolbar(); // Increases the minimum height of the editor editor.getRootElement().style.height = '600px'; // Loads from SVG data await editor.loadFromSVG(` <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" > <path d="M500,500L500,0L0,0L0,500L500,500" fill="#aaa" class="js-draw-image-background" /> <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: #111;" >Testing...</text> </svg> `); // Adding tags to a toolbar button allows different styles to be applied. // Also see addActionButton. toolbar.addSaveButton(() => { const saveData = editor.toSVG().outerHTML; // Do something with saveData alert('Saved data!\n\n' + saveData); }); toolbar.addExitButton(() => { // Save/confirm exiting here? editor.remove(); }); })();
Example 37
From page: modules/js-draw.Vec2.html
import { Vec2 } from '@js-draw/math'; const v = Vec2.of(1, 2); console.log('a Vec2:', v); console.log('x component:', v.x); console.log('z component:', v.z);
Example 38
From page: functions/js-draw.Vec2.of.html
import { Vec2 } from '@js-draw/math'; const v = Vec2.of(3, 4); // x=3, y=4.
Example 39
From page: functions/js-draw.Vec2.ofXY.html
import { Vec2 } from '@js-draw/math'; const v1 = Vec2.ofXY({ x: 3, y: 4.5 }); const v2 = Vec2.ofXY({ x: -123.4, y: 1 });
Example 40
From page: functions/js-draw.Vec3.of.html
import { Vec3 } from '@js-draw/math'; const v1 = Vec3.of(1, 2, 3); console.log(v1.plus(Vec3.of(0, 100, 0)));
Example 41
From page: classes/js-draw.AbstractToolbar.html
import { Editor } from 'js-draw'; const editor = new Editor(document.body); const toolbar = editor.addToolbar(); function makeTrashIcon() { const container = document.createElement('div'); container.textContent = '🗑️'; return container; } toolbar.addActionButton({ icon: makeTrashIcon(), // can be any Element not in the DOM label: 'Delete all', }, () => { alert('to-do!');
Example 42
From page: classes/js-draw.AbstractToolbar.html
import { Editor, makeDropdownToolbar } from 'js-draw'; const editor = new Editor(document.body); const toolbar = makeDropdownToolbar(editor); toolbar.addDefaults(); toolbar.addSaveButton(() => alert('save clicked!'));
Example 43
From page: classes/js-draw.CanvasRenderer.html
import {Editor,CanvasRenderer} from 'js-draw'; // Create an editor and load initial data -- don't add to the body (hidden editor). const editor = new Editor(document.createElement('div')); await editor.loadFromSVG('<svg><path d="m0,0 l100,5 l-50,60 l30,20 z" fill="green"/></svg>'); ---visible--- // Given some editor. // Set up the canvas to be drawn onto. const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // Ensure that the canvas can fit the entire rendering const viewport = editor.image.getImportExportViewport(); canvas.width = viewport.getScreenRectSize().x; canvas.height = viewport.getScreenRectSize().y; // Render editor.image onto the renderer const renderer = new CanvasRenderer(ctx, viewport); editor.image.render(renderer, viewport); // Add the rendered canvas to the document. document.body.appendChild(canvas);
Example 44
From page: classes/js-draw.Color4.html
import { Color4 } from '@js-draw/math'; console.log('Red:', Color4.fromString('#f00')); console.log('Also red:', Color4.ofRGB(1, 0, 0), Color4.red); console.log('Mixing red and blue:', Color4.red.mix(Color4.blue, 0.5)); console.log('To string:', Color4.orange.toHexString());
Example 45
From page: classes/js-draw.Color4.html
import { Color4 } from '@js-draw/math'; console.log(Color4.fromHex('#ff0'));
Example 46
From page: classes/js-draw.Editor.html
import { Editor } from 'js-draw'; const editor = new Editor(document.body); const toolbar = editor.addToolbar(); toolbar.addSaveButton(() => { const saveData = editor.toSVG().outerHTML; // Do something with saveData... });
Example 47
From page: classes/js-draw.Editor.html
import { Editor } from 'js-draw'; const container = document.body; // Create an editor const editor = new Editor(container, { // 2e-10 and 1e12 are the default values for minimum/maximum zoom. minZoom: 2e-10, maxZoom: 1e12, }); // Add the default toolbar const toolbar = editor.addToolbar(); const createCustomIcon = () => { // Create/return an icon here. }; // Add a custom button toolbar.addActionButton({ label: 'Custom Button' icon: createCustomIcon(), }, () => { // Do something here });
Example 48
From page: classes/js-draw.Editor.html
import { Editor, Stroke, Path, Color4, pathToRenderable } from 'js-draw'; const editor = new Editor(document.body); // Create a path. const stroke = new Stroke([ pathToRenderable(Path.fromString('M0,0 L100,100 L300,30 z'), { fill: Color4.red }), ]); const addComponentCommand = editor.image.addComponent(stroke); // Add the stroke to the editor editor.dispatch(addComponentCommand);
Example 49
From page: classes/js-draw.Editor.html
import { Editor, EditorEventType, SerializableCommand } from 'js-draw'; // Create a minimal editor const editor = new Editor(document.body); editor.addToolbar(); // Create a place to show text output const log = document.createElement('textarea'); document.body.appendChild(log); log.style.width = '100%'; log.style.height = '200px'; // Listen for CommandDone events (there's also a CommandUndone) editor.notifier.on(EditorEventType.CommandDone, event => { // Type narrowing for TypeScript -- event will always be of kind CommandDone, // but TypeScript doesn't know this. if (event.kind !== EditorEventType.CommandDone) return; log.value = `Command done ${event.command.description(editor, editor.localization)}\n`; if (event.command instanceof SerializableCommand) { log.value += `serializes to: ${JSON.stringify(event.command.serialize())}`; } }); // Dispatch an initial command to trigger the event listener for the first time editor.dispatch(editor.image.setAutoresizeEnabled(true));
Example 50
From page: classes/js-draw.Editor.html
import { Editor, EditorImage, Stroke, Path, Color4, } from 'js-draw'; const editor = new Editor(document.body); const stroke = Stroke.fromFilled( Path.fromString('m0,0 l100,100 l0,-10 z'), Color4.red, ); editor.dispatch(EditorImage.addComponent(stroke));
Example 51
From page: classes/js-draw.Editor.html
import {Editor} from 'js-draw'; const editor = new Editor(document.body); ---visible--- await editor.loadFromSVG(` <svg viewBox="5 23 52 30" width="52" height="16" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <text style=" transform: matrix(0.181846, 0.1, 0, 0.181846, 11.4, 33.2); font-family: serif; font-size: 32px; fill: rgb(100, 140, 61); ">An SVG image!</text> </svg> `);
Example 52
From page: classes/js-draw.Editor.html
import { Editor, Color4, BackgroundComponentBackgroundType } from 'js-draw'; const editor = new Editor(document.body); editor.dispatch(editor.setBackgroundStyle({ color: Color4.orange, type: BackgroundComponentBackgroundType.Grid, autoresize: true, }));
Example 53
From page: classes/js-draw.Editor.html
import { Editor, ImageComponent, Mat33 } from 'js-draw'; const editor = new Editor(document.body); // // Adding an image // const myHtmlImage = new Image(); myHtmlImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAAKklEQVQIW2Ns022zZGRgfPnz8s8HDQwN/xgZgKBDu0PuL8tf5d8/fz8FAOiDD1H2gfpGAAAAAElFTkSuQmCC'; const rotated45Degrees = Mat33.zRotation(Math.PI / 4); // A 45 degree = pi/4 radian rotation const scaledByFactorOf100 = Mat33.scaling2D(100); // Scale **and** rotate const transform = rotated45Degrees.rightMul(scaledByFactorOf100); const imageComponent = await ImageComponent.fromImage(myHtmlImage, transform); await editor.dispatch(editor.image.addComponent(imageComponent)); // // Make a new image from the editor itself (with editor.toDataURL) // const toolbar = editor.addToolbar(); toolbar.addActionButton('From editor', async () => { const dataUrl = editor.toDataURL(); const htmlImage = new Image(); htmlImage.src = dataUrl; const imageComponent = await ImageComponent.fromImage(htmlImage, Mat33.identity); await editor.addAndCenterComponents([ imageComponent ]); });
Example 54
From page: classes/js-draw.EditorImage.html
import { Editor, RenderingStyle, Erase, Stroke, pathToRenderable } from 'js-draw'; import { Path, Color4, Point2, Vec2, Rect2 } from '@js-draw/math'; const editor = new Editor(document.body); // //////////////// // // Helper functions // // //////////////// // function addStroke(path: Path, style: RenderingStyle) { const stroke = new Stroke([ pathToRenderable(path, style) ]); // Create a command that adds the stroke to the image // (but don't apply it yet). const command = editor.image.addComponent(stroke); // Actually apply the command. editor.dispatch(command); } function addBoxAt(point: Point2, color: Color4) { // Create a 10x10 square at the given point: const box = new Rect2(point.x, point.y, 10, 10); addStroke( Path.fromRect(box), { fill: color }, ); } function makeTrashIcon() { const container = document.createElement('div'); container.textContent = '🗑️'; return container; } // //////////////////// // // End helper functions // // //////////////////// // // Add some components to the image: addBoxAt(Vec2.of(0, 0), Color4.green); addBoxAt(Vec2.of(20, 0), Color4.orange); addBoxAt(Vec2.of(20, 20), Color4.blue); // Get the components in a small rectangle near (0, 0) const components = editor.image.getComponentsIntersecting( new Rect2(0, 0, 5, 5), // a 5x5 square with top left (0, 0) ); // Add a button that removes the components const toolbar = editor.addToolbar(); toolbar.addActionButton({ icon: makeTrashIcon(), label: 'remove near (0,0)', }, () => { const deleteCommand = new Erase(components); editor.dispatch(deleteCommand); });
Example 55
From page: classes/js-draw.EditorImage.html
import {Editor,CanvasRenderer} from 'js-draw'; // Create an editor and load initial data -- don't add to the body (hidden editor). const editor = new Editor(document.createElement('div')); await editor.loadFromSVG('<svg><path d="m0,0 l100,5 l-50,60 l30,20 z" fill="green"/></svg>'); ---visible--- // Given some editor. // Set up the canvas to be drawn onto. const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // Ensure that the canvas can fit the entire rendering const viewport = editor.image.getImportExportViewport(); canvas.width = viewport.getScreenRectSize().x; canvas.height = viewport.getScreenRectSize().y; // Render editor.image onto the renderer const renderer = new CanvasRenderer(ctx, viewport); editor.image.render(renderer, viewport); // Add the rendered canvas to the document. document.body.appendChild(canvas);
Example 56
From page: classes/js-draw.EditorImage.html
import { Editor } from 'js-draw'; const editor = new Editor(document.body); const toolbar = editor.addToolbar(); // Add a save button to demonstrate what the output looks like // (it should change size to fit whatever was drawn) toolbar.addSaveButton(() => { document.body.replaceChildren(editor.toSVG({ sanitize: true })); }); // Actually using setAutoresizeEnabled: // // To set autoresize without announcing for accessibility/making undoable const addToHistory = false; editor.dispatchNoAnnounce(editor.image.setAutoresizeEnabled(true), addToHistory); // Add to undo history **and** announce for accessibility //editor.dispatch(editor.image.setAutoresizeEnabled(true), true);
Example 57
From page: classes/js-draw.EditorImage.html
import { Editor, EditorImage, Stroke, Path, Color4, } from 'js-draw'; const editor = new Editor(document.body); const stroke = Stroke.fromFilled( Path.fromString('m0,0 l100,100 l0,-10 z'), Color4.red, ); editor.dispatch(EditorImage.addComponent(stroke));
Example 58
From page: classes/js-draw.Erase.html
import { Editor, Erase, uniteCommands, Color4, Path, Stroke, Rect2, pathToRenderable } from 'js-draw'; const editor = new Editor(document.body); editor.addToolbar(); // Add a large number of strokes const commands = []; for (let x = -20; x < 20; x++) { for (let y = 0; y < 60; y++) { const stroke = new Stroke([ pathToRenderable( Path.fromString(`m${x * 5},${y * 5}l1,1`), { fill: Color4.transparent, stroke: {width: 2, color: Color4.ofRGB(x / 10, y / 10, 0.5)}} ) ]); commands.push(editor.image.addElement(stroke)); } } await editor.dispatch(uniteCommands(commands, 100)); ---visible--- // Given some editor... // Find all elements intersecting the rectangle with top left (-10,-30) and // (width,height)=(50,100). const elems = editor.image.getComponentsIntersecting( new Rect2(-10, -30, 50, 100) ); // Create a command that erases [elems] when applied const eraseElemsCmd = new Erase(elems); // Apply the command (and make it undoable) editor.dispatch(eraseElemsCmd);
Example 59
From page: classes/js-draw.IconProvider.html
import * as jsdraw from 'js-draw'; class CustomIconProvider extends jsdraw.IconProvider { // Use '☺' instead of the default dropdown symbol. public override makeDropdownIcon() { const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); icon.innerHTML = ` <text x='5' y='55' style='fill: var(--icon-color); font-size: 50pt;'>☺</text> `; icon.setAttribute('viewBox', '0 0 100 100'); return icon; } } const icons = new CustomIconProvider(); const editor = new jsdraw.Editor(document.body, { // The icon pack to use is specified through the editor's initial // configuration object: iconProvider: icons, }); // Add a toolbar that uses these icons jsdraw.makeDropdownToolbar(editor).addDefaults();
Example 60
From page: classes/js-draw.ImageComponent.html
import { Editor, ImageComponent, Mat33 } from 'js-draw'; const editor = new Editor(document.body); // // Adding an image // const myHtmlImage = new Image(); myHtmlImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAAKklEQVQIW2Ns022zZGRgfPnz8s8HDQwN/xgZgKBDu0PuL8tf5d8/fz8FAOiDD1H2gfpGAAAAAElFTkSuQmCC'; const rotated45Degrees = Mat33.zRotation(Math.PI / 4); // A 45 degree = pi/4 radian rotation const scaledByFactorOf100 = Mat33.scaling2D(100); // Scale **and** rotate const transform = rotated45Degrees.rightMul(scaledByFactorOf100); const imageComponent = await ImageComponent.fromImage(myHtmlImage, transform); await editor.dispatch(editor.image.addComponent(imageComponent)); // // Make a new image from the editor itself (with editor.toDataURL) // const toolbar = editor.addToolbar(); toolbar.addActionButton('From editor', async () => { const dataUrl = editor.toDataURL(); const htmlImage = new Image(); htmlImage.src = dataUrl; const imageComponent = await ImageComponent.fromImage(htmlImage, Mat33.identity); await editor.addAndCenterComponents([ imageComponent ]); });
Example 61
From page: classes/js-draw.InsertImageWidget.html
import { Editor, makeEdgeToolbar, InsertImageWidget } from 'js-draw'; const editor = new Editor(document.body); const toolbar = makeEdgeToolbar(editor); toolbar.addWidget(new InsertImageWidget(editor));
Example 62
From page: classes/js-draw.LineSegment2.html
import {LineSegment2, Vec2} from '@js-draw/math'; const l = new LineSegment2(Vec2.of(1, 1), Vec2.of(2, 2)); console.log('length: ', l.length); console.log('direction: ', l.direction); console.log('bounding box: ', l.bbox);
Example 63
From page: classes/js-draw.LineSegment2.html
import {LineSegment2, Vec2} from '@js-draw/math'; console.log(LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 0), Vec2.of(0, 1)]));
Example 64
From page: classes/js-draw.Mat33.html
import {Mat33, Vec2} from '@js-draw/math'; const moveLeftAndUp = Mat33.translation(Vec2.of(5, 6)); console.log(moveLeftAndUp);
Example 65
From page: classes/js-draw.Mat33.html
---use-previous--- ---visible--- console.log(moveLeftAndUp.transformVec2(Vec2.of(1, 1))); console.log(moveLeftAndUp.transformVec2(Vec2.of(-1, 2)));
Example 66
From page: classes/js-draw.Mat33.html
---use-previous--- ---visible--- // Create a matrix by right multiplying. const scaleThenRotate = // The resultant matrix first scales by a factor of two Mat33.scaling2D(2).rightMul( // ...then rotates by pi/2 radians = 90 degrees. Mat33.zRotation(Math.PI / 2) ); console.log(scaleThenRotate); // Use scaleThenRotate to scale then rotate a vector. console.log(scaleThenRotate.transformVec2(Vec2.unitX));
Example 67
From page: classes/js-draw.Mat33.html
import {Mat33, Vec2} from '@js-draw/math'; console.log(Mat33.identity.rightMul(Mat33.identity)); // Create a matrix by right multiplying. const scaleThenRotate = // The resultant matrix first scales by a factor of two Mat33.scaling2D(2).rightMul( // ...then rotates by pi/4 radians = 45 degrees. Mat33.zRotation(Math.PI / 4) ); console.log(scaleThenRotate); // Use scaleThenRotate to scale then rotate a vector. console.log(scaleThenRotate.transformVec2(Vec2.unitX));
Example 68
From page: classes/js-draw.Mat33.html
import { Mat33 } from '@js-draw/math'; console.log( new Mat33( 1, 2, 3, 4, 5, 6, 7, 8, 9, ) );
Example 69
From page: classes/js-draw.Mat33.html
import { Mat33 } from '@js-draw/math'; console.log(Mat33.identity.toString());
Example 70
From page: classes/js-draw.Mat33.html
import { Mat33, Vec2 } from '@js-draw/math'; const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle const center = Vec2.of(1, 1); // The point (1,1) const rotationMatrix = Mat33.zRotation(halfCircle, center); console.log( 'Rotating (0,0) 180deg about', center, 'results in', // Rotates (0,0) rotationMatrix.transformVec2(Vec2.zero), );
Example 71
From page: classes/js-draw.PanZoomTool.html
import { Editor, PanZoomTool, PanZoomMode } from 'js-draw'; const editor = new Editor(document.body); // By default, there are multiple PanZoom tools that handle different events. // This gets all PanZoomTools. const panZoomToolList = editor.toolController.getMatchingTools(PanZoomTool); // The first PanZoomTool is the highest priority -- by default, // this tool is responsible for handling multi-finger touch gestures. // // Lower-priority PanZoomTools handle one-finger touch gestures and // key-presses. const panZoomTool = panZoomToolList[0]; // Lock rotation for multi-finger touch gestures. panZoomTool.setModeEnabled(PanZoomMode.RotationLocked, true);
Example 72
From page: classes/js-draw.Path.html
import {Path, Mat33, Vec2, LineSegment2} from '@js-draw/math'; // Creates a path from an SVG path string. // In this case, // 1. Move to (0,0) // 2. Line to (100,0) const path = Path.fromString('M0,0 L100,0'); // Logs the distance from (10,0) to the curve 1 unit // away from path. This curve forms a stroke with the path at // its center. const strokeRadius = 1; console.log(path.signedDistance(Vec2.of(10,0), strokeRadius)); // Log a version of the path that's scaled by a factor of 4. console.log(path.transformedBy(Mat33.scaling2D(4)).toString()); // Log all intersections of a stroked version of the path with // a vertical line segment. // (Try removing the `strokeRadius` parameter). const segment = new LineSegment2(Vec2.of(5, -100), Vec2.of(5, 100)); console.log(path.intersection(segment, strokeRadius).map(i => i.point));
Example 73
From page: classes/js-draw.Path.html
import {Path} from '@js-draw/math'; console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
Example 74
From page: classes/js-draw.Path.html
import {Path, Vec2} from '@js-draw/math'; console.log(Path.fromString('m0,0 L100,0').signedDistance(Vec2.zero, 1));
Example 75
From page: classes/js-draw.Path.html
import { Path } from '@js-draw/math'; const path = Path.fromString('m0,0l100,100'); console.log(path.toString(true)); // true: Prefer relative to absolute path commands
Example 76
From page: classes/js-draw.PenTool.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 77
From page: classes/js-draw.QuadraticBezier.html
import { QuadraticBezier, Vec2 } from '@js-draw/math'; const startPoint = Vec2.of(4, 3); const controlPoint = Vec2.of(1, 1); const endPoint = Vec2.of(1, 3); const curve = new QuadraticBezier( startPoint, controlPoint, endPoint, ); console.log('Curve:', curve);
Example 78
From page: classes/js-draw.Rect2.html
import { Rect2, Vec2 } from '@js-draw/math'; const rect = Rect2.fromCorners( Vec2.of(0, 0), Vec2.of(10, 10), ); console.log('area', rect.area); console.log('topLeft', rect.topLeft);
Example 79
From page: classes/js-draw.Stroke.html
import { Editor, EditorImage, Stroke, Path, Color4, } from 'js-draw'; const editor = new Editor(document.body); const stroke = Stroke.fromFilled( Path.fromString('m0,0 l100,100 l0,-10 z'), Color4.red, ); editor.dispatch(EditorImage.addComponent(stroke));
Example 80
From page: classes/js-draw.Stroke.html
import { Editor, Stroke, Color4 } from 'js-draw'; const editor = new Editor(document.body); ---visible--- const stroke = Stroke.fromStroked('m0,0 l10,10', { width: 10, color: Color4.red }); editor.dispatch(editor.image.addComponent(stroke));
Example 81
From page: classes/js-draw.TextComponent.html
import { Editor, TextComponent, Mat33, Vec2, Color4, TextRenderingStyle } from 'js-draw'; const editor = new Editor(document.body); editor.dispatch(editor.setBackgroundStyle({ color: Color4.black, autoresize: true )); ---visible--- /// Adding a simple TextComponent ///------------------------------ const positioning1 = Mat33.translation(Vec2.of(10, 10)); const style: TextRenderingStyle = { fontFamily: 'sans', size: 12, renderingStyle: { fill: Color4.green }, }; editor.dispatch( editor.image.addComponent(new TextComponent(['Hello, world'], positioning1, style)), ); /// Adding nested TextComponents ///----------------------------- // Add another TextComponent that contains text and a TextComponent. Observe that '[Test]' // is placed directly after 'Test'. const positioning2 = Mat33.translation(Vec2.of(10, 50)); editor.dispatch( editor.image.addComponent( new TextComponent([ new TextComponent(['Test'], positioning1, style), '[Test]' ], positioning2, style) ), );
Example 82
From page: classes/js-draw.Viewport.html
import { Editor, Viewport, Mat33, Vec2 } from 'js-draw'; const editor = new Editor(document.body); const moveRight = Mat33.translation(Vec2.unitX.times(500)); // Move the **canvas** right by 500 units: Viewport.transformBy(moveRight).apply(editor);
Example 83
From page: interfaces/js-draw.EditorSettings.html
import { EditorSettings, Editor, KeyBinding, makeEdgeToolbar } from 'js-draw'; import { MaterialIconProvider } from '@js-draw/material-icons'; // All settings are optional! Try commenting them out. const settings: EditorSettings = { // Use a non-default set of icons iconProvider: new MaterialIconProvider(), // Only capture mouse wheel events if the editor has focus. This is useful // when the editor is part of a larger, scrolling page. wheelEventsEnabled: 'only-if-focused', // The default minimum zoom is 2e-10... minZoom: 2e-10, // and the maximum default zoom is 1e12 maxZoom: 1e12, // Override some keyboard shortcuts! keyboardShortcutOverrides: { // The ID for the save action 'jsdraw.toolbar.SaveActionWidget.save': [ // "Meta" = the command key on MacOS KeyBinding.fromString('ctrlOrMeta+s'), // Also map ctrl+M to save! KeyBinding.fromString('ctrl+m'), ], }, }; // Create the editor! const editor = new Editor(document.body, settings); // Selects a specific toolbar type. See also makeDropdownToolbar const toolbar = makeEdgeToolbar(editor); toolbar.addDefaults(); // Add the action button that is triggered by the save keyboard shortcuts above. toolbar.addSaveButton(() => { const saveData = editor.toSVG().outerHTML; // Do something with saveData alert('Saved data:\n\n' + saveData); });
Example 84
From page: interfaces/js-draw.EditorSettings.html
import { Editor, makePolylineBuilder } from 'js-draw'; const editor = new Editor(document.body, { pens: { additionalPenTypes: [{ name: 'Polyline (For debugging)', id: 'custom-polyline', factory: makePolylineBuilder, // The pen doesn't create fixed shapes (e.g. squares, rectangles, etc) // and so should go under the "pens" section. isShapeBuilder: false, }], }, }); editor.addToolbar();
Example 85
From page: interfaces/js-draw.EditorSettings.html
import {Editor} from 'js-draw'; const editor = new Editor(document.body, { // Only allow selecting the polyline pen from the toolbar. pens: { filterPenTypes: p => p.id === 'polyline-pen' }, }); editor.addToolbar();
Example 86
From page: interfaces/js-draw.Vec3-1.html
import { Vec3 } from '@js-draw/math'; console.log('Vector addition:', Vec3.of(1, 2, 3).plus(Vec3.of(0, 1, 0))); console.log('Scalar multiplication:', Vec3.of(1, 2, 3).times(2)); console.log('Cross products:', Vec3.unitX.cross(Vec3.unitY)); console.log('Magnitude:', Vec3.of(1, 2, 3).length(), 'or', Vec3.of(1, 2, 3).magnitude()); console.log('Square Magnitude:', Vec3.of(1, 2, 3).magnitudeSquared()); console.log('As an array:', Vec3.unitZ.asArray());
Example 87
From page: interfaces/js-draw.Vec3-1.html
import { Vec2 } from '@js-draw/math'; console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1) console.log(Vec2.of(-1, 0).angle()); // atan2(0, -1)
Example 88
From page: interfaces/js-draw.Vec3-1.html
import { Vec3 } from '@js-draw/math'; console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
Example 89
From page: interfaces/js-draw.Vec3-1.html
import { Vec3 } from '@js-draw/math'; console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
Example 90
From page: functions/js-draw.adjustEditorThemeForContrast.html
import { Editor, adjustEditorThemeForContrast } from 'js-draw'; const editor = new Editor(document.body); editor.addToolbar(); const css = ` :root .imageEditorContainer { --background-color-1: #ffff77; --foreground-color-1: #fff; --background-color-2: #ffff99; --foreground-color-2: #ffff88; --background-color-3: #ddffff; --foreground-color-3: #eeffff; --selection-background-color: #9f7; --selection-foreground-color: #98f; } @media screen and (prefers-color-scheme: dark) { :root .imageEditorContainer { --background-color-1: black; } } `; editor.addStyleSheet(css); adjustEditorThemeForContrast(editor); // Because adjustEditorThemeForContrast overrides the current theme, it should be called again // to allow the editor to switch between light/dark themes. window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { adjustEditorThemeForContrast(editor); }); window.matchMedia('print').addEventListener('change', () => { adjustEditorThemeForContrast(editor); });
Example 91
From page: functions/js-draw.makeArrowBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 92
From page: functions/js-draw.makeDropdownToolbar.html
import { makeDropdownToolbar, Editor } from 'js-draw'; const editor = new Editor(document.body); const toolbar = makeDropdownToolbar(editor); toolbar.addDefaults(); toolbar.addExitButton(editor => { // TODO }); toolbar.addSaveButton(editor => { // TODO });
Example 93
From page: functions/js-draw.makeEdgeToolbar.html
import { makeEdgeToolbar, Editor } from 'js-draw'; const editor = new Editor(document.body); const toolbar = makeEdgeToolbar(editor); toolbar.addDefaults(); toolbar.addSaveButton(editor => { // TODO }); toolbar.addExitButton(editor => { // TODO });
Example 94
From page: functions/js-draw.makeFilledRectangleBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 95
From page: functions/js-draw.makeFreehandLineBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 96
From page: functions/js-draw.makeLineBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 97
From page: functions/js-draw.makeOutlinedCircleBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 98
From page: functions/js-draw.makeOutlinedRectangleBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 99
From page: functions/js-draw.makePolylineBuilder.html
import { Editor, PenTool, makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, InputEvtType, sendPenEvent, } from 'js-draw'; import { Color4, Vec2 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Different pen types that build strokes in different ways. This is a list of some of the // default ones: const strokeBuilders = [ makePolylineBuilder, makeOutlinedCircleBuilder, makeOutlinedRectangleBuilder, makeArrowBuilder, makePressureSensitiveFreehandLineBuilder, makeFreehandLineBuilder, ]; // Get the first pen const pen = editor.toolController.getMatchingTools(PenTool)[0]; // If using a different pen (e.g. the second), be sure to select it! // pen.setEnabled(true); // Draw something with each style for (const factory of strokeBuilders) { // Make the pen use a certain style. pen.setStrokeFactory(factory); // What happens if the following line is uncommented? // pen.setStrokeFactory(makeArrowBuilder); // Select a random pen color const penColor = Color4.ofRGB(Math.random(), Math.random(), Math.random()); pen.setColor(penColor); // Draw something! const imageSize = editor.getImportExportRect().size; const startPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); const endPos = Vec2.of(Math.random() * imageSize.x, Math.random() * imageSize.y); sendPenEvent(editor, InputEvtType.PointerDownEvt, startPos); sendPenEvent(editor, InputEvtType.PointerMoveEvt, startPos.lerp(endPos, 0.5)); sendPenEvent(editor, InputEvtType.PointerUpEvt, endPos); }
Example 100
From page: functions/js-draw.toRoundedString.html
import { toRoundedString } from '@js-draw/math'; console.log('Rounded: ', toRoundedString(1.000000011));
Example 101
From page: functions/js-draw.uniteCommands.html
import { Editor, pathToRenderable, Stroke, uniteCommands } from 'js-draw'; import { Path, Color4 } from '@js-draw/math'; const editor = new Editor(document.body); editor.addToolbar(); // Create strokes! const strokes = []; for (let i = 0; i < 10; i++) { const renderablePath = pathToRenderable( Path.fromString(`M0,${i * 10} L100,100 L300,30 z`), { fill: Color4.transparent, stroke: { color: Color4.red, width: 1, } } ); strokes.push(new Stroke([ renderablePath ])); } // Convert to commands const addStrokesCommands = strokes.map(stroke => editor.image.addElement(stroke)); // Apply all as a single undoable command (try applying each in a loop instead!) await editor.dispatch(uniteCommands(addStrokesCommands)); // The second parameter to uniteCommands is for very large numbers of commands, when // applying them shouldn't be done all at once (which would block the UI). // The second parameter to uniteCommands is for very large numbers of commands, when // applying them shouldn't be done all at once (which would block the UI).
Example 102
From page: modules/_js-draw_material-icons.html
import { Editor, makeEdgeToolbar } from 'js-draw'; import { MaterialIconProvider } from '@js-draw/material-icons'; // Apply js-draw CSS import 'js-draw/styles'; const editor = new Editor(document.body, { iconProvider: new MaterialIconProvider(), }); // Ensure that there is enough room for the toolbar editor.getRootElement().style.minHeight = '500px'; // Add a toolbar const toolbar = makeEdgeToolbar(editor); // ...with the default elements toolbar.addDefaults();
Example 103
From page: functions/_js-draw_material-icons.makeMaterialIconProviderClass.html
import * as jsdraw from 'js-draw'; import { makeMaterialIconProviderClass } from '@js-draw/material-icons'; const MaterialIconProvider = makeMaterialIconProviderClass(jsdraw); (new jsdraw.Editor( document.body, { iconProvider: new MaterialIconProvider() }, )).addToolbar();
Example 104
From page: modules/_js-draw_math.html
import { Vec2, Mat33, Rect2 } from '@js-draw/math'; // Example: Rotate a vector 90 degrees about the z-axis const rotate90Degrees = Mat33.zRotation(Math.PI/2); // π/2 radians = 90 deg const moveUp = Mat33.translation(Vec2.of(1, 0)); const moveUpThenRotate = rotate90Degrees.rightMul(moveUp); console.log(moveUpThenRotate.transformVec2(Vec2.of(1, 2))); // Example: Bounding box of some points console.log(Rect2.bboxOf([ Vec2.of(1, 2), Vec2.of(3, 4), Vec2.of(-100, 1000), ]));
Example 105
From page: modules/_js-draw_math.Vec2.html
import { Vec2 } from '@js-draw/math'; const v = Vec2.of(1, 2); console.log('a Vec2:', v); console.log('x component:', v.x); console.log('z component:', v.z);
Example 106
From page: functions/_js-draw_math.Vec2.of.html
import { Vec2 } from '@js-draw/math'; const v = Vec2.of(3, 4); // x=3, y=4.
Example 107
From page: functions/_js-draw_math.Vec2.ofXY.html
import { Vec2 } from '@js-draw/math'; const v1 = Vec2.ofXY({ x: 3, y: 4.5 }); const v2 = Vec2.ofXY({ x: -123.4, y: 1 });
Example 108
From page: functions/_js-draw_math.Vec3.of.html
import { Vec3 } from '@js-draw/math'; const v1 = Vec3.of(1, 2, 3); console.log(v1.plus(Vec3.of(0, 100, 0)));
Example 109
From page: classes/_js-draw_math.Color4.html
import { Color4 } from '@js-draw/math'; console.log('Red:', Color4.fromString('#f00')); console.log('Also red:', Color4.ofRGB(1, 0, 0), Color4.red); console.log('Mixing red and blue:', Color4.red.mix(Color4.blue, 0.5)); console.log('To string:', Color4.orange.toHexString());
Example 110
From page: classes/_js-draw_math.Color4.html
import { Color4 } from '@js-draw/math'; console.log(Color4.fromHex('#ff0'));
Example 111
From page: classes/_js-draw_math.LineSegment2.html
import {LineSegment2, Vec2} from '@js-draw/math'; const l = new LineSegment2(Vec2.of(1, 1), Vec2.of(2, 2)); console.log('length: ', l.length); console.log('direction: ', l.direction); console.log('bounding box: ', l.bbox);
Example 112
From page: classes/_js-draw_math.LineSegment2.html
import {LineSegment2, Vec2} from '@js-draw/math'; console.log(LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 0), Vec2.of(0, 1)]));
Example 113
From page: classes/_js-draw_math.Mat33.html
import {Mat33, Vec2} from '@js-draw/math'; const moveLeftAndUp = Mat33.translation(Vec2.of(5, 6)); console.log(moveLeftAndUp);
Example 114
From page: classes/_js-draw_math.Mat33.html
---use-previous--- ---visible--- console.log(moveLeftAndUp.transformVec2(Vec2.of(1, 1))); console.log(moveLeftAndUp.transformVec2(Vec2.of(-1, 2)));
Example 115
From page: classes/_js-draw_math.Mat33.html
---use-previous--- ---visible--- // Create a matrix by right multiplying. const scaleThenRotate = // The resultant matrix first scales by a factor of two Mat33.scaling2D(2).rightMul( // ...then rotates by pi/2 radians = 90 degrees. Mat33.zRotation(Math.PI / 2) ); console.log(scaleThenRotate); // Use scaleThenRotate to scale then rotate a vector. console.log(scaleThenRotate.transformVec2(Vec2.unitX));
Example 116
From page: classes/_js-draw_math.Mat33.html
import {Mat33, Vec2} from '@js-draw/math'; console.log(Mat33.identity.rightMul(Mat33.identity)); // Create a matrix by right multiplying. const scaleThenRotate = // The resultant matrix first scales by a factor of two Mat33.scaling2D(2).rightMul( // ...then rotates by pi/4 radians = 45 degrees. Mat33.zRotation(Math.PI / 4) ); console.log(scaleThenRotate); // Use scaleThenRotate to scale then rotate a vector. console.log(scaleThenRotate.transformVec2(Vec2.unitX));
Example 117
From page: classes/_js-draw_math.Mat33.html
import { Mat33 } from '@js-draw/math'; console.log( new Mat33( 1, 2, 3, 4, 5, 6, 7, 8, 9, ) );
Example 118
From page: classes/_js-draw_math.Mat33.html
import { Mat33 } from '@js-draw/math'; console.log(Mat33.identity.toString());
Example 119
From page: classes/_js-draw_math.Mat33.html
import { Mat33, Vec2 } from '@js-draw/math'; const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle const center = Vec2.of(1, 1); // The point (1,1) const rotationMatrix = Mat33.zRotation(halfCircle, center); console.log( 'Rotating (0,0) 180deg about', center, 'results in', // Rotates (0,0) rotationMatrix.transformVec2(Vec2.zero), );
Example 120
From page: classes/_js-draw_math.Path.html
import {Path, Mat33, Vec2, LineSegment2} from '@js-draw/math'; // Creates a path from an SVG path string. // In this case, // 1. Move to (0,0) // 2. Line to (100,0) const path = Path.fromString('M0,0 L100,0'); // Logs the distance from (10,0) to the curve 1 unit // away from path. This curve forms a stroke with the path at // its center. const strokeRadius = 1; console.log(path.signedDistance(Vec2.of(10,0), strokeRadius)); // Log a version of the path that's scaled by a factor of 4. console.log(path.transformedBy(Mat33.scaling2D(4)).toString()); // Log all intersections of a stroked version of the path with // a vertical line segment. // (Try removing the `strokeRadius` parameter). const segment = new LineSegment2(Vec2.of(5, -100), Vec2.of(5, 100)); console.log(path.intersection(segment, strokeRadius).map(i => i.point));
Example 121
From page: classes/_js-draw_math.Path.html
import {Path} from '@js-draw/math'; console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
Example 122
From page: classes/_js-draw_math.Path.html
import {Path, Vec2} from '@js-draw/math'; console.log(Path.fromString('m0,0 L100,0').signedDistance(Vec2.zero, 1));
Example 123
From page: classes/_js-draw_math.Path.html
import { Path } from '@js-draw/math'; const path = Path.fromString('m0,0l100,100'); console.log(path.toString(true)); // true: Prefer relative to absolute path commands
Example 124
From page: classes/_js-draw_math.QuadraticBezier.html
import { QuadraticBezier, Vec2 } from '@js-draw/math'; const startPoint = Vec2.of(4, 3); const controlPoint = Vec2.of(1, 1); const endPoint = Vec2.of(1, 3); const curve = new QuadraticBezier( startPoint, controlPoint, endPoint, ); console.log('Curve:', curve);
Example 125
From page: classes/_js-draw_math.Rect2.html
import { Rect2, Vec2 } from '@js-draw/math'; const rect = Rect2.fromCorners( Vec2.of(0, 0), Vec2.of(10, 10), ); console.log('area', rect.area); console.log('topLeft', rect.topLeft);
Example 126
From page: interfaces/_js-draw_math.Vec3-1.html
import { Vec3 } from '@js-draw/math'; console.log('Vector addition:', Vec3.of(1, 2, 3).plus(Vec3.of(0, 1, 0))); console.log('Scalar multiplication:', Vec3.of(1, 2, 3).times(2)); console.log('Cross products:', Vec3.unitX.cross(Vec3.unitY)); console.log('Magnitude:', Vec3.of(1, 2, 3).length(), 'or', Vec3.of(1, 2, 3).magnitude()); console.log('Square Magnitude:', Vec3.of(1, 2, 3).magnitudeSquared()); console.log('As an array:', Vec3.unitZ.asArray());
Example 127
From page: interfaces/_js-draw_math.Vec3-1.html
import { Vec2 } from '@js-draw/math'; console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1) console.log(Vec2.of(-1, 0).angle()); // atan2(0, -1)
Example 128
From page: interfaces/_js-draw_math.Vec3-1.html
import { Vec3 } from '@js-draw/math'; console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
Example 129
From page: interfaces/_js-draw_math.Vec3-1.html
import { Vec3 } from '@js-draw/math'; console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
Example 130
From page: functions/_js-draw_math.toRoundedString.html
import { toRoundedString } from '@js-draw/math'; console.log('Rounded: ', toRoundedString(1.000000011));