UNPKG

photo-editor

Version:
289 lines (282 loc) 7.66 kB
import { Tool } from "./chunk-W32WS4AD.js"; // src/PhotoEditor.ts import { EventEmitter } from "eventemitter3"; // src/getInitialState.ts import { unwrap } from "krustykrab"; // src/waitForImageComplete.ts var waitForImageComplete = (image) => new Promise((resolve, reject) => { const onLoad = () => { image.removeEventListener("load", onLoad); image.removeEventListener("error", onError); resolve(); }; const onError = () => { image.removeEventListener("load", onLoad); image.removeEventListener("error", onError); reject(); }; image.addEventListener("load", onLoad); image.addEventListener("error", onError); }); // src/getInitialState.ts var emptyImageWarning = "PhotoEditor: source image is loaded with error"; var imageToBase64 = async (image) => { if (image.complete) { if (image.naturalWidth === 0) { console.warn(emptyImageWarning); } } else { try { await waitForImageComplete(image); } catch (e) { console.warn(emptyImageWarning); } } const fakeCanvasEl = document.createElement("canvas"); const ctx = unwrap(fakeCanvasEl.getContext("2d")); fakeCanvasEl.height = image.naturalHeight; fakeCanvasEl.width = image.naturalWidth; ctx.drawImage(image, 0, 0); return fakeCanvasEl.toDataURL(); }; var getInitialState = async (el, options) => { switch (options.sourceType) { case "current-canvas": return el.toDataURL(); case "canvas": return options.source.toDataURL(); case "img": { const base64image = await imageToBase64( options.source ); return base64image; } case "base64": return options.source; default: throw new Error( '"sourceType" should be one of: "current-canvas", "canvas", "img", "base64"' ); } }; // src/validateSource.ts var validateSource = (options) => { switch (options.sourceType) { case "current-canvas": return; case "canvas": { const { source } = options; if (!(source instanceof HTMLElement) || source.tagName !== "CANVAS") { throw new Error( 'PhotoEditor source for sourceType "canvas" should be a canvas' ); } return; } case "img": { const { source } = options; if (!(source instanceof HTMLElement) || source.tagName !== "IMG") { throw new Error( 'PhotoEditor source for sourceType "img" should be an image' ); } return; } case "base64": { const { source } = options; if (typeof source !== "string") { throw new Error( 'PhotoEditor source for sourceType "base64" should be a string' ); } return; } default: throw new Error( '"sourceType" should be one of: "current-canvas", "canvas", "img", "base64"' ); } }; // src/validateOptions.ts var validateOptions = (options) => { if (typeof options !== "object") { throw new Error("PhotoEditor options should be an object"); } if (options === null) { throw new Error("PhotoEditor options can't be null"); } if (typeof options.tools !== "object") { throw new Error("PhotoEditor tools should be an object"); } if (options.tools === null) { throw new Error("PhotoEditor tools can't be null"); } validateSource(options); }; // src/PhotoEditor.ts var defaultOptions = { sourceType: "current-canvas" }; var PhotoEditor = class extends EventEmitter { _el = null; _options = null; _currentState = -1; _states = []; _enabledToolId = null; _touched = false; _destroyed = false; tools = {}; constructor(el, editorOptions) { super(); const options = { ...defaultOptions, ...editorOptions }; if (!(el instanceof HTMLElement) || el.tagName !== "CANVAS") { throw new Error("Element for init PhotoEditor should be a canvas"); } validateOptions(options); this._el = el; this._options = options; this._init(); } async _init() { if (!this._el) { throw new Error("Canvas is not provided"); } if (!this._options) { throw new Error("Options are not provided"); } const initialState = await getInitialState(this._el, this._options); this._currentState = 0; this._states = [initialState]; if (this._options.sourceType !== "current-canvas") { await this._drawCurrentState(); } this._initTools(); this.emit("ready"); } _initTools() { if (!this._el) { throw new Error("Canvas is not provided"); } if (!this._options) { throw new Error("Options are not provided"); } const { tools } = this._options; const el = this._el; for (const toolIdRaw of Object.keys(tools)) { const toolId = toolIdRaw; const ToolConstructor = tools[toolId]; if (typeof ToolConstructor !== "function") { throw new Error( `PhotoEditor tool "${toolId}": should be a class that extends Tool` ); } const tool = new ToolConstructor({ el, pushState: this._pushState, updateState: this._updateState, disable: this.disableTool, touch: this.touch }); if (!(tool instanceof Tool)) { throw new Error( `PhotoEditor tool "${toolId}": should be an instance of Tool ` ); } this.tools[toolId] = tool; } } _pushState = (state) => { const slicedStates = this._states.slice(0, this._currentState + 1); slicedStates.push(state); ++this._currentState; this._states = slicedStates; }; _updateState = (state) => { const slicedStates = this._states.slice(0, this._currentState); slicedStates.push(state); this._states = slicedStates; }; async _drawCurrentState() { const base64 = this.getCurrentState(); const image = new Image(); image.src = base64; await waitForImageComplete(image); if (this._destroyed) { return; } if (!this._el) { throw new Error("Canvas is not provided"); } this._el.width = image.naturalWidth; this._el.height = image.naturalHeight; const ctx = this._el.getContext("2d"); if (!ctx) { throw new Error("Context is not found"); } ctx.drawImage(image, 0, 0); } destroy() { this._destroyed = true; for (const toolId of Object.keys(this.tools)) { const tool = this.tools[toolId]; tool.destroy(); } } getCurrentState() { return this._states[this._currentState]; } enableTool(toolId) { if (!this.tools[toolId]) { throw new Error(`PhotoEditor tool with id "${toolId}" is not defined`); } this.disableTool(); this._enabledToolId = toolId; this.tools[toolId].enableFromEditor(); this.emit("enableTool", toolId); } disableTool = () => { if (this._enabledToolId) { this.tools[this._enabledToolId].disableFromEditor(); this.emit("disableTool", this._enabledToolId); if (this._touched) { this._drawCurrentState(); } this._touched = false; this._enabledToolId = null; } }; toggleTool(toolId) { if (this._enabledToolId === toolId) { this.disableTool(); } else { this.enableTool(toolId); } } touch = () => { this._touched = true; }; undo() { if (this._currentState > 0) { --this._currentState; this.disableTool(); this._drawCurrentState(); } } redo() { if (this._currentState < this._states.length - 1) { ++this._currentState; this.disableTool(); this._drawCurrentState(); } } }; export { PhotoEditor, Tool }; //# sourceMappingURL=index.js.map