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