UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

286 lines (285 loc) 12 kB
import AbstractRenderer from '../rendering/renderers/AbstractRenderer'; import Viewport from '../Viewport'; import AbstractComponent from '../components/AbstractComponent'; import { Rect2 } from '@js-draw/math'; import RenderingCache from '../rendering/caching/RenderingCache'; import SerializableCommand from '../commands/SerializableCommand'; import EventDispatcher from '../EventDispatcher'; import Command from '../commands/Command'; export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void; export declare enum EditorImageEventType { ExportViewportChanged = 0, AutoresizeModeChanged = 1, ComponentAdded = 2, ComponentRemoved = 3 } interface StateChangeEvent { kind: EditorImageEventType.ExportViewportChanged | EditorImageEventType.AutoresizeModeChanged; image: EditorImage; } interface ComponentAddRemoveEvent { kind: EditorImageEventType.ComponentAdded | EditorImageEventType.ComponentRemoved; image: EditorImage; componentId: string; } export type EditorImageEvent = StateChangeEvent | ComponentAddRemoveEvent; export type EditorImageNotifier = EventDispatcher<EditorImageEventType, EditorImageEvent>; /** * A callback used to * 1. pause the render process * 2. observe progress through `componentsProcessed` and `totalComponents` * 3. stop the render process early by returning `false`. */ export type PreRenderComponentCallback = (component: AbstractComponent, componentsProcessed: number, totalComponents: number) => Promise<boolean>; /** * Handles lookup/storage of elements in the image. * * `js-draw` images are made up of a collection of {@link AbstractComponent}s (which * includes {@link Stroke}s, {@link TextComponent}s, etc.). An `EditorImage` * is the data structure that stores these components. * * Here's how to do a few common operations: * - **Get all components in a {@link @js-draw/math!Rect2 | Rect2}**: * {@link EditorImage.getComponentsIntersecting}. * - **Draw an `EditorImage` onto a canvas/SVG**: {@link EditorImage.render}. * - **Adding a new component**: {@link EditorImage.addComponent}. * * **Example**: * [[include:doc-pages/inline-examples/image-add-and-lookup.md]] */ export default class EditorImage { private root; private background; private componentsById; private componentCount; /** Viewport for the exported/imported image. */ private importExportViewport; private shouldAutoresizeExportViewport; readonly notifier: EditorImageNotifier; constructor(); getBackgroundComponents(): AbstractComponent[]; findParent(elem: AbstractComponent): ImageNode | null; queueRerenderOf(elem: AbstractComponent): void; /** @internal */ renderWithCache(screenRenderer: AbstractRenderer, cache: RenderingCache, viewport: Viewport): void; /** * Renders this image to the given `renderer`. * * If `viewport` is non-null, only components that can be seen from that viewport * will be rendered. If `viewport` is `null`, **all** components are rendered. * * **Example**: * [[include:doc-pages/inline-examples/canvas-renderer.md]] */ render(renderer: AbstractRenderer, viewport: Viewport | null): void; /** * Like {@link renderAll}, but can be stopped early and paused. * * **Note**: If the image is being edited during an async rendering, there is no * guarantee that all nodes will be rendered correctly (some may be missing). * * @internal */ renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>; /** * Renders all nodes, even ones not within the viewport. * * This can be slow for large images * @internal */ renderAll(renderer: AbstractRenderer): void; /** * @returns all elements in the image, sorted by z-index (low to high). * * This can be slow for large images. If you only need all elemenst in part of the image, * consider using {@link getComponentsIntersecting} instead. * * **Note**: The result does not include background elements. See {@link getBackgroundComponents}. */ getAllComponents(): AbstractComponent[]; /** @deprecated in favor of {@link getAllComponents} */ getAllElements(): AbstractComponent[]; /** Returns the number of elements added to this image. @internal */ estimateNumElements(): number; /** @deprecated @see getComponentsIntersecting */ getElementsIntersectingRegion(region: Rect2, includeBackground?: boolean): AbstractComponent[]; /** * @returns a list of `AbstractComponent`s intersecting `region`, sorted by increasing z-index. * * Components in the background layer are only included if `includeBackground` is `true`. */ getComponentsIntersecting(region: Rect2, includeBackground?: boolean): AbstractComponent[]; /** Called whenever (just after) an element is completely removed. @internal */ onDestroyElement(elem: AbstractComponent): void; /** Called just after an element is added. @internal */ private onElementAdded; /** * @returns the AbstractComponent with `id`, if it exists. * * @see {@link AbstractComponent.getId} */ lookupElement(id: string): AbstractComponent | null; private addComponentDirectly; private removeElementDirectly; /** * Returns a command that adds the given element to the `EditorImage`. * If `applyByFlattening` is true, the content of the wet ink renderer is * rendered onto the main rendering canvas instead of doing a full re-render. * * @see {@link Display.flatten} * * **Example**: * * [[include:doc-pages/inline-examples/adding-a-stroke.md]] */ static addComponent(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand; /** @see EditorImage.addComponent */ addComponent(component: AbstractComponent, applyByFlattening?: boolean): SerializableCommand; /** Alias for {@link addComponent}. @deprecated Prefer `.addComponent` */ addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand; /** Alias for {@link addComponent}. @deprecated Prefer `.addComponent`. */ static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand; private static AddComponentCommand; /** * @returns a `Viewport` for rendering the image when importing/exporting. */ getImportExportViewport(): Viewport; /** * @see {@link setImportExportRect} */ getImportExportRect(): Rect2; /** * Sets the import/export rectangle to the given `imageRect`. Disables * autoresize if it was previously enabled. * * **Note**: The import/export rectangle is the same as the size of any * {@link BackgroundComponent}s (and other components that auto-resize). */ setImportExportRect(imageRect: Rect2): SerializableCommand; /** @see {@link setAutoresizeEnabled} */ getAutoresizeEnabled(): boolean; /** * Returns a `Command` that sets whether the image should autoresize when * {@link AbstractComponent}s are added/removed. * * @example * * ```ts,runnable * 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); * ``` */ setAutoresizeEnabled(autoresize: boolean): Command; private setAutoresizeEnabledDirectly; /** Updates the size/position of the viewport */ private autoresizeExportViewport; private settingExportRect; /** * Sets the import/export viewport directly, without returning a `Command`. * As such, this is not undoable. * * See setImportExportRect * * Returns true if changes to the viewport were made (and thus * ExportViewportChanged was fired.) */ private setExportRectDirectly; private onExportViewportChanged; /** * @internal * * Enables debug mode for **all** `EditorImage`s. * * **Only use for debugging**. * * @internal */ static setDebugMode(newDebugMode: boolean): void; private static SetImportExportRectCommand; } /** * Determines the first index in `sortedLeaves` that needs to be rendered * (based on occlusion -- everything before that index can be skipped and * produce a visually-equivalent image). * * Does nothing if visibleRect is not provided * * @internal */ export declare const computeFirstIndexToRender: (sortedLeaves: Array<ImageNode>, visibleRect?: Rect2) => number; type TooSmallToRenderCheck = (rect: Rect2) => boolean; /** * Part of the Editor's image. Does not handle fullscreen/invisible components. * @internal */ export declare class ImageNode { private parent; private content; private bbox; private children; private targetChildCount; private id; private static idCounter; constructor(parent?: ImageNode | null); getId(): number; onContentChange(): void; getContent(): AbstractComponent | null; getParent(): ImageNode | null; protected getChildrenIntersectingRegion(region: Rect2, isTooSmallFilter?: TooSmallToRenderCheck): ImageNode[]; getChildrenOrSelfIntersectingRegion(region: Rect2, isTooSmall?: TooSmallToRenderCheck): ImageNode[]; /** * Returns a list of `ImageNode`s with content (and thus no children). * Override getChildrenIntersectingRegion to customize how this method * determines whether/which children are in `region`. * * @paran region - All resultant `ImageNode`s must intersect `region`. * @param isTooSmall - If `isTooSmall` returns true for an image node, that node * is excluded from the output. * */ getLeavesIntersectingRegion(region: Rect2, isTooSmall?: TooSmallToRenderCheck): ImageNode[]; getChildWithContent(target: AbstractComponent): ImageNode | null; getLeaves(): ImageNode[]; addLeaf(leaf: AbstractComponent): ImageNode; protected static createLeafNode(parent: ImageNode, content: AbstractComponent): ImageNode; getBBox(): Rect2; recomputeBBox(bubbleUp: boolean): void; private unionBBoxWith; private updateParents; private rebalance; protected removeChild(child: ImageNode): void; remove(): void; renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>; render(renderer: AbstractRenderer, visibleRect?: Rect2): void; renderDebugBoundingBoxes(renderer: AbstractRenderer, visibleRect: Rect2, depth?: number): void; private checkRep; } /** An `ImageNode` that can properly handle fullscreen/data components. @internal */ export declare class RootImageNode extends ImageNode { private fullscreenChildren; private dataComponents; protected getChildrenIntersectingRegion(region: Rect2, _isTooSmall?: TooSmallToRenderCheck): ImageNode[]; getChildrenOrSelfIntersectingRegion(region: Rect2, _isTooSmall?: TooSmallToRenderCheck): ImageNode[]; getLeaves(): ImageNode[]; removeChild(child: ImageNode): void; getChildWithContent(target: AbstractComponent): ImageNode | null; addLeaf(leafContent: AbstractComponent): ImageNode; } export {};