UNPKG

js-draw

Version:

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

190 lines (189 loc) 8.23 kB
import SerializableCommand from '../commands/SerializableCommand'; import EditorImage from '../image/EditorImage'; import { LineSegment2, Mat33, Path, Rect2 } from '@js-draw/math'; import AbstractRenderer from '../rendering/renderers/AbstractRenderer'; import { ImageComponentLocalization } from './localization'; import Viewport from '../Viewport'; import { Point2 } from '@js-draw/math'; export type LoadSaveData = string[] | Record<symbol, string | number>; export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>; export type DeserializeCallback = (data: string) => AbstractComponent; export declare enum ComponentSizingMode { /** The default. The compnent gets its size from its bounding box. */ BoundingBox = 0, /** Causes the component to fill the entire visible region of the screen */ FillScreen = 1, /** * Displays the component anywhere (arbitrary location) on the * canvas. (Ignoring the bounding box). * * These components may be ignored unless a full render is done. * * Intended for compnents that need to be rendered on a full export, * but won't be visible to the user. * * For example, a metadata component. */ Anywhere = 2 } /** * A base class for everything that can be added to an {@link EditorImage}. * * In addition to the `abstract` methods, there are a few methods that should be * overridden when creating a selectable/erasable subclass: * - {@link keyPoints}: Overriding this may improve how the component interacts with the selection tool. * - {@link withRegionErased}: Override/implement this to allow the component to be partially erased * by the partial stroke eraser. */ export default abstract class AbstractComponent { private readonly componentKind; protected lastChangedTime: number; /** * The bounding box of this component. * {@link getBBox}, by default, returns `contentBBox`. * This must be set by components. * * If this changes, {@link EditorImage.queueRerenderOf} should be called for * this object (provided that this object has been added to the editor.) * * **Note**: This value is ignored if {@link getSizingMode} returns `FillScreen` * or `FillImage`. */ protected abstract contentBBox: Rect2; private zIndex; private id; private static zIndexCounter; protected constructor(componentKind: string, initialZIndex?: number); getId(): string; private static deserializationCallbacks; static registerComponent(componentKind: string, deserialize: DeserializeCallback | null): void; private loadSaveData; /** * Attach data that can be used while exporting the component (e.g. to SVG). * * This is intended for use by an {@link ImageLoader}. */ attachLoadSaveData(key: string, data: LoadSaveData): void; /** See {@link attachLoadSaveData} */ getLoadSaveData(): LoadSaveDataTable; getZIndex(): number; /** * @returns the bounding box of this. This can be a slight overestimate if doing so * significantly improves performance. */ getBBox(): Rect2; /** * @returns the bounding box of this. Unlike `getBBox`, this should **not** be a rough estimate. */ getExactBBox(): Rect2; /** * Returns information about how this component should be displayed * (e.g. fill the screen or get its size from {@link getBBox}). * * {@link EditorImage.queueRerenderOf} must be called to apply changes to * the output of this method if this component has already been added to an * {@link EditorImage}. */ getSizingMode(): ComponentSizingMode; /** * **Optimization** * * Should return `true` if this component covers the entire `visibleRect` * and would prevent anything below this component from being visible. * * Should return `false` otherwise. */ occludesEverythingBelowWhenRenderedInRect(_visibleRect: Rect2): boolean; /** Called when this component is added to the given image. */ onAddToImage(_image: EditorImage): void; onRemoveFromImage(): void; /** * Renders this component onto the given `canvas`. * * If `visibleRect` is given, it should be the region of `canvas` that is visible -- * rendering anything outside of `visibleRect` should have no visible effect on the * resultant image. * * For optimal performance, implementers should call `canvas.startObject` and `canvas.endObject` * before and after rendering. */ abstract render(canvas: AbstractRenderer, visibleRect?: Rect2): void; /** @return true if `lineSegment` intersects this component. */ abstract intersects(lineSegment: LineSegment2): boolean; /** * @returns true if this component intersects `rect` -- it is entirely contained * within the rectangle or one of the rectangle's edges intersects this component. * * The default implementation assumes that `this.getExactBBox()` returns a tight bounding box * -- that any horiziontal/vertical line that intersects this' boounding box also * intersects a point in this component. If this is not the case, components must override * this function. */ intersectsRect(rect: Rect2): boolean; /** * Returns a selection of points within this object. Each contiguous section * of this object should have a point in the returned array. * * Subclasses should override this method if the center of the bounding box is * not contained within the object. */ keyPoints(): Point2[]; isSelectable(): boolean; isBackground(): boolean; getProportionalRenderingTime(): number; protected abstract applyTransformation(affineTransfm: Mat33): void; /** * Returns a command that, when applied, transforms this by [affineTransfm] and * updates the editor. * * The transformed component is also moved to the top (use * {@link AbstractComponent#setZIndexAndTransformBy} to avoid this behavior). */ transformBy(affineTransfm: Mat33): SerializableCommand; setZIndex(newZIndex: number): SerializableCommand; /** * Combines {@link transformBy} and {@link setZIndex} into a single command. * * @param newZIndex - The z-index this component should have after applying this command. * @param originalZIndex - @internal The z-index the component should revert to after unapplying * this command. */ setZIndexAndTransformBy(affineTransfm: Mat33, newZIndex: number, originalZIndex?: number): SerializableCommand; private static transformElementCommandId; private static TransformElementCommand; /** * @return a description that could be read by a screen reader * (e.g. when adding/erasing the component) */ abstract description(localizationTable: ImageComponentLocalization): string; protected abstract createClone(): AbstractComponent; clone(): AbstractComponent; /** * Creates a copy of this component with a particular `id`. * This is used internally by {@link Duplicate} when deserializing. * * @internal -- users of the library shouldn't need this. */ cloneWithId(cloneId: string): AbstractComponent; /** * **Optional method**: Divides this component into sections roughly along the given path, * removing parts that are roughly within `shape`. * * **Notes**: * - A default implementation may be provided for this method in the future. Until then, * this method is `undefined` if unsupported. * * `viewport` should be provided to determine how newly-added points should be rounded. */ withRegionErased?(shape: Path, viewport: Viewport): AbstractComponent[]; protected abstract serializeToJSON(): any[] | Record<string, any> | number | string | null; serialize(): { name: string; zIndex: number; id: string; loadSaveData: LoadSaveDataTable; data: string | number | any[] | Record<string, any>; }; private static isNotDeserializable; static deserialize(json: any): AbstractComponent; }