@infinite-canvas-tutorial/webcomponents
Version:
WebComponents UI implementation
236 lines • 10.4 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ExtendedAPI_threads;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtendedAPI = exports.pendingCanvases = void 0;
const uuid_1 = require("uuid");
const ecs_1 = require("@infinite-canvas-tutorial/ecs");
const event_1 = require("./event");
const images_1 = require("@loaders.gl/images");
const core_1 = require("@loaders.gl/core");
const utils_1 = require("./utils");
const util_1 = require("@antv/util");
/**
* Since the canvas is created in the system, we need to store them here for later use.
*/
exports.pendingCanvases = [];
// const PenMap = {
// [Pen.BRUSH]: {
// icon: html`<sp-icon-brush slot="icon"></sp-icon-brush>`,
// label: msg(str`Brush`),
// },
// [Pen.VECTOR_NETWORK]: {
// icon: html`<sp-icon-vector-draw slot="icon"></sp-icon-vector-draw>`,
// label: msg(str`Vector Network`),
// },
// [Pen.COMMENT]: {
// icon: html`<sp-icon-comment slot="icon"></sp-icon-comment>`,
// label: msg(str`Comment`),
// },
// };
/**
* Emit CustomEvents for the canvas.
*/
class ExtendedAPI extends ecs_1.API {
constructor(stateManagement, commands, element) {
super(stateManagement, commands);
this.element = element;
/**
* Threads and comments
* @see https://liveblocks.io/docs/ready-made-features/comments
*/
_ExtendedAPI_threads.set(this, []);
}
resizeCanvas(width, height) {
super.resizeCanvas(width, height);
this.element.dispatchEvent(new CustomEvent(event_1.Event.RESIZED, { detail: { width, height } }));
}
selectNodes(selected, preserveSelection = false, updateAppState = true) {
super.selectNodes(selected, preserveSelection, updateAppState);
this.element.dispatchEvent(new CustomEvent(event_1.Event.SELECTED_NODES_CHANGED, {
detail: { selected, preserveSelection },
}));
}
updateNode(node, diff, updateAppState = true, skipOverrideKeys = []) {
super.updateNode(node, diff, updateAppState, skipOverrideKeys);
if (updateAppState) {
this.element.dispatchEvent(new CustomEvent(event_1.Event.NODE_UPDATED, { detail: { node } }));
}
}
updateNodes(nodes, updateAppState = true) {
super.updateNodes(nodes, updateAppState);
if (updateAppState) {
this.element.dispatchEvent(new CustomEvent(event_1.Event.NODES_UPDATED, {
detail: {
nodes,
},
}));
}
}
deleteNodesById(ids) {
const nodes = super.deleteNodesById(ids);
this.element.dispatchEvent(new CustomEvent(event_1.Event.NODE_DELETED, {
detail: {
nodes,
},
}));
return nodes;
}
/**
* Delete Canvas component
*/
destroy() {
super.destroy();
this.element.dispatchEvent(new CustomEvent(event_1.Event.DESTROY));
}
setThreads(threads) {
__classPrivateFieldSet(this, _ExtendedAPI_threads, threads, "f");
}
getThreads() {
return __classPrivateFieldGet(this, _ExtendedAPI_threads, "f");
}
/**
* I18n
*/
setLocale(locale) {
throw new Error('Method not implemented.');
}
getLocale() {
throw new Error('Method not implemented.');
}
setThemeMode(themeMode) {
this.element.dispatchEvent(new CustomEvent('theme-change', {
detail: {
themeMode,
},
bubbles: true,
composed: true,
}));
this.setAppState({
themeMode,
});
}
createImageFromFile(file_1) {
return __awaiter(this, arguments, void 0, function* (file, { position, heuristicResize, } = {}) {
var _a, _b;
const size = {
width: this.element.clientWidth,
height: this.element.clientHeight,
zoom: this.getAppState().cameraZoom,
};
const [image, dataURL] = yield Promise.all([
(0, core_1.load)(file, images_1.ImageLoader),
(0, util_1.isString)(file) ? Promise.resolve(file) : (0, utils_1.getDataURL)(file),
]);
let cdnUrl = dataURL;
if (!(0, util_1.isString)(file) && this.upload) {
cdnUrl = yield this.upload(file);
}
let height = image.height;
let width = image.width;
if (heuristicResize) {
// Heuristic to calculate the size of the image.
// @see https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/components/App.tsx#L10059
const minHeight = Math.max(size.height - 120, 160);
// max 65% of canvas height, clamped to <300px, vh - 120px>
const maxHeight = Math.min(minHeight, Math.floor(size.height * 0.5) / size.zoom);
height = Math.min(image.height, maxHeight);
width = height * (image.width / image.height);
}
const maxZIndex = this.getNodes().reduce((max, node) => { var _a; return Math.max(max, (_a = node.zIndex) !== null && _a !== void 0 ? _a : 0); }, 0);
const node = {
id: (0, uuid_1.v4)(),
type: 'rect',
x: ((_a = position === null || position === void 0 ? void 0 : position.x) !== null && _a !== void 0 ? _a : 0) - width / 2,
y: ((_b = position === null || position === void 0 ? void 0 : position.y) !== null && _b !== void 0 ? _b : 0) - height / 2,
width,
height,
fill: cdnUrl,
lockAspectRatio: true,
zIndex: maxZIndex + 1,
};
(0, utils_1.updateAndSelectNodes)(this, this.getAppState(), [
node,
]);
return node;
});
}
/**
* Used by image model to edit with.
*/
createMask(nodes, relativeTo) {
const canvas = ecs_1.DOMAdapter.get().createCanvas(relativeTo.width, relativeTo.height);
const ctx = canvas.getContext('2d');
// 在图像处理和 AI 掩码(Mask)中,数值通常映射在 0 到 1 之间:
// 白色 (White, 值为 1 或 255): 代表“激活”或“满分”。模型会识别这个区域,并在这里进行扩散生成。
// 黑色 (Black, 值为 0): 代表“屏蔽”或“零分”。模型会忽略这个区域,或者说将其锁定,保持原图不动。
ctx.clearRect(0, 0, relativeTo.width, relativeTo.height);
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, relativeTo.width, relativeTo.height);
ctx.fillStyle = 'white';
ctx.strokeStyle = 'white';
nodes.forEach(node => {
if (node.type === 'rect') {
const { x, y, width, height } = node;
ctx.fillRect(x, y, width, height);
}
else if (node.type === 'path') {
const { d, strokeWidth, x, y } = node;
ctx.beginPath();
(0, util_1.path2Absolute)(d).forEach(([command, ...data]) => {
if (command === 'M') {
ctx.moveTo(data[0] + x, data[1] + y);
}
else if (command === 'L') {
ctx.lineTo(data[0] + x, data[1] + y);
}
else if (command === 'C') {
ctx.bezierCurveTo(data[0] + x, data[1] + y, data[2] + x, data[3] + y, data[4] + x, data[5] + y);
}
});
ctx.closePath();
ctx.fill();
ctx.lineWidth = strokeWidth;
ctx.stroke();
}
else if (node.type === 'polyline') {
const { points, strokeWidth, strokeLinecap, strokeLinejoin, x, y } = node;
(0, ecs_1.deserializePoints)(points).forEach((point, index) => {
if (index === 0) {
ctx.moveTo(point[0] + x, point[1] + y);
}
else {
ctx.lineTo(point[0] + x, point[1] + y);
}
});
ctx.lineWidth = strokeWidth;
ctx.lineCap = strokeLinecap;
ctx.lineJoin = strokeLinejoin;
ctx.stroke();
}
});
return canvas;
}
}
exports.ExtendedAPI = ExtendedAPI;
_ExtendedAPI_threads = new WeakMap();
//# sourceMappingURL=API.js.map