The main entrypoint for the full editor.

To create an editor with a toolbar,

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...
});

See also

Constructors

  • Parameters

    Returns Editor

    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
    });
    

Properties

display: Display

Manages drawing surfaces/AbstractRenderers.

Handles undo/redo.

const editor = new Editor(document.body);

// Do something undoable.
// ...

// Undo the last action
editor.history.undo();

Data structure for adding/removing/querying objects in the image.

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);
notifier: EditorNotifier

Global event dispatcher/subscriber.

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));
toolController: ToolController

Controls the list of tools. See the custom tool example for more.

viewport: Viewport

Allows transforming the view and querying information about what is currently visible.

Methods

  • Adds all components in components such that they are in the center of the screen. This is a convenience method that creates and applies a single command.

    If selectComponents is true (the default), the components are selected.

    actionDescription, if given, should be a screenreader-friendly description of the reason components were added (e.g. "pasted").

    Parameters

    • components: AbstractComponent[]
    • selectComponents: boolean = true
    • OptionalactionDescription: string

    Returns Promise<void>

  • Creates a CSS stylesheet with content and applies it to the document (and thus, to this editor).

    Parameters

    • content: string

    Returns HTMLStyleElement

  • Creates a toolbar. If defaultLayout is true, default buttons are used.

    Parameters

    • defaultLayout: boolean = true

    Returns AbstractToolbar

    a reference to the toolbar.

  • Anchors the given element to the canvas with a given position/transformation in canvas space.

    Parameters

    Returns { remove: () => void }

  • Announce message for screen readers. If message is the same as the previous message, it is re-announced.

    Parameters

    • message: string

    Returns void

  • Apply a large transformation in chunks. If apply is false, the commands are unapplied. Triggers a re-render after each updateChunkSize-sized group of commands has been applied.

    Parameters

    • commands: Command[]
    • apply: boolean
    • updateChunkSize: number

    Returns Promise<void>

  • Parameters

    • commands: Command[]
    • chunkSize: number
    • unapplyInReverseOrder: boolean = false

    Returns Promise<void>

    asyncApplyOrUnapplyCommands

    If unapplyInReverseOrder, commands are reversed before unapplying.

  • Clears the wet ink display.

    The wet ink display can be used by the currently active tool to display a preview of an in-progress action.

    Returns void

  • Creates an element that will be positioned on top of the dry/wet ink renderers.

    So as not to change the position of other overlays, overlay should either be styled to have 0 height or have position: absolute.

    This is useful for displaying content on top of the rendered content (e.g. a selection box).

    Parameters

    • overlay: HTMLElement

    Returns { remove: () => void }

  • apply a command. command will be announced for accessibility.

    Example:

    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));
    

    Parameters

    • command: Command
    • addToHistory: boolean = true

    Returns void | Promise<void>

  • Dispatches a command without announcing it. By default, does not add to history. Use this to show finalized commands that don't need to have announceForAccessibility called.

    If addToHistory is false, this is equivalent to command.apply(editor).

    Parameters

    • command: Command
    • addToHistory: boolean = false

    Returns void | Promise<void>

    const addToHistory = false;
    editor.dispatchNoAnnounce(editor.viewport.zoomTo(someRectangle), addToHistory);
  • Returns Color4

    the average of the colors of all background components. Use this to get the current background color.

  • Focuses the region used for text input/key commands.

    Returns void

  • Returns the size of the visible region of the output SVG

    Returns Rect2

  • Returns HTMLElement

    a reference to the editor's container.

      // Set the editor's height to 500px
    editor.getRootElement().style.height = '500px';
  • Dispatches a PointerEvent to the editor. The target element for evt must have the same top left as the content of the editor.

    Parameters

    • eventType: "pointercancel" | "pointerdown" | "pointermove" | "pointerup"
    • evt: PointerEvent

    Returns boolean

  • Adds event listners for keypresses (and drop events) on elem and forwards those events to the editor.

    If the given filter returns false for an event, the event is ignored and not passed to the editor.

    Parameters

    • elem: HTMLElement
    • filter: (event: KeyboardEvent) => boolean = ...

    Returns void

  • Like handlePointerEventsFrom except ignores short input gestures like clicks.

    filter is called once per event, before doing any other processing. If filter returns true the event is forwarded to the editor.

    otherEventsFilter is passed unmodified to handlePointerEventsFrom.

    Parameters

    • elem: HTMLElement
    • Optionalfilter: HTMLPointerEventFilter
    • OptionalotherEventsFilter: (eventName: string, event: Event) => boolean

    Returns { remove: () => void }

    • remove: () => void

      Remove all event listeners registered by this function.

  • Forward pointer events from elem to this editor. Such that right-click/right-click drag events are also forwarded, elem's contextmenu is disabled.

    filter is called once per pointer event, before doing any other processing. If filter returns true the event is forwarded to the editor.

    Note: otherEventsFilter is like filter, but is called for other pointer-related events that could also be forwarded to the editor. To forward just pointer events, for example, otherEventsFilter could be given as ()=>false.

    Parameters

    • elem: HTMLElement
    • Optionalfilter: HTMLPointerEventFilter
    • OptionalotherEventsFilter: (eventName: string, event: Event) => boolean

    Returns { remove: () => void }

    • remove: () => void

      Remove all event listeners registered by this function.

    const overlay = document.createElement('div');
    editor.createHTMLOverlay(overlay);

    // Send all pointer events that don't have the control key pressed
    // to the editor.
    editor.handlePointerEventsFrom(overlay, (event) => {
    if (event.ctrlKey) {
    return false;
    }
    return true;
    });
  • Alias for loadFrom(SVGLoader.fromString).

    Parameters

    • svgData: string
    • sanitize: boolean = false

    Returns Promise<void>

    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>
    `);
    
  • Schedule a re-render for some time in the near future. Does not schedule an additional re-render if a re-render is already queued.

    Returns Promise<void>

    a promise that resolves when re-rendering has completed.

  • Removes and destroys the editor. The editor cannot be added to a parent again after calling this method.

    Returns void

  • Re-renders the entire image.

    Parameters

    • showImageBounds: boolean = true

    Returns void

  • Dispatch a keyboard event to the currently selected tool. Intended for unit testing.

    If shiftKey is undefined, it is guessed from key.

    At present, the key code dispatched is guessed from the given key and, while this works for ASCII alphanumeric characters, this does not work for most non-alphanumeric keys.

    Because guessing the key code from key is problematic, only use this for testing.

    Parameters

    • eventType: KeyPressEvent | KeyUpEvent
    • key: string
    • ctrlKey: boolean = false
    • altKey: boolean = false
    • shiftKey: undefined | boolean = undefined

    Returns void

  • Attempts to prevent user-triggered events from modifying the content of the image.

    Parameters

    • readOnly: boolean

    Returns void

  • Shows an information dialog with legal notices.

    Returns void

  • Shows a "Loading..." message.

    Parameters

    • fractionLoaded: number

      should be a number from 0 to 1, where 1 represents completely loaded.

    Returns void

  • Get a data URL (e.g. as produced by HTMLCanvasElement::toDataURL). If format is not image/png, a PNG image URL may still be returned (as in the case of HTMLCanvasElement::toDataURL).

    The export resolution is the same as the size of the drawing canvas, unless outputSize is given.

    Example:

    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 ]);
    });
    

    Parameters

    • format: "image/png" | "image/jpeg" | "image/webp" = 'image/png'
    • OptionaloutputSize: Vec3

    Returns string

  • Converts the editor's content into an SVG image.

    If the output SVG has width or height less than options.minDimension, its size will be increased.

    Parameters

    • Optionaloptions: { minDimension?: number }

    Returns SVGElement

  • Converts the editor's content into an SVG image in an asynchronous, but potentially lossy way.

    Warning: If the image is being edited during an async rendering, edited components may not be rendered.

    Like toSVG, but can be configured to briefly pause after processing every pauseAfterCount items. This can prevent the editor from becoming unresponsive when saving very large images.

    Parameters

    • options: {
          minDimension?: number;
          onProgress?: (
              processedCountInLayer: number,
              totalToProcessInLayer: number,
          ) => Promise<boolean | void>;
          pauseAfterCount?: number;
      } = {}
      • OptionalminDimension?: number
      • OptionalonProgress?: (
            processedCountInLayer: number,
            totalToProcessInLayer: number,
        ) => Promise<boolean | void>

        Returns false to cancel the render. Note that totalToProcess is the total for the currently-being-processed layer.

      • OptionalpauseAfterCount?: number

        Number of components to process before pausing

    Returns Promise<SVGElement>

OpenSource licenses