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.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 2

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 3

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 4

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 5

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 6

From page: documents/Guides.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 7

From page: documents/Guides.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 8

From page: documents/Guides.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 9

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 10

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 11

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 12

From page: documents/Guides.Positioning_an_element_above_the_editor.html

---use-previous---
---visible---
button.onclick = () => {
	anchor.remove();
};

Example 13

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 14

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 15

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 16

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 17

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
// viewport to the left by 1 unit.
const moveLeftUpdate = Mat33.translation(Vec2.of(-1, 0));

function update() {
	const moveLeftCommand = Viewport.transformBy(moveLeftUpdate);
	moveLeftCommand.apply(editor);

	requestAnimationFrame(update);
}
update();

Example 18

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 19

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 20

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 21

From page: modules/js-draw.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.addElement(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.getElementsIntersectingRegion(
	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 22

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 23

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 24

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 25

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 26

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 27

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 addElementCommand = editor.image.addElement(stroke);

// Add the stroke to the editor
editor.dispatch(addElementCommand);

Example 28

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 29

From page: classes/js-draw.Editor.html

import {
	Editor, EditorImage, Stroke, pathToRenderable.
	Path, Color4,
} from 'js-draw';

const editor = new Editor(document.body);

const stroke = new Stroke([
	pathToRenderable(Path.fromString('m0,0 l100,100 l0,-10 z'), { fill: Color4.red }),
]);
editor.dispatch(EditorImage.addElement(stroke));

Example 30

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 31

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 32

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.addElement(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 33

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 34

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 35

From page: classes/js-draw.EditorImage.html

import {
	Editor, EditorImage, Stroke, pathToRenderable.
	Path, Color4,
} from 'js-draw';

const editor = new Editor(document.body);

const stroke = new Stroke([
	pathToRenderable(Path.fromString('m0,0 l100,100 l0,-10 z'), { fill: Color4.red }),
]);
editor.dispatch(EditorImage.addElement(stroke));

Example 36

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.getElementsIntersectingRegion(
	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 37

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 38

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.addElement(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 39

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 40

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 41

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 42

From page: classes/js-draw.Stroke.html

import {
	Editor, EditorImage, Stroke, pathToRenderable.
	Path, Color4,
} from 'js-draw';

const editor = new Editor(document.body);

const stroke = new Stroke([
	pathToRenderable(Path.fromString('m0,0 l100,100 l0,-10 z'), { fill: Color4.red }),
]);
editor.dispatch(EditorImage.addElement(stroke));

Example 43

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.addElement(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.addElement(
        new TextComponent([ new TextComponent(['Test'], positioning1, style), '[Test]' ], positioning2, style)
    ),
);

Example 44

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 45

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 46

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 47

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 48

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 49

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 50

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 51

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 52

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 53

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 54

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 55

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 56

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 57

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 58

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 59

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 60

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 61

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 62

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 63

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 64

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 65

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 66

From page: classes/_js-draw_math.Color4.html

import { Color4 } from '@js-draw/math';
console.log(Color4.fromHex('#ff0'));

Example 67

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 68

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 69

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 70

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 71

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 72

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 73

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 74

From page: classes/_js-draw_math.Mat33.html

import { Mat33 } from '@js-draw/math';
console.log(Mat33.identity.toString());

Example 75

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 76

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 77

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 78

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 79

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 80

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 81

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 82

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 83

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 84

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 85

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 86

From page: functions/_js-draw_math.toRoundedString.html

import { toRoundedString } from '@js-draw/math';

console.log('Rounded: ', toRoundedString(1.000000011));