Merge pull request #176 from idrawjs/dev-improve-0.3
Refactor and format code
2
.npmrc
|
|
@ -1 +1 @@
|
|||
# registry=https://registry.npmmirror.com
|
||||
registry=https://registry.npmmirror.com
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 762 KiB After Width: | Height: | Size: 772 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 389 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 161 KiB |
|
|
@ -1,6 +1,4 @@
|
|||
module.exports = {
|
||||
// "collectCoverage": true,
|
||||
testEnvironment: 'jsdom',
|
||||
testTimeout: 2 * 60 * 1000,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
modulePaths: ['<rootDir>'],
|
||||
|
|
|
|||
50
package.json
|
|
@ -28,47 +28,47 @@
|
|||
"pu2": "lerna version && npm run build && lerna publish from-git --force-publish"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.0",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.21.0",
|
||||
"@babel/core": "^7.21.4",
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@babel/preset-typescript": "^7.21.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/koa-compose": "^3.2.5",
|
||||
"@types/node": "^18.14.1",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/serve-handler": "^6.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
||||
"@typescript-eslint/parser": "^5.53.0",
|
||||
"babel-jest": "^29.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
||||
"@typescript-eslint/parser": "^5.57.0",
|
||||
"babel-jest": "^29.5.0",
|
||||
"canvas": "^2.11.0",
|
||||
"chalk": "^5.2.0",
|
||||
"enquirer": "^2.3.6",
|
||||
"esbuild": "^0.17.10",
|
||||
"eslint": "^8.34.0",
|
||||
"execa": "^7.0.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"glob": "^8.1.0",
|
||||
"esbuild": "^0.17.15",
|
||||
"eslint": "^8.37.0",
|
||||
"execa": "^7.1.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"glob": "^9.3.2",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.4.3",
|
||||
"jest-canvas-mock": "^2.4.0",
|
||||
"jest-environment-jsdom": "^29.4.3",
|
||||
"jest": "^29.5.0",
|
||||
"jest-canvas-mock": "^2.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"jimp": "^0.22.7",
|
||||
"koa-compose": "^4.1.0",
|
||||
"lerna": "^6.5.1",
|
||||
"lerna": "^6.6.1",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"pngjs": "^7.0.0",
|
||||
"puppeteer": "^19.7.2",
|
||||
"rollup": "^3.17.2",
|
||||
"rollup-plugin-dts": "^5.2.0",
|
||||
"puppeteer": "^19.8.2",
|
||||
"rollup": "^3.20.2",
|
||||
"rollup-plugin-dts": "^5.3.0",
|
||||
"rollup-plugin-esbuild": "^5.0.0",
|
||||
"serve-handler": "^6.1.5",
|
||||
"terser": "^5.16.5",
|
||||
"ts-morph": "^17.0.1",
|
||||
"terser": "^5.16.8",
|
||||
"ts-morph": "^18.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.5.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.4",
|
||||
"vite-node": "^0.28.5"
|
||||
"typescript": "^5.0.3",
|
||||
"vite": "^4.2.1",
|
||||
"vite-node": "^0.29.8"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
TypeScreenPosition,
|
||||
TypeScreenSize,
|
||||
TypeScreenContext,
|
||||
TypePoint,
|
||||
TypePointCursor,
|
||||
TypeBoardOptions,
|
||||
TypeBoardSizeOptions,
|
||||
TypeContext
|
||||
ScreenPosition,
|
||||
ScreenSize,
|
||||
ScreenContext,
|
||||
Point,
|
||||
PointCursor,
|
||||
BoardOptions,
|
||||
BoardSizeOptions,
|
||||
IDrawContext
|
||||
} from '@idraw/types';
|
||||
import util from '@idraw/util';
|
||||
import { ScreenWatcher } from './lib/screen-watcher';
|
||||
|
|
@ -18,119 +18,98 @@ import { Screen } from './lib/screen';
|
|||
|
||||
const { throttle, Context } = util;
|
||||
|
||||
import {
|
||||
_canvas,
|
||||
_displayCanvas,
|
||||
_mount,
|
||||
_opts,
|
||||
_hasRendered,
|
||||
_ctx,
|
||||
_watcher,
|
||||
_render,
|
||||
_parsePrivateOptions,
|
||||
_scroller,
|
||||
_helperCanvas,
|
||||
_helperCtx,
|
||||
_initEvent,
|
||||
_doScrollX,
|
||||
_doScrollY,
|
||||
_doMoveScroll,
|
||||
_resetContext,
|
||||
_screen
|
||||
} from './names';
|
||||
|
||||
type PrivateOptions = TypeBoardOptions & {
|
||||
type PrivateOptions = BoardOptions & {
|
||||
devicePixelRatio: number;
|
||||
};
|
||||
|
||||
export default class Board {
|
||||
private [_hasRendered] = false;
|
||||
private _hasRendered = false;
|
||||
|
||||
private [_canvas]: HTMLCanvasElement;
|
||||
private [_helperCanvas]: HTMLCanvasElement;
|
||||
private [_displayCanvas]: HTMLCanvasElement;
|
||||
private [_mount]: HTMLDivElement;
|
||||
private [_opts]: PrivateOptions;
|
||||
private [_ctx]: TypeContext;
|
||||
private [_helperCtx]: TypeContext;
|
||||
// private [_watcher]: Watcher;
|
||||
private [_watcher]: ScreenWatcher;
|
||||
private [_scroller]: Scroller;
|
||||
private [_screen]: Screen;
|
||||
// private [_tempData]: TempData;
|
||||
private _canvas: HTMLCanvasElement;
|
||||
private _helperCanvas: HTMLCanvasElement;
|
||||
private _displayCanvas: HTMLCanvasElement;
|
||||
private _mount: HTMLDivElement;
|
||||
private _opts: PrivateOptions;
|
||||
private _ctx: IDrawContext;
|
||||
private _helperCtx: IDrawContext;
|
||||
// private _watcher: Watcher;
|
||||
private _watcher: ScreenWatcher;
|
||||
private _scroller: Scroller;
|
||||
private _screen: Screen;
|
||||
// private _tempData: TempData;
|
||||
|
||||
constructor(mount: HTMLDivElement, opts: TypeBoardOptions) {
|
||||
// this[_tempData] = new TempData(opts);
|
||||
constructor(mount: HTMLDivElement, opts: BoardOptions) {
|
||||
// this._tempData = new TempData(opts);
|
||||
|
||||
this[_mount] = mount;
|
||||
this[_canvas] = document.createElement('canvas');
|
||||
this[_helperCanvas] = document.createElement('canvas');
|
||||
this[_displayCanvas] = document.createElement('canvas');
|
||||
this[_mount].appendChild(this[_displayCanvas]);
|
||||
this[_opts] = this[_parsePrivateOptions](opts);
|
||||
this._mount = mount;
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._helperCanvas = document.createElement('canvas');
|
||||
this._displayCanvas = document.createElement('canvas');
|
||||
this._mount.appendChild(this._displayCanvas);
|
||||
this._opts = this._parsePrivateOptions(opts);
|
||||
|
||||
const originCtx2d = this[_canvas].getContext(
|
||||
const originCtx2d = this._canvas.getContext(
|
||||
'2d'
|
||||
) as CanvasRenderingContext2D;
|
||||
const displayCtx2d = this[_displayCanvas].getContext(
|
||||
const displayCtx2d = this._displayCanvas.getContext(
|
||||
'2d'
|
||||
) as CanvasRenderingContext2D;
|
||||
const helperCtx2d = this[_helperCanvas].getContext(
|
||||
const helperCtx2d = this._helperCanvas.getContext(
|
||||
'2d'
|
||||
) as CanvasRenderingContext2D;
|
||||
this[_ctx] = new Context(originCtx2d, this[_opts]);
|
||||
this[_helperCtx] = new Context(helperCtx2d, this[_opts]);
|
||||
this[_screen] = new Screen(this[_ctx], this[_opts]);
|
||||
// this[_watcher] = new Watcher(this[_displayCanvas]);
|
||||
this[_watcher] = new ScreenWatcher(this[_displayCanvas], this[_ctx]);
|
||||
this[_scroller] = new Scroller(displayCtx2d, {
|
||||
this._ctx = new Context(originCtx2d, this._opts);
|
||||
this._helperCtx = new Context(helperCtx2d, this._opts);
|
||||
this._screen = new Screen(this._ctx, this._opts);
|
||||
// this._watcher = new Watcher(this._displayCanvas);
|
||||
this._watcher = new ScreenWatcher(this._displayCanvas, this._ctx);
|
||||
this._scroller = new Scroller(displayCtx2d, {
|
||||
width: opts.width,
|
||||
height: opts.height,
|
||||
devicePixelRatio: opts.devicePixelRatio || 1,
|
||||
scrollConfig: opts.scrollConfig
|
||||
});
|
||||
this[_render]();
|
||||
this._render();
|
||||
}
|
||||
|
||||
getDisplayContext2D(): CanvasRenderingContext2D {
|
||||
return this[_displayCanvas].getContext('2d') as CanvasRenderingContext2D;
|
||||
return this._displayCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
}
|
||||
|
||||
getOriginContext2D(): CanvasRenderingContext2D {
|
||||
return this[_ctx].getContext();
|
||||
return this._ctx.getContext();
|
||||
}
|
||||
|
||||
getHelperContext2D(): CanvasRenderingContext2D {
|
||||
return this[_helperCtx].getContext();
|
||||
return this._helperCtx.getContext();
|
||||
}
|
||||
|
||||
getContext(): TypeContext {
|
||||
return this[_ctx];
|
||||
getContext(): IDrawContext {
|
||||
return this._ctx;
|
||||
}
|
||||
|
||||
getHelperContext(): TypeContext {
|
||||
return this[_helperCtx];
|
||||
getHelperContext(): IDrawContext {
|
||||
return this._helperCtx;
|
||||
}
|
||||
|
||||
scale(scaleRatio: number): TypeScreenContext {
|
||||
scale(scaleRatio: number): ScreenContext {
|
||||
if (scaleRatio > 0) {
|
||||
this[_ctx].setTransform({ scale: scaleRatio });
|
||||
this[_helperCtx].setTransform({ scale: scaleRatio });
|
||||
this._ctx.setTransform({ scale: scaleRatio });
|
||||
this._helperCtx.setTransform({ scale: scaleRatio });
|
||||
}
|
||||
const { position, size } = this[_screen].calcScreen();
|
||||
const { position, size } = this._screen.calcScreen();
|
||||
return { position, size };
|
||||
}
|
||||
|
||||
scrollX(x: number) {
|
||||
this[_watcher].setStatusMap({
|
||||
this._watcher.setStatusMap({
|
||||
canScrollYPrev: true,
|
||||
canScrollYNext: true,
|
||||
canScrollXPrev: true,
|
||||
canScrollXNext: true
|
||||
});
|
||||
if (x >= 0 || x < 0) {
|
||||
this[_ctx].setTransform({ scrollX: x });
|
||||
this[_helperCtx].setTransform({ scrollX: x });
|
||||
this._ctx.setTransform({ scrollX: x });
|
||||
this._helperCtx.setTransform({ scrollX: x });
|
||||
}
|
||||
const {
|
||||
position,
|
||||
|
|
@ -139,8 +118,8 @@ export default class Board {
|
|||
canScrollYNext,
|
||||
canScrollXPrev,
|
||||
canScrollYPrev
|
||||
} = this[_screen].calcScreen();
|
||||
this[_watcher].setStatusMap({
|
||||
} = this._screen.calcScreen();
|
||||
this._watcher.setStatusMap({
|
||||
canScrollYPrev,
|
||||
canScrollYNext,
|
||||
canScrollXPrev,
|
||||
|
|
@ -149,16 +128,16 @@ export default class Board {
|
|||
return { position, size };
|
||||
}
|
||||
|
||||
scrollY(y: number): TypeScreenContext {
|
||||
this[_watcher].setStatusMap({
|
||||
scrollY(y: number): ScreenContext {
|
||||
this._watcher.setStatusMap({
|
||||
canScrollYPrev: true,
|
||||
canScrollYNext: true,
|
||||
canScrollXPrev: true,
|
||||
canScrollXNext: true
|
||||
});
|
||||
if (y >= 0 || y < 0) {
|
||||
this[_ctx].setTransform({ scrollY: y });
|
||||
this[_helperCtx].setTransform({ scrollY: y });
|
||||
this._ctx.setTransform({ scrollY: y });
|
||||
this._helperCtx.setTransform({ scrollY: y });
|
||||
}
|
||||
const {
|
||||
position,
|
||||
|
|
@ -167,8 +146,8 @@ export default class Board {
|
|||
canScrollYNext,
|
||||
canScrollXPrev,
|
||||
canScrollYPrev
|
||||
} = this[_screen].calcScreen();
|
||||
this[_watcher].setStatusMap({
|
||||
} = this._screen.calcScreen();
|
||||
this._watcher.setStatusMap({
|
||||
canScrollYPrev,
|
||||
canScrollYNext,
|
||||
canScrollXPrev,
|
||||
|
|
@ -178,40 +157,40 @@ export default class Board {
|
|||
}
|
||||
|
||||
getTransform() {
|
||||
return this[_ctx].getTransform();
|
||||
return this._ctx.getTransform();
|
||||
}
|
||||
|
||||
draw(): TypeScreenContext {
|
||||
draw(): ScreenContext {
|
||||
this.clear();
|
||||
const { position, deviceSize, size } = this[_screen].calcScreen();
|
||||
const displayCtx = this[_displayCanvas].getContext('2d');
|
||||
const { position, deviceSize, size } = this._screen.calcScreen();
|
||||
const displayCtx = this._displayCanvas.getContext('2d');
|
||||
displayCtx?.drawImage(
|
||||
this[_canvas],
|
||||
this._canvas,
|
||||
deviceSize.x,
|
||||
deviceSize.y,
|
||||
deviceSize.w,
|
||||
deviceSize.h
|
||||
);
|
||||
displayCtx?.drawImage(
|
||||
this[_helperCanvas],
|
||||
this._helperCanvas,
|
||||
deviceSize.x,
|
||||
deviceSize.y,
|
||||
deviceSize.w,
|
||||
deviceSize.h
|
||||
);
|
||||
if (this[_opts].canScroll === true) {
|
||||
this[_scroller].draw(position);
|
||||
if (this._opts.canScroll === true) {
|
||||
this._scroller.draw(position);
|
||||
}
|
||||
return { position, size };
|
||||
}
|
||||
|
||||
clear() {
|
||||
const displayCtx = this[_displayCanvas].getContext('2d');
|
||||
const displayCtx = this._displayCanvas.getContext('2d');
|
||||
displayCtx?.clearRect(
|
||||
0,
|
||||
0,
|
||||
this[_displayCanvas].width,
|
||||
this[_displayCanvas].height
|
||||
this._displayCanvas.width,
|
||||
this._displayCanvas.height
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -219,59 +198,59 @@ export default class Board {
|
|||
name: T,
|
||||
callback: (p: TypeBoardEventArgMap[T]) => void
|
||||
) {
|
||||
this[_watcher].on(name, callback);
|
||||
this._watcher.on(name, callback);
|
||||
}
|
||||
|
||||
off<T extends keyof TypeBoardEventArgMap>(
|
||||
name: T,
|
||||
callback: (p: TypeBoardEventArgMap[T]) => void
|
||||
) {
|
||||
this[_watcher].off(name, callback);
|
||||
this._watcher.off(name, callback);
|
||||
}
|
||||
|
||||
getScreenInfo(): {
|
||||
size: TypeScreenSize;
|
||||
position: TypeScreenPosition;
|
||||
deviceSize: TypeScreenSize;
|
||||
size: ScreenSize;
|
||||
position: ScreenPosition;
|
||||
deviceSize: ScreenSize;
|
||||
width: number;
|
||||
height: number;
|
||||
devicePixelRatio: number;
|
||||
// eslint-disable-next-line indent
|
||||
} {
|
||||
return this[_screen].calcScreen();
|
||||
return this._screen.calcScreen();
|
||||
}
|
||||
|
||||
setCursor(cursor: TypePointCursor) {
|
||||
this[_displayCanvas].style.cursor = cursor;
|
||||
setCursor(cursor: PointCursor) {
|
||||
this._displayCanvas.style.cursor = cursor;
|
||||
}
|
||||
|
||||
resetCursor() {
|
||||
this[_displayCanvas].style.cursor = 'auto';
|
||||
this._displayCanvas.style.cursor = 'auto';
|
||||
}
|
||||
|
||||
resetSize(opts: TypeBoardSizeOptions) {
|
||||
this[_opts] = { ...this[_opts], ...opts };
|
||||
this[_resetContext]();
|
||||
this[_ctx].resetSize(opts);
|
||||
this[_helperCtx].resetSize(opts);
|
||||
this[_screen].resetSize(opts);
|
||||
this[_scroller].resetSize({
|
||||
width: this[_opts].width,
|
||||
height: this[_opts].height,
|
||||
devicePixelRatio: this[_opts].devicePixelRatio
|
||||
resetSize(opts: BoardSizeOptions) {
|
||||
this._opts = { ...this._opts, ...opts };
|
||||
this._resetContext();
|
||||
this._ctx.resetSize(opts);
|
||||
this._helperCtx.resetSize(opts);
|
||||
this._screen.resetSize(opts);
|
||||
this._scroller.resetSize({
|
||||
width: this._opts.width,
|
||||
height: this._opts.height,
|
||||
devicePixelRatio: this._opts.devicePixelRatio
|
||||
});
|
||||
this.draw();
|
||||
}
|
||||
|
||||
getScrollLineWidth(): number {
|
||||
let lineWidth = 0;
|
||||
if (this[_opts].canScroll === true) {
|
||||
lineWidth = this[_scroller].getLineWidth();
|
||||
if (this._opts.canScroll === true) {
|
||||
lineWidth = this._scroller.getLineWidth();
|
||||
}
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
pointScreenToContext(screenPoint: TypePoint): TypePoint {
|
||||
pointScreenToContext(screenPoint: Point): Point {
|
||||
const { scrollX, scrollY, scale } = this.getTransform();
|
||||
const ctxPoint = {
|
||||
x: (screenPoint.x - scrollX) / scale,
|
||||
|
|
@ -280,7 +259,7 @@ export default class Board {
|
|||
return ctxPoint;
|
||||
}
|
||||
|
||||
pointContextToScreen(ctxPoint: TypePoint): TypePoint {
|
||||
pointContextToScreen(ctxPoint: Point): Point {
|
||||
const { scrollX, scrollY, scale } = this.getTransform();
|
||||
const screenPoint = {
|
||||
x: ctxPoint.x * scale + scrollX,
|
||||
|
|
@ -289,65 +268,65 @@ export default class Board {
|
|||
return screenPoint;
|
||||
}
|
||||
|
||||
private [_render]() {
|
||||
if (this[_hasRendered] === true) {
|
||||
private _render() {
|
||||
if (this._hasRendered === true) {
|
||||
return;
|
||||
}
|
||||
this[_resetContext]();
|
||||
this[_initEvent]();
|
||||
this[_hasRendered] = true;
|
||||
this._resetContext();
|
||||
this._initEvent();
|
||||
this._hasRendered = true;
|
||||
}
|
||||
|
||||
private [_resetContext]() {
|
||||
private _resetContext() {
|
||||
const { width, height, contextWidth, contextHeight, devicePixelRatio } =
|
||||
this[_opts];
|
||||
this[_canvas].width = contextWidth * devicePixelRatio;
|
||||
this[_canvas].height = contextHeight * devicePixelRatio;
|
||||
this._opts;
|
||||
this._canvas.width = contextWidth * devicePixelRatio;
|
||||
this._canvas.height = contextHeight * devicePixelRatio;
|
||||
|
||||
this[_helperCanvas].width = contextWidth * devicePixelRatio;
|
||||
this[_helperCanvas].height = contextHeight * devicePixelRatio;
|
||||
this._helperCanvas.width = contextWidth * devicePixelRatio;
|
||||
this._helperCanvas.height = contextHeight * devicePixelRatio;
|
||||
|
||||
this[_displayCanvas].width = width * devicePixelRatio;
|
||||
this[_displayCanvas].height = height * devicePixelRatio;
|
||||
this._displayCanvas.width = width * devicePixelRatio;
|
||||
this._displayCanvas.height = height * devicePixelRatio;
|
||||
|
||||
setStyle(this[_displayCanvas], {
|
||||
setStyle(this._displayCanvas, {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`
|
||||
});
|
||||
}
|
||||
|
||||
private [_parsePrivateOptions](opts: TypeBoardOptions): PrivateOptions {
|
||||
private _parsePrivateOptions(opts: BoardOptions): PrivateOptions {
|
||||
const defaultOpts = {
|
||||
devicePixelRatio: 1
|
||||
};
|
||||
return { ...defaultOpts, ...opts };
|
||||
}
|
||||
|
||||
private [_initEvent]() {
|
||||
if (this[_hasRendered] === true) {
|
||||
private _initEvent() {
|
||||
if (this._hasRendered === true) {
|
||||
return;
|
||||
}
|
||||
if (this[_opts].canScroll === true) {
|
||||
if (this._opts.canScroll === true) {
|
||||
this.on(
|
||||
'wheelX',
|
||||
throttle((deltaX) => {
|
||||
this[_doScrollX](deltaX);
|
||||
this._doScrollX(deltaX);
|
||||
}, 16)
|
||||
);
|
||||
this.on(
|
||||
'wheelY',
|
||||
throttle((deltaY: number) => {
|
||||
this[_doScrollY](deltaY);
|
||||
this._doScrollY(deltaY);
|
||||
}, 16)
|
||||
);
|
||||
|
||||
let scrollType: 'x' | 'y' | null = null;
|
||||
this.on(
|
||||
'moveStart',
|
||||
throttle((p: TypePoint) => {
|
||||
if (this[_scroller].isPointAtScrollX(p)) {
|
||||
throttle((p: Point) => {
|
||||
if (this._scroller.isPointAtScrollX(p)) {
|
||||
scrollType = 'x';
|
||||
} else if (this[_scroller].isPointAtScrollY(p)) {
|
||||
} else if (this._scroller.isPointAtScrollY(p)) {
|
||||
scrollType = 'y';
|
||||
}
|
||||
}, 16)
|
||||
|
|
@ -355,36 +334,36 @@ export default class Board {
|
|||
|
||||
this.on(
|
||||
'move',
|
||||
throttle((p: TypePoint) => {
|
||||
throttle((p: Point) => {
|
||||
if (scrollType) {
|
||||
this[_doMoveScroll](scrollType, p);
|
||||
this._doMoveScroll(scrollType, p);
|
||||
}
|
||||
}, 16)
|
||||
);
|
||||
|
||||
this.on(
|
||||
'moveEnd',
|
||||
throttle((p: TypePoint) => {
|
||||
throttle((p: Point) => {
|
||||
if (scrollType) {
|
||||
this[_doMoveScroll](scrollType, p);
|
||||
this._doMoveScroll(scrollType, p);
|
||||
}
|
||||
scrollType = null;
|
||||
}, 16)
|
||||
);
|
||||
|
||||
// this.on('doubleClick', (p: TypePoint) => {})
|
||||
// this.on('doubleClick', (p: Point) => {})
|
||||
}
|
||||
}
|
||||
|
||||
private [_doScrollX](dx: number, prevScrollX?: number) {
|
||||
const { width } = this[_opts];
|
||||
private _doScrollX(dx: number, prevScrollX?: number) {
|
||||
const { width } = this._opts;
|
||||
let scrollX = prevScrollX;
|
||||
if (!(typeof scrollX === 'number' && (scrollX > 0 || scrollX <= 0))) {
|
||||
scrollX = this[_ctx].getTransform().scrollX;
|
||||
scrollX = this._ctx.getTransform().scrollX;
|
||||
}
|
||||
const { position } = this[_screen].calcScreen();
|
||||
const { xSize } = this[_scroller].calc(position);
|
||||
const moveX = this[_screen].calcScreenScroll(
|
||||
const { position } = this._screen.calcScreen();
|
||||
const { xSize } = this._scroller.calc(position);
|
||||
const moveX = this._screen.calcScreenScroll(
|
||||
position.left,
|
||||
position.right,
|
||||
xSize,
|
||||
|
|
@ -395,15 +374,15 @@ export default class Board {
|
|||
this.draw();
|
||||
}
|
||||
|
||||
private [_doScrollY](dy: number, prevScrollY?: number) {
|
||||
const { height } = this[_opts];
|
||||
private _doScrollY(dy: number, prevScrollY?: number) {
|
||||
const { height } = this._opts;
|
||||
let scrollY = prevScrollY;
|
||||
if (!(typeof scrollY === 'number' && (scrollY > 0 || scrollY <= 0))) {
|
||||
scrollY = this[_ctx].getTransform().scrollY;
|
||||
scrollY = this._ctx.getTransform().scrollY;
|
||||
}
|
||||
const { position } = this[_screen].calcScreen();
|
||||
const { ySize } = this[_scroller].calc(position);
|
||||
const moveY = this[_screen].calcScreenScroll(
|
||||
const { position } = this._screen.calcScreen();
|
||||
const { ySize } = this._scroller.calc(position);
|
||||
const moveY = this._screen.calcScreenScroll(
|
||||
position.top,
|
||||
position.bottom,
|
||||
ySize,
|
||||
|
|
@ -414,16 +393,16 @@ export default class Board {
|
|||
this.draw();
|
||||
}
|
||||
|
||||
private [_doMoveScroll](scrollType: 'x' | 'y', point: TypePoint) {
|
||||
private _doMoveScroll(scrollType: 'x' | 'y', point: Point) {
|
||||
if (!scrollType) {
|
||||
return;
|
||||
}
|
||||
const { position } = this[_screen].calcScreen();
|
||||
const { xSize, ySize } = this[_scroller].calc(position);
|
||||
const { position } = this._screen.calcScreen();
|
||||
const { xSize, ySize } = this._scroller.calc(position);
|
||||
if (scrollType === 'x') {
|
||||
this[_doScrollX](point.x - xSize / 2, 0);
|
||||
this._doScrollX(point.x - xSize / 2, 0);
|
||||
} else if (scrollType === 'y') {
|
||||
this[_doScrollY](point.y - ySize / 2, 0);
|
||||
this._doScrollY(point.y - ySize / 2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,43 @@
|
|||
import { TypePoint } from '@idraw/types';
|
||||
import { Point } from '@idraw/types';
|
||||
|
||||
export interface TypeBoardEventArgMap {
|
||||
'doubleClick': TypePoint;
|
||||
'hover': TypePoint;
|
||||
'leave': void;
|
||||
'point': TypePoint;
|
||||
'move': TypePoint;
|
||||
'moveStart': TypePoint;
|
||||
'moveEnd': TypePoint;
|
||||
'wheelX': number;
|
||||
'wheelY': number;
|
||||
doubleClick: Point;
|
||||
hover: Point;
|
||||
leave: void;
|
||||
point: Point;
|
||||
move: Point;
|
||||
moveStart: Point;
|
||||
moveEnd: Point;
|
||||
wheelX: number;
|
||||
wheelY: number;
|
||||
}
|
||||
|
||||
export interface TypeBoardEvent {
|
||||
on<T extends keyof TypeBoardEventArgMap >(key: T, callback: (p: TypeBoardEventArgMap[T]) => void): void
|
||||
off<T extends keyof TypeBoardEventArgMap >(key: T, callback: (p: TypeBoardEventArgMap[T]) => void): void
|
||||
trigger<T extends keyof TypeBoardEventArgMap >(key: T, p: TypeBoardEventArgMap[T]): void
|
||||
on<T extends keyof TypeBoardEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeBoardEventArgMap[T]) => void
|
||||
): void;
|
||||
off<T extends keyof TypeBoardEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeBoardEventArgMap[T]) => void
|
||||
): void;
|
||||
trigger<T extends keyof TypeBoardEventArgMap>(
|
||||
key: T,
|
||||
p: TypeBoardEventArgMap[T]
|
||||
): void;
|
||||
}
|
||||
|
||||
|
||||
export class BoardEvent implements TypeBoardEvent {
|
||||
|
||||
private _listeners: Map<string, ((p: any) => void)[]>;
|
||||
|
||||
constructor() {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
|
||||
on<T extends keyof TypeBoardEventArgMap >(eventKey: T, callback: (p: TypeBoardEventArgMap[T]) => void) {
|
||||
on<T extends keyof TypeBoardEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeBoardEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
callbacks?.push(callback);
|
||||
|
|
@ -36,8 +46,11 @@ export class BoardEvent implements TypeBoardEvent {
|
|||
this._listeners.set(eventKey, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
off<T extends keyof TypeBoardEventArgMap >(eventKey: T, callback: (p: TypeBoardEventArgMap[T]) => void) {
|
||||
|
||||
off<T extends keyof TypeBoardEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeBoardEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
|
|
@ -52,7 +65,10 @@ export class BoardEvent implements TypeBoardEvent {
|
|||
}
|
||||
}
|
||||
|
||||
trigger<T extends keyof TypeBoardEventArgMap >(eventKey: T, arg: TypeBoardEventArgMap[T]) {
|
||||
trigger<T extends keyof TypeBoardEventArgMap>(
|
||||
eventKey: T,
|
||||
arg: TypeBoardEventArgMap[T]
|
||||
) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
callbacks.forEach((cb) => {
|
||||
|
|
@ -64,14 +80,14 @@ export class BoardEvent implements TypeBoardEvent {
|
|||
}
|
||||
}
|
||||
|
||||
has<T extends keyof TypeBoardEventArgMap> (name: string) {
|
||||
has<T extends keyof TypeBoardEventArgMap>(name: string) {
|
||||
if (this._listeners.has(name)) {
|
||||
const list: ((p: TypeBoardEventArgMap[T]) => void)[] | undefined = this._listeners.get(name);
|
||||
const list: ((p: TypeBoardEventArgMap[T]) => void)[] | undefined =
|
||||
this._listeners.get(name);
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { TypePoint, TypeContext } from '@idraw/types';
|
||||
import { Point, IDrawContext } from '@idraw/types';
|
||||
import { BoardEvent, TypeBoardEventArgMap } from './event';
|
||||
import { TempData } from './watcher-temp';
|
||||
|
||||
|
|
@ -16,9 +16,9 @@ export class ScreenWatcher {
|
|||
private _event: BoardEvent;
|
||||
private _temp: TempData = new TempData();
|
||||
private _container: HTMLElement | Window = window;
|
||||
// private _ctx: TypeContext;
|
||||
// private _ctx: IDrawContext;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement, ctx: TypeContext) {
|
||||
constructor(canvas: HTMLCanvasElement, ctx: IDrawContext) {
|
||||
this._canvas = canvas;
|
||||
this._isMoving = false;
|
||||
this._initEvent();
|
||||
|
|
@ -328,7 +328,7 @@ export class ScreenWatcher {
|
|||
}
|
||||
}
|
||||
|
||||
_getPosition(e: MouseEvent | TouchEvent): TypePoint {
|
||||
_getPosition(e: MouseEvent | TouchEvent): Point {
|
||||
const canvas = this._canvas;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
|
@ -356,7 +356,7 @@ export class ScreenWatcher {
|
|||
return p;
|
||||
}
|
||||
|
||||
private _isVaildPoint(p: TypePoint): boolean {
|
||||
private _isVaildPoint(p: Point): boolean {
|
||||
return isAvailableNum(p.x) && isAvailableNum(p.y);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import {
|
||||
TypeBoardSizeOptions, TypeScreenPosition,
|
||||
TypeContext, TypeScreenSize,
|
||||
BoardSizeOptions,
|
||||
ScreenPosition,
|
||||
IDrawContext,
|
||||
ScreenSize
|
||||
} from '@idraw/types';
|
||||
|
||||
type Options = {
|
||||
|
|
@ -9,37 +11,43 @@ type Options = {
|
|||
contextWidth: number;
|
||||
contextHeight: number;
|
||||
devicePixelRatio: number;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const _opts = Symbol('_opts');
|
||||
const _ctx = Symbol('_ctx');
|
||||
|
||||
export class Screen {
|
||||
private [_opts]: Options;
|
||||
private [_ctx]: TypeContext;
|
||||
|
||||
constructor(ctx: TypeContext, opts: Options) {
|
||||
private [_ctx]: IDrawContext;
|
||||
|
||||
constructor(ctx: IDrawContext, opts: Options) {
|
||||
this[_opts] = opts;
|
||||
this[_ctx] = ctx;
|
||||
}
|
||||
|
||||
resetSize(opts: TypeBoardSizeOptions) {
|
||||
this[_opts] = {...this[_opts], ...opts};
|
||||
resetSize(opts: BoardSizeOptions) {
|
||||
this[_opts] = { ...this[_opts], ...opts };
|
||||
}
|
||||
|
||||
calcScreen(): {
|
||||
size: TypeScreenSize, position: TypeScreenPosition, deviceSize: TypeScreenSize,
|
||||
width: number, height: number, devicePixelRatio: number,
|
||||
canScrollXPrev: boolean,
|
||||
canScrollXNext: boolean,
|
||||
canScrollYPrev: boolean,
|
||||
canScrollYNext: boolean,
|
||||
size: ScreenSize;
|
||||
position: ScreenPosition;
|
||||
deviceSize: ScreenSize;
|
||||
width: number;
|
||||
height: number;
|
||||
devicePixelRatio: number;
|
||||
canScrollXPrev: boolean;
|
||||
canScrollXNext: boolean;
|
||||
canScrollYPrev: boolean;
|
||||
canScrollYNext: boolean;
|
||||
} {
|
||||
const scaleRatio = this[_ctx].getTransform().scale;
|
||||
const {
|
||||
width, height, contextWidth, contextHeight,
|
||||
devicePixelRatio: pxRatio,
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
contextWidth,
|
||||
contextHeight,
|
||||
devicePixelRatio: pxRatio
|
||||
} = this[_opts];
|
||||
|
||||
let canScrollXPrev: boolean = true;
|
||||
|
|
@ -51,7 +59,7 @@ export class Screen {
|
|||
if (contextWidth * scaleRatio <= width) {
|
||||
// make context center
|
||||
this[_ctx].setTransform({
|
||||
scrollX: (width - contextWidth * scaleRatio) / 2,
|
||||
scrollX: (width - contextWidth * scaleRatio) / 2
|
||||
});
|
||||
canScrollXPrev = false;
|
||||
canScrollXNext = false;
|
||||
|
|
@ -60,21 +68,27 @@ export class Screen {
|
|||
if (contextHeight * scaleRatio <= height) {
|
||||
// make context center
|
||||
this[_ctx].setTransform({
|
||||
scrollY: (height - contextHeight * scaleRatio) / 2,
|
||||
scrollY: (height - contextHeight * scaleRatio) / 2
|
||||
});
|
||||
canScrollYPrev = false;
|
||||
canScrollYNext = false;
|
||||
}
|
||||
|
||||
if (contextWidth * scaleRatio >= width && this[_ctx].getTransform().scrollX > 0) {
|
||||
if (
|
||||
contextWidth * scaleRatio >= width &&
|
||||
this[_ctx].getTransform().scrollX > 0
|
||||
) {
|
||||
this[_ctx].setTransform({
|
||||
scrollX: 0,
|
||||
scrollX: 0
|
||||
});
|
||||
canScrollXPrev = false;
|
||||
}
|
||||
if (contextHeight * scaleRatio >= height && this[_ctx].getTransform().scrollY > 0) {
|
||||
if (
|
||||
contextHeight * scaleRatio >= height &&
|
||||
this[_ctx].getTransform().scrollY > 0
|
||||
) {
|
||||
this[_ctx].setTransform({
|
||||
scrollY: 0,
|
||||
scrollY: 0
|
||||
});
|
||||
canScrollYPrev = false;
|
||||
}
|
||||
|
|
@ -82,13 +96,19 @@ export class Screen {
|
|||
const { scrollX: _scrollX, scrollY: _scrollY } = this[_ctx].getTransform();
|
||||
|
||||
// reset scroll
|
||||
if (_scrollX < 0 && Math.abs(_scrollX) > Math.abs(contextWidth * scaleRatio - width)) {
|
||||
if (
|
||||
_scrollX < 0 &&
|
||||
Math.abs(_scrollX) > Math.abs(contextWidth * scaleRatio - width)
|
||||
) {
|
||||
this[_ctx].setTransform({
|
||||
scrollX: 0 - Math.abs(contextWidth * scaleRatio - width)
|
||||
});
|
||||
canScrollXNext = false;
|
||||
}
|
||||
if (_scrollY < 0 && Math.abs(_scrollY) > Math.abs(contextHeight * scaleRatio - height)) {
|
||||
if (
|
||||
_scrollY < 0 &&
|
||||
Math.abs(_scrollY) > Math.abs(contextHeight * scaleRatio - height)
|
||||
) {
|
||||
this[_ctx].setTransform({
|
||||
scrollY: 0 - Math.abs(contextHeight * scaleRatio - height)
|
||||
});
|
||||
|
|
@ -101,19 +121,19 @@ export class Screen {
|
|||
x: scrollX * scaleRatio,
|
||||
y: scrollY * scaleRatio,
|
||||
w: contextWidth * scaleRatio,
|
||||
h: contextHeight * scaleRatio,
|
||||
h: contextHeight * scaleRatio
|
||||
};
|
||||
const deviceSize = {
|
||||
x: scrollX * pxRatio,
|
||||
y: scrollY * pxRatio,
|
||||
w: contextWidth * pxRatio * scaleRatio,
|
||||
h: contextHeight * pxRatio * scaleRatio,
|
||||
h: contextHeight * pxRatio * scaleRatio
|
||||
};
|
||||
const position = {
|
||||
top: scrollY,
|
||||
bottom: height - (contextHeight * scaleRatio + scrollY),
|
||||
left: scrollX,
|
||||
right: width - (contextWidth * scaleRatio + scrollX),
|
||||
right: width - (contextWidth * scaleRatio + scrollX)
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
@ -126,11 +146,16 @@ export class Screen {
|
|||
canScrollYPrev,
|
||||
canScrollYNext,
|
||||
canScrollXPrev,
|
||||
canScrollXNext,
|
||||
canScrollXNext
|
||||
};
|
||||
}
|
||||
|
||||
calcScreenScroll( start: number, end: number, sliderSize: number, limitLen: number, moveDistance: number
|
||||
calcScreenScroll(
|
||||
start: number,
|
||||
end: number,
|
||||
sliderSize: number,
|
||||
limitLen: number,
|
||||
moveDistance: number
|
||||
): number {
|
||||
let scrollDistance = start;
|
||||
let scrollLen = limitLen - sliderSize;
|
||||
|
|
@ -141,8 +166,7 @@ export class Screen {
|
|||
if (scrollLen > 0) {
|
||||
unit = scrollLen / (limitLen - sliderSize);
|
||||
}
|
||||
scrollDistance = 0 - unit * moveDistance;
|
||||
scrollDistance = 0 - unit * moveDistance;
|
||||
return scrollDistance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,22 @@
|
|||
import {
|
||||
TypePoint,
|
||||
TypeScreenPosition,
|
||||
TypeBoardScrollConfig
|
||||
} from '@idraw/types';
|
||||
import { Point, ScreenPosition, BoardScrollConfig } from '@idraw/types';
|
||||
import { isColorStr } from '@idraw/util';
|
||||
|
||||
type TypeOptions = {
|
||||
width: number;
|
||||
height: number;
|
||||
devicePixelRatio: number;
|
||||
scrollConfig?: TypeBoardScrollConfig;
|
||||
scrollConfig?: BoardScrollConfig;
|
||||
};
|
||||
|
||||
type TypePrivateOptions = Required<
|
||||
TypeOptions & { scrollConfig: Required<TypeBoardScrollConfig> }
|
||||
TypeOptions & { scrollConfig: Required<BoardScrollConfig> }
|
||||
>;
|
||||
|
||||
const minScrollerWidth = 12;
|
||||
const scrollerAlpha = 0.12;
|
||||
const scrollerThumbAlpha = 0.36;
|
||||
|
||||
const defaultScrollConfig: Partial<TypeBoardScrollConfig> & {
|
||||
const defaultScrollConfig: Partial<BoardScrollConfig> & {
|
||||
width: number;
|
||||
color: string;
|
||||
} = {
|
||||
|
|
@ -38,7 +34,7 @@ export class Scroller {
|
|||
this._opts = this._getOpts(opts);
|
||||
}
|
||||
|
||||
draw(position: TypeScreenPosition) {
|
||||
draw(position: ScreenPosition) {
|
||||
const { width, height, scrollConfig } = this._opts;
|
||||
const wrapper = this.calc(position);
|
||||
const ctx = this._displayCtx;
|
||||
|
|
@ -102,7 +98,7 @@ export class Scroller {
|
|||
this._opts = { ...this._opts, ...opts };
|
||||
}
|
||||
|
||||
isPointAtScrollY(p: TypePoint): boolean {
|
||||
isPointAtScrollY(p: Point): boolean {
|
||||
const { width, height, scrollConfig } = this._opts;
|
||||
const ctx = this._displayCtx;
|
||||
ctx.beginPath();
|
||||
|
|
@ -119,7 +115,7 @@ export class Scroller {
|
|||
return false;
|
||||
}
|
||||
|
||||
isPointAtScrollX(p: TypePoint): boolean {
|
||||
isPointAtScrollX(p: Point): boolean {
|
||||
const { width, height, scrollConfig } = this._opts;
|
||||
const ctx = this._displayCtx;
|
||||
ctx.beginPath();
|
||||
|
|
@ -141,7 +137,7 @@ export class Scroller {
|
|||
return lineWidth;
|
||||
}
|
||||
|
||||
calc(position: TypeScreenPosition) {
|
||||
calc(position: ScreenPosition) {
|
||||
const { width, height, scrollConfig } = this._opts;
|
||||
const sliderMinSize = scrollConfig.width * 2.5;
|
||||
const lineSize = scrollConfig.width;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { TypeBoardOptions, TypeContext } from '@idraw/types';
|
||||
import { BoardOptions, IDrawContext } from '@idraw/types';
|
||||
import { Context } from '@idraw/util';
|
||||
|
||||
type TempDataDesc = {
|
||||
ctx: TypeContext,
|
||||
}
|
||||
ctx: IDrawContext;
|
||||
};
|
||||
|
||||
function createDefaultData(opts: TypeBoardOptions) {
|
||||
function createDefaultData(opts: BoardOptions) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx2d = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context(ctx2d, {
|
||||
|
|
@ -13,34 +13,31 @@ function createDefaultData(opts: TypeBoardOptions) {
|
|||
height: opts.height,
|
||||
contextWidth: opts.contextWidth,
|
||||
contextHeight: opts.contextHeight,
|
||||
devicePixelRatio: opts.devicePixelRatio || window.devicePixelRatio || 1,
|
||||
devicePixelRatio: opts.devicePixelRatio || window.devicePixelRatio || 1
|
||||
});
|
||||
|
||||
return {
|
||||
plugins: [],
|
||||
ctx: ctx
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class TempData {
|
||||
private _temp: TempDataDesc;
|
||||
|
||||
private _temp: TempDataDesc
|
||||
|
||||
constructor(opts: TypeBoardOptions) {
|
||||
this._temp = createDefaultData(opts)
|
||||
constructor(opts: BoardOptions) {
|
||||
this._temp = createDefaultData(opts);
|
||||
}
|
||||
|
||||
set<T extends keyof TempDataDesc >(name: T, value: TempDataDesc[T]) {
|
||||
set<T extends keyof TempDataDesc>(name: T, value: TempDataDesc[T]) {
|
||||
this._temp[name] = value;
|
||||
}
|
||||
|
||||
get<T extends keyof TempDataDesc >(name: T): TempDataDesc[T] {
|
||||
get<T extends keyof TempDataDesc>(name: T): TempDataDesc[T] {
|
||||
return this._temp[name];
|
||||
}
|
||||
|
||||
clear(opts: TypeBoardOptions) {
|
||||
this._temp = createDefaultData(opts)
|
||||
clear(opts: BoardOptions) {
|
||||
this._temp = createDefaultData(opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { TypePoint } from '@idraw/types'
|
||||
import { Point } from '@idraw/types';
|
||||
|
||||
type TempDataDesc = {
|
||||
prevClickPoint: TypePoint & { t: number } | null,
|
||||
prevClickPoint: (Point & { t: number }) | null;
|
||||
isHoverCanvas: boolean;
|
||||
isDragCanvas: boolean;
|
||||
statusMap: {
|
||||
canScrollYPrev: boolean,
|
||||
canScrollYNext: boolean,
|
||||
canScrollXPrev: boolean,
|
||||
canScrollXNext: boolean,
|
||||
}
|
||||
}
|
||||
canScrollYPrev: boolean;
|
||||
canScrollYNext: boolean;
|
||||
canScrollXPrev: boolean;
|
||||
canScrollXNext: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
function createTempData() {
|
||||
return {
|
||||
|
|
@ -21,28 +21,27 @@ function createTempData() {
|
|||
canScrollYPrev: true,
|
||||
canScrollYNext: true,
|
||||
canScrollXPrev: true,
|
||||
canScrollXNext: true,
|
||||
canScrollXNext: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export class TempData {
|
||||
|
||||
private _temp: TempDataDesc
|
||||
private _temp: TempDataDesc;
|
||||
|
||||
constructor() {
|
||||
this._temp = createTempData();
|
||||
}
|
||||
|
||||
set<T extends keyof TempDataDesc >(name: T, value: TempDataDesc[T]) {
|
||||
set<T extends keyof TempDataDesc>(name: T, value: TempDataDesc[T]) {
|
||||
this._temp[name] = value;
|
||||
}
|
||||
|
||||
get<T extends keyof TempDataDesc >(name: T): TempDataDesc[T] {
|
||||
get<T extends keyof TempDataDesc>(name: T): TempDataDesc[T] {
|
||||
return this._temp[name];
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._temp = createTempData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { TypePoint } from '@idraw/types';
|
||||
import { Point } from '@idraw/types';
|
||||
import { BoardEvent, TypeBoardEventArgMap } from './event';
|
||||
import { TempData } from './watcher-temp';
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ export class Watcher {
|
|||
}
|
||||
}
|
||||
|
||||
_getPosition(e: MouseEvent | TouchEvent): TypePoint {
|
||||
_getPosition(e: MouseEvent | TouchEvent): Point {
|
||||
const canvas = this._canvas;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
|
@ -171,7 +171,7 @@ export class Watcher {
|
|||
return p;
|
||||
}
|
||||
|
||||
private _isVaildPoint(p: TypePoint): boolean {
|
||||
private _isVaildPoint(p: Point): boolean {
|
||||
return p.x > 0 && p.y > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
const _canvas = Symbol('_canvas');
|
||||
const _displayCanvas = Symbol('_displayCanvas');
|
||||
const _helperCanvas = Symbol('_helperCanvas');
|
||||
const _mount = Symbol('_mount');
|
||||
const _opts = Symbol('_opts');
|
||||
const _hasRendered = Symbol('_hasRendered');
|
||||
const _ctx = Symbol('_ctx');
|
||||
const _helperCtx = Symbol('_helperCtx');
|
||||
|
||||
const _watcher = Symbol('_watcher');
|
||||
const _render = Symbol('_render');
|
||||
const _parsePrivateOptions = Symbol('_parsePrivateOptions');
|
||||
const _scroller = Symbol('_scroller');
|
||||
const _initEvent = Symbol('_initEvent');
|
||||
const _doScrollX = Symbol('_doScrollX');
|
||||
const _doScrollY = Symbol('_doScrollY');
|
||||
const _doMoveScroll = Symbol('_doMoveScroll');
|
||||
// const _doDoubleClick = Symbol('_doDoubleClick');
|
||||
const _resetContext = Symbol('_resetContext');
|
||||
const _screen = Symbol('_screen');
|
||||
const _tempData = Symbol('_tempData');
|
||||
|
||||
export {
|
||||
_canvas, _displayCanvas, _mount, _opts, _hasRendered, _ctx,
|
||||
_watcher, _render, _parsePrivateOptions, _scroller,
|
||||
_initEvent, _doScrollX, _doScrollY, _doMoveScroll, _resetContext,
|
||||
_screen, _tempData, _helperCtx, _helperCanvas,
|
||||
};
|
||||
|
|
@ -28,12 +28,12 @@ describe('@idraw/core', () => {
|
|||
|
||||
requestAnimationFrameMock.triggerNextAnimationFrame();
|
||||
|
||||
const originCtx = core.__getOriginContext2D();
|
||||
const originCtx = core.$getOriginContext2D();
|
||||
// @ts-ignore;
|
||||
const originCalls = originCtx.__getDrawCalls();
|
||||
expect(originCalls).toMatchSnapshot();
|
||||
|
||||
const displayCtx = core.__getDisplayContext2D();
|
||||
const displayCtx = core.$getDisplayContext2D();
|
||||
// @ts-ignore;
|
||||
const displayCalls = displayCtx.__getDrawCalls();
|
||||
expect(displayCalls).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ describe('@idraw/core: lib/element', () => {
|
|||
};
|
||||
const mount = document.querySelector('#mount') as HTMLDivElement;
|
||||
const core = new Core(mount, opts);
|
||||
const ctx = core.__getBoardContext();
|
||||
const ctx = core.$getBoardContext();
|
||||
const data = getData();
|
||||
|
||||
test('initData', async () => {
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ describe('@idraw/core', () => {
|
|||
|
||||
requestAnimationFrameMock.triggerNextAnimationFrame();
|
||||
|
||||
const originCtx = idraw.__getOriginContext2D();
|
||||
const originCtx = idraw.$getOriginContext2D();
|
||||
// @ts-ignore;
|
||||
const originCalls = originCtx.__getDrawCalls();
|
||||
expect(originCalls).toMatchSnapshot();
|
||||
|
||||
const displayCtx = idraw.__getDisplayContext2D();
|
||||
const displayCtx = idraw.$getDisplayContext2D();
|
||||
// @ts-ignore;
|
||||
const displayCalls = displayCtx.__getDrawCalls();
|
||||
expect(displayCalls).toMatchSnapshot();
|
||||
|
|
@ -67,12 +67,12 @@ describe('@idraw/core', () => {
|
|||
|
||||
requestAnimationFrameMock.triggerNextAnimationFrame();
|
||||
|
||||
const originCtx = idraw.__getOriginContext2D();
|
||||
const originCtx = idraw.$getOriginContext2D();
|
||||
// @ts-ignore;
|
||||
const originCalls = originCtx.__getDrawCalls();
|
||||
expect(originCalls).toMatchSnapshot();
|
||||
|
||||
const displayCtx = idraw.__getDisplayContext2D();
|
||||
const displayCtx = idraw.$getDisplayContext2D();
|
||||
// @ts-ignore;
|
||||
const displayCalls = displayCtx.__getDrawCalls();
|
||||
expect(displayCalls).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import {
|
||||
TypeData,
|
||||
TypePoint,
|
||||
TypeBoardSizeOptions,
|
||||
TypeConfig,
|
||||
TypeConfigStrict,
|
||||
TypeElementBase,
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
TypeContext,
|
||||
TypeCoreOptions,
|
||||
TypeScreenContext,
|
||||
TypeScreenData
|
||||
IDrawData,
|
||||
Point,
|
||||
BoardSizeOptions,
|
||||
IDrawConfig,
|
||||
IDrawConfigStrict,
|
||||
DataElementBase,
|
||||
DataElement,
|
||||
DataElemDesc,
|
||||
IDrawContext,
|
||||
CoreOptions,
|
||||
ScreenContext,
|
||||
ScreenData
|
||||
} from '@idraw/types';
|
||||
import Board from '@idraw/board';
|
||||
import { deepClone } from '@idraw/util';
|
||||
import Renderer from '@idraw/renderer';
|
||||
import is, { TypeIs } from './lib/is';
|
||||
import check, { TypeCheck } from './lib/check';
|
||||
import is, { IsTypeUtil } from './lib/is';
|
||||
import check, { CheckTypeUtil } from './lib/check';
|
||||
import {
|
||||
Element,
|
||||
mergeConfig,
|
||||
|
|
@ -26,21 +26,6 @@ import {
|
|||
TempData,
|
||||
diffElementResourceChangeList
|
||||
} from './lib';
|
||||
import {
|
||||
_board,
|
||||
_data,
|
||||
_opts,
|
||||
_config,
|
||||
_renderer,
|
||||
_element,
|
||||
_tempData,
|
||||
_draw,
|
||||
_coreEvent,
|
||||
// _mapper,
|
||||
_emitChangeScreen,
|
||||
_emitChangeData,
|
||||
_engine
|
||||
} from './names';
|
||||
import {
|
||||
getSelectedElements,
|
||||
updateElement,
|
||||
|
|
@ -68,29 +53,25 @@ import {
|
|||
} from './lib/draw/wrapper';
|
||||
|
||||
export default class Core {
|
||||
private [_board]: Board;
|
||||
private [_data]: TypeData;
|
||||
private [_opts]: TypeCoreOptions;
|
||||
private [_config]: TypeConfigStrict;
|
||||
private [_renderer]: Renderer;
|
||||
private [_element]: Element;
|
||||
private [_coreEvent]: CoreEvent = new CoreEvent();
|
||||
private [_tempData]: TempData = new TempData();
|
||||
private [_engine]: Engine;
|
||||
$data: IDrawData;
|
||||
private _board: Board;
|
||||
private _opts: CoreOptions;
|
||||
private _config: IDrawConfigStrict;
|
||||
private _renderer: Renderer;
|
||||
private _elementHandler: Element;
|
||||
private _coreEvent: CoreEvent = new CoreEvent();
|
||||
private _tempData: TempData = new TempData();
|
||||
private _engine: Engine;
|
||||
|
||||
static is: TypeIs = is;
|
||||
static check: TypeCheck = check;
|
||||
static is: IsTypeUtil = is;
|
||||
static check: CheckTypeUtil = check;
|
||||
|
||||
constructor(
|
||||
mount: HTMLDivElement,
|
||||
opts: TypeCoreOptions,
|
||||
config?: TypeConfig
|
||||
) {
|
||||
this[_data] = { elements: [] };
|
||||
this[_opts] = opts;
|
||||
this[_config] = mergeConfig(config || {});
|
||||
this[_board] = new Board(mount, {
|
||||
...this[_opts],
|
||||
constructor(mount: HTMLDivElement, opts: CoreOptions, config?: IDrawConfig) {
|
||||
this.$data = { elements: [] };
|
||||
this._opts = opts;
|
||||
this._config = mergeConfig(config || {});
|
||||
this._board = new Board(mount, {
|
||||
...this._opts,
|
||||
canScroll: config?.scrollWrapper?.use,
|
||||
scrollConfig: {
|
||||
color: config?.scrollWrapper?.color || '#000000',
|
||||
|
|
@ -98,12 +79,12 @@ export default class Core {
|
|||
...(config?.scrollWrapper || {})
|
||||
}
|
||||
});
|
||||
this[_renderer] = new Renderer();
|
||||
this._renderer = new Renderer();
|
||||
const drawFrame = () => {
|
||||
const helperCtx = this[_board].getHelperContext();
|
||||
const helperConfig = this[_engine].getHelperConfig();
|
||||
this[_board].clear();
|
||||
const { contextWidth, contextHeight, devicePixelRatio } = this[_opts];
|
||||
const helperCtx = this._board.getHelperContext();
|
||||
const helperConfig = this._engine.getHelperConfig();
|
||||
this._board.clear();
|
||||
const { contextWidth, contextHeight, devicePixelRatio } = this._opts;
|
||||
helperCtx.clearRect(
|
||||
0,
|
||||
0,
|
||||
|
|
@ -113,47 +94,55 @@ export default class Core {
|
|||
drawElementWrapper(helperCtx, helperConfig);
|
||||
drawAreaWrapper(helperCtx, helperConfig);
|
||||
drawElementListWrappers(helperCtx, helperConfig);
|
||||
this[_board].draw();
|
||||
this._board.draw();
|
||||
};
|
||||
this[_renderer].on('drawFrame', () => {
|
||||
this._renderer.on('drawFrame', () => {
|
||||
drawFrame();
|
||||
});
|
||||
this[_renderer].on('drawFrameComplete', () => {
|
||||
this._renderer.on('drawFrameComplete', () => {
|
||||
drawFrame();
|
||||
});
|
||||
this[_element] = new Element(this[_board].getContext());
|
||||
this[_engine] = new Engine({
|
||||
coreEvent: this[_coreEvent],
|
||||
board: this[_board],
|
||||
element: this[_element],
|
||||
config: this[_config],
|
||||
drawFeekback: this[_draw].bind(this),
|
||||
getDataFeekback: () => this[_data],
|
||||
this._elementHandler = new Element(this._board.getContext());
|
||||
this._engine = new Engine({
|
||||
coreEvent: this._coreEvent,
|
||||
board: this._board,
|
||||
element: this._elementHandler,
|
||||
config: this._config,
|
||||
drawFeekback: this.$draw.bind(this),
|
||||
getDataFeekback: () => this.$data,
|
||||
selectElementByIndex: this.selectElementByIndex.bind(this),
|
||||
emitChangeScreen: this[_emitChangeScreen].bind(this),
|
||||
emitChangeData: this[_emitChangeData].bind(this)
|
||||
emitChangeScreen: this._emitChangeScreen.bind(this),
|
||||
emitChangeData: this.$emitChangeData.bind(this)
|
||||
});
|
||||
this[_engine].init();
|
||||
this._engine.init();
|
||||
|
||||
this[_renderer].on('drawFrame', () => {
|
||||
this[_coreEvent].trigger('drawFrame', undefined);
|
||||
this._renderer.on('drawFrame', () => {
|
||||
this._coreEvent.trigger('drawFrame', undefined);
|
||||
});
|
||||
this[_renderer].on('drawFrameComplete', () => {
|
||||
this[_coreEvent].trigger('drawFrameComplete', undefined);
|
||||
this._renderer.on('drawFrameComplete', () => {
|
||||
this._coreEvent.trigger('drawFrameComplete', undefined);
|
||||
});
|
||||
|
||||
this[_tempData].set('hasInited', true);
|
||||
this._tempData.set('hasInited', true);
|
||||
}
|
||||
|
||||
[_draw](opts?: { resourceChangeUUIDs?: string[] }): void {
|
||||
this[_engine].updateHelperConfig({
|
||||
width: this[_opts].width,
|
||||
height: this[_opts].height,
|
||||
devicePixelRatio: this[_opts].devicePixelRatio
|
||||
private _emitChangeScreen() {
|
||||
if (this._coreEvent.has('changeScreen')) {
|
||||
this._coreEvent.trigger('changeScreen', {
|
||||
...this.getScreenTransform()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$draw(opts?: { resourceChangeUUIDs?: string[] }): void {
|
||||
this._engine.updateHelperConfig({
|
||||
width: this._opts.width,
|
||||
height: this._opts.height,
|
||||
devicePixelRatio: this._opts.devicePixelRatio
|
||||
});
|
||||
|
||||
this[_renderer].thaw();
|
||||
this[_renderer].render(this[_board].getContext(), this[_data], {
|
||||
this._renderer.thaw();
|
||||
this._renderer.render(this._board.getContext(), this.$data, {
|
||||
changeResourceUUIDs: opts?.resourceChangeUUIDs || []
|
||||
});
|
||||
}
|
||||
|
|
@ -178,8 +167,8 @@ export default class Core {
|
|||
return cancelElementByIndex(this, index);
|
||||
}
|
||||
|
||||
cancelElement(uuid: string, opts?: { useMode?: boolean }): void {
|
||||
return cancelElement(this, uuid, opts);
|
||||
cancelElement(uuid: string): void {
|
||||
return cancelElement(this, uuid);
|
||||
}
|
||||
|
||||
moveUpElement(uuid: string): void {
|
||||
|
|
@ -190,11 +179,11 @@ export default class Core {
|
|||
return moveDownElement(this, uuid);
|
||||
}
|
||||
|
||||
updateElement(elem: TypeElement<keyof TypeElemDesc>) {
|
||||
updateElement(elem: DataElement<keyof DataElemDesc>) {
|
||||
return updateElement(this, elem);
|
||||
}
|
||||
|
||||
addElement(elem: TypeElementBase<keyof TypeElemDesc>): string | null {
|
||||
addElement(elem: DataElementBase<keyof DataElemDesc>): string | null {
|
||||
return addElement(this, elem);
|
||||
}
|
||||
|
||||
|
|
@ -203,14 +192,14 @@ export default class Core {
|
|||
}
|
||||
|
||||
insertElementBefore(
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
beforeUUID: string
|
||||
) {
|
||||
return insertElementBefore(this, elem, beforeUUID);
|
||||
}
|
||||
|
||||
insertElementBeforeIndex(
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
index: number
|
||||
) {
|
||||
return insertElementBeforeIndex(this, elem, index);
|
||||
|
|
@ -221,48 +210,48 @@ export default class Core {
|
|||
}
|
||||
|
||||
insertElementAfter(
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
beforeUUID: string
|
||||
) {
|
||||
return insertElementAfter(this, elem, beforeUUID);
|
||||
}
|
||||
|
||||
insertElementAfterIndex(
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
index: number
|
||||
) {
|
||||
return insertElementAfterIndex(this, elem, index);
|
||||
}
|
||||
|
||||
resetSize(opts: TypeBoardSizeOptions) {
|
||||
this[_opts] = { ...this[_opts], ...opts };
|
||||
this[_board].resetSize(opts);
|
||||
this[_draw]();
|
||||
resetSize(opts: BoardSizeOptions) {
|
||||
this._opts = { ...this._opts, ...opts };
|
||||
this._board.resetSize(opts);
|
||||
this.$draw();
|
||||
}
|
||||
|
||||
scale(ratio: number): TypeScreenContext {
|
||||
const screen = this[_board].scale(ratio);
|
||||
this[_draw]();
|
||||
this[_emitChangeScreen]();
|
||||
scale(ratio: number): ScreenContext {
|
||||
const screen = this._board.scale(ratio);
|
||||
this.$draw();
|
||||
this._emitChangeScreen();
|
||||
return screen;
|
||||
}
|
||||
|
||||
scrollLeft(left: number): TypeScreenContext {
|
||||
const screen = this[_board].scrollX(0 - left);
|
||||
this[_draw]();
|
||||
this[_emitChangeScreen]();
|
||||
scrollLeft(left: number): ScreenContext {
|
||||
const screen = this._board.scrollX(0 - left);
|
||||
this.$draw();
|
||||
this._emitChangeScreen();
|
||||
return screen;
|
||||
}
|
||||
|
||||
scrollTop(top: number): TypeScreenContext {
|
||||
const screen = this[_board].scrollY(0 - top);
|
||||
this[_draw]();
|
||||
this[_emitChangeScreen]();
|
||||
scrollTop(top: number): ScreenContext {
|
||||
const screen = this._board.scrollY(0 - top);
|
||||
this.$draw();
|
||||
this._emitChangeScreen();
|
||||
return screen;
|
||||
}
|
||||
|
||||
getScreenTransform(): TypeScreenData {
|
||||
const transform = this[_board].getTransform();
|
||||
getScreenTransform(): ScreenData {
|
||||
const transform = this._board.getTransform();
|
||||
return {
|
||||
scale: transform.scale,
|
||||
scrollTop: Math.max(0, 0 - transform.scrollY),
|
||||
|
|
@ -270,72 +259,69 @@ export default class Core {
|
|||
};
|
||||
}
|
||||
|
||||
getData(): TypeData {
|
||||
return deepClone(this[_data]);
|
||||
getData(): IDrawData {
|
||||
return deepClone(this.$data);
|
||||
}
|
||||
|
||||
setData(data: any | TypeData, opts?: { triggerChangeEvent: boolean }): void {
|
||||
const resourceChangeUUIDs = diffElementResourceChangeList(
|
||||
this[_data],
|
||||
data
|
||||
);
|
||||
this[_data] = this[_element].initData(deepClone(parseData(data)));
|
||||
setData(data: any | IDrawData, opts?: { triggerChangeEvent: boolean }): void {
|
||||
const resourceChangeUUIDs = diffElementResourceChangeList(this.$data, data);
|
||||
this.$data = this._elementHandler.initData(deepClone(parseData(data)));
|
||||
if (opts && opts.triggerChangeEvent === true) {
|
||||
this[_emitChangeData]();
|
||||
this.$emitChangeData();
|
||||
}
|
||||
this[_draw]({ resourceChangeUUIDs });
|
||||
this.$draw({ resourceChangeUUIDs });
|
||||
}
|
||||
|
||||
clearOperation() {
|
||||
this[_tempData].clear();
|
||||
this[_draw]();
|
||||
this._tempData.clear();
|
||||
this.$draw();
|
||||
}
|
||||
|
||||
on<T extends keyof TypeCoreEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeCoreEventArgMap[T]) => void
|
||||
) {
|
||||
this[_coreEvent].on(key, callback);
|
||||
this._coreEvent.on(key, callback);
|
||||
}
|
||||
|
||||
off<T extends keyof TypeCoreEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeCoreEventArgMap[T]) => void
|
||||
) {
|
||||
this[_coreEvent].off(key, callback);
|
||||
this._coreEvent.off(key, callback);
|
||||
}
|
||||
|
||||
pointScreenToContext(p: TypePoint) {
|
||||
return this[_board].pointScreenToContext(p);
|
||||
getEngine() {
|
||||
return this._engine;
|
||||
}
|
||||
|
||||
pointContextToScreen(p: TypePoint) {
|
||||
return this[_board].pointContextToScreen(p);
|
||||
pointScreenToContext(p: Point) {
|
||||
return this._board.pointScreenToContext(p);
|
||||
}
|
||||
|
||||
__getBoardContext(): TypeContext {
|
||||
return this[_board].getContext();
|
||||
pointContextToScreen(p: Point) {
|
||||
return this._board.pointContextToScreen(p);
|
||||
}
|
||||
|
||||
__getDisplayContext2D(): CanvasRenderingContext2D {
|
||||
return this[_board].getDisplayContext2D();
|
||||
$getBoardContext(): IDrawContext {
|
||||
return this._board.getContext();
|
||||
}
|
||||
|
||||
__getOriginContext2D(): CanvasRenderingContext2D {
|
||||
return this[_board].getOriginContext2D();
|
||||
$getDisplayContext2D(): CanvasRenderingContext2D {
|
||||
return this._board.getDisplayContext2D();
|
||||
}
|
||||
|
||||
private [_emitChangeScreen]() {
|
||||
if (this[_coreEvent].has('changeScreen')) {
|
||||
this[_coreEvent].trigger('changeScreen', {
|
||||
...this.getScreenTransform()
|
||||
});
|
||||
$getOriginContext2D(): CanvasRenderingContext2D {
|
||||
return this._board.getOriginContext2D();
|
||||
}
|
||||
|
||||
$emitChangeData() {
|
||||
if (this._coreEvent.has('changeData')) {
|
||||
this._coreEvent.trigger('changeData', deepClone(this.$data));
|
||||
}
|
||||
}
|
||||
|
||||
private [_emitChangeData]() {
|
||||
if (this[_coreEvent].has('changeData')) {
|
||||
this[_coreEvent].trigger('changeData', deepClone(this[_data]));
|
||||
}
|
||||
$getElementHandler() {
|
||||
return this._elementHandler;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,30 @@
|
|||
import {
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
TypePoint,
|
||||
} from '@idraw/types';
|
||||
|
||||
import { DataElement, DataElemDesc, Point } from '@idraw/types';
|
||||
|
||||
export function parseRadianToAngle(radian: number): number {
|
||||
return radian / Math.PI * 180;
|
||||
return (radian / Math.PI) * 180;
|
||||
}
|
||||
|
||||
export function parseAngleToRadian(angle: number): number {
|
||||
return angle / 180 * Math.PI;
|
||||
return (angle / 180) * Math.PI;
|
||||
}
|
||||
|
||||
export function calcElementCenter(elem: TypeElement<keyof TypeElemDesc>): TypePoint {
|
||||
export function calcElementCenter(
|
||||
elem: DataElement<keyof DataElemDesc>
|
||||
): Point {
|
||||
const p = {
|
||||
x: elem.x + elem.w / 2,
|
||||
y: elem.y + elem.h / 2,
|
||||
y: elem.y + elem.h / 2
|
||||
};
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
export function calcRadian(center: TypePoint, start: TypePoint, end: TypePoint): number {
|
||||
export function calcRadian(center: Point, start: Point, end: Point): number {
|
||||
const startAngle = calcLineAngle(center, start);
|
||||
const endAngle = calcLineAngle(center, end);
|
||||
if (endAngle !== null && startAngle !== null ) {
|
||||
if (startAngle > Math.PI * 3 / 2 && endAngle < Math.PI / 2) {
|
||||
if (endAngle !== null && startAngle !== null) {
|
||||
if (startAngle > (Math.PI * 3) / 2 && endAngle < Math.PI / 2) {
|
||||
return endAngle + (Math.PI * 2 - startAngle);
|
||||
} else if (endAngle > Math.PI * 3 / 2 && startAngle < Math.PI / 2) {
|
||||
} else if (endAngle > (Math.PI * 3) / 2 && startAngle < Math.PI / 2) {
|
||||
return startAngle + (Math.PI * 2 - endAngle);
|
||||
} else {
|
||||
return endAngle - startAngle;
|
||||
|
|
@ -38,14 +34,14 @@ export function calcRadian(center: TypePoint, start: TypePoint, end: TypePoint):
|
|||
}
|
||||
}
|
||||
|
||||
function calcLineAngle(center: TypePoint, p: TypePoint): number | null {
|
||||
function calcLineAngle(center: Point, p: Point): number | null {
|
||||
const x = p.x - center.x;
|
||||
const y = center.y - p.y;
|
||||
if (x === 0) {
|
||||
if (y < 0) {
|
||||
return Math.PI / 2;
|
||||
} else if (y > 0) {
|
||||
return Math.PI * ( 3 / 2 );
|
||||
return Math.PI * (3 / 2);
|
||||
}
|
||||
} else if (y === 0) {
|
||||
if (x < 0) {
|
||||
|
|
@ -64,4 +60,4 @@ function calcLineAngle(center: TypePoint, p: TypePoint): number | null {
|
|||
return Math.PI * 2 - Math.atan(Math.abs(y) / Math.abs(x));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
|
||||
import { TypeElementAttrs } from '@idraw/types';
|
||||
import { DataElementAttrs } from '@idraw/types';
|
||||
import is from './is';
|
||||
|
||||
|
||||
function attrs(
|
||||
attrs: TypeElementAttrs
|
||||
): boolean {
|
||||
function attrs(attrs: DataElementAttrs): boolean {
|
||||
const { x, y, w, h, angle } = attrs;
|
||||
if (!(is.x(x) && is.y(y) && is.w(w) && is.h(h) && is.angle(angle))) {
|
||||
return false;
|
||||
}
|
||||
if (!(angle >= -360 && angle <= 360 )) {
|
||||
if (!(angle >= -360 && angle <= 360)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function box(
|
||||
desc: any = {},
|
||||
): boolean {
|
||||
function box(desc: any = {}): boolean {
|
||||
const { borderColor, borderRadius, borderWidth } = desc;
|
||||
if (desc.hasOwnProperty('borderColor') && !is.color(borderColor)) {
|
||||
return false;
|
||||
|
|
@ -32,9 +26,7 @@ function box(
|
|||
return true;
|
||||
}
|
||||
|
||||
function rectDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function rectDesc(desc: any): boolean {
|
||||
const { bgColor } = desc;
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)) {
|
||||
return false;
|
||||
|
|
@ -45,9 +37,7 @@ function rectDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function circleDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function circleDesc(desc: any): boolean {
|
||||
const { bgColor, borderColor, borderWidth } = desc;
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)) {
|
||||
return false;
|
||||
|
|
@ -61,10 +51,7 @@ function circleDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
function imageDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function imageDesc(desc: any): boolean {
|
||||
const { src } = desc;
|
||||
if (!is.imageSrc(src)) {
|
||||
return false;
|
||||
|
|
@ -72,9 +59,7 @@ function imageDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function svgDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function svgDesc(desc: any): boolean {
|
||||
const { svg } = desc;
|
||||
if (!is.svg(svg)) {
|
||||
return false;
|
||||
|
|
@ -82,9 +67,7 @@ function svgDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function htmlDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function htmlDesc(desc: any): boolean {
|
||||
const { html } = desc;
|
||||
if (!is.html(html)) {
|
||||
return false;
|
||||
|
|
@ -92,44 +75,50 @@ function htmlDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function textDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function textDesc(desc: any): boolean {
|
||||
const {
|
||||
text, color, fontSize, lineHeight, fontFamily, textAlign,
|
||||
fontWeight, bgColor, strokeWidth, strokeColor
|
||||
text,
|
||||
color,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
fontFamily,
|
||||
textAlign,
|
||||
fontWeight,
|
||||
bgColor,
|
||||
strokeWidth,
|
||||
strokeColor
|
||||
} = desc;
|
||||
if (!is.text(text)){
|
||||
if (!is.text(text)) {
|
||||
return false;
|
||||
}
|
||||
if (!is.color(color)){
|
||||
if (!is.color(color)) {
|
||||
return false;
|
||||
}
|
||||
if (!is.fontSize(fontSize)){
|
||||
if (!is.fontSize(fontSize)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)){
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('fontWeight') && !is.fontWeight(fontWeight)){
|
||||
if (desc.hasOwnProperty('fontWeight') && !is.fontWeight(fontWeight)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('lineHeight') && !is.lineHeight(lineHeight)){
|
||||
if (desc.hasOwnProperty('lineHeight') && !is.lineHeight(lineHeight)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('fontFamily') && !is.fontFamily(fontFamily)){
|
||||
if (desc.hasOwnProperty('fontFamily') && !is.fontFamily(fontFamily)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('textAlign') && !is.textAlign(textAlign)){
|
||||
if (desc.hasOwnProperty('textAlign') && !is.textAlign(textAlign)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('strokeWidth') && !is.strokeWidth(strokeWidth)){
|
||||
if (desc.hasOwnProperty('strokeWidth') && !is.strokeWidth(strokeWidth)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('strokeColor') && !is.color(strokeColor)){
|
||||
if (desc.hasOwnProperty('strokeColor') && !is.color(strokeColor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!box(desc)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -143,21 +132,19 @@ const check = {
|
|||
circleDesc,
|
||||
imageDesc,
|
||||
svgDesc,
|
||||
htmlDesc,
|
||||
htmlDesc
|
||||
};
|
||||
|
||||
type TypeCheck = {
|
||||
attrs: (value: any) => boolean,
|
||||
rectDesc: (value: any) => boolean,
|
||||
circleDesc: (value: any) => boolean,
|
||||
imageDesc: (value: any) => boolean,
|
||||
svgDesc: (value: any) => boolean,
|
||||
htmlDesc: (value: any) => boolean,
|
||||
textDesc: (value: any) => boolean,
|
||||
}
|
||||
|
||||
export {
|
||||
TypeCheck
|
||||
type CheckTypeUtil = {
|
||||
attrs: (value: any) => boolean;
|
||||
rectDesc: (value: any) => boolean;
|
||||
circleDesc: (value: any) => boolean;
|
||||
imageDesc: (value: any) => boolean;
|
||||
svgDesc: (value: any) => boolean;
|
||||
htmlDesc: (value: any) => boolean;
|
||||
textDesc: (value: any) => boolean;
|
||||
};
|
||||
|
||||
export default check;
|
||||
export { CheckTypeUtil };
|
||||
|
||||
export default check;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { TypeConfig, TypeConfigStrict } from '@idraw/types';
|
||||
import { IDrawConfig, IDrawConfigStrict } from '@idraw/types';
|
||||
import { deepClone } from '@idraw/util';
|
||||
|
||||
const defaultConfig: TypeConfigStrict = {
|
||||
const defaultConfig: IDrawConfigStrict = {
|
||||
elementWrapper: {
|
||||
color: '#0d85da',
|
||||
lockColor: '#aaaaaa',
|
||||
|
|
@ -11,7 +11,7 @@ const defaultConfig: TypeConfigStrict = {
|
|||
}
|
||||
};
|
||||
|
||||
function mergeConfig(config?: TypeConfig): TypeConfigStrict {
|
||||
function mergeConfig(config?: IDrawConfig): IDrawConfigStrict {
|
||||
const result = deepClone(defaultConfig);
|
||||
if (config) {
|
||||
if (config.elementWrapper) {
|
||||
|
|
|
|||
|
|
@ -1,50 +1,74 @@
|
|||
import {
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
TypePoint,
|
||||
TypeData,
|
||||
TypeScreenData,
|
||||
DataElement,
|
||||
DataElemDesc,
|
||||
Point,
|
||||
IDrawData,
|
||||
ScreenData
|
||||
} from '@idraw/types';
|
||||
|
||||
export type TypeCoreEventSelectBaseArg = {
|
||||
index: number | null;
|
||||
uuid: string | null;
|
||||
}
|
||||
};
|
||||
|
||||
export type TypeCoreEventArgMap = {
|
||||
error: any;
|
||||
mouseOverScreen: Point;
|
||||
mouseLeaveScreen: void;
|
||||
mouseOverElement: TypeCoreEventSelectBaseArg & {
|
||||
element: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
mouseLeaveElement: TypeCoreEventSelectBaseArg & {
|
||||
element: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
screenClickElement: TypeCoreEventSelectBaseArg & {
|
||||
element: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
screenDoubleClickElement: TypeCoreEventSelectBaseArg & {
|
||||
element: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
screenSelectElement: TypeCoreEventSelectBaseArg & {
|
||||
element: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
screenMoveElementStart: TypeCoreEventSelectBaseArg & Point;
|
||||
screenMoveElementEnd: TypeCoreEventSelectBaseArg & Point;
|
||||
screenChangeElement: TypeCoreEventSelectBaseArg & {
|
||||
width: number;
|
||||
height: number;
|
||||
angle: number;
|
||||
};
|
||||
changeData: IDrawData;
|
||||
changeScreen: ScreenData;
|
||||
drawFrameComplete: void;
|
||||
drawFrame: void;
|
||||
};
|
||||
|
||||
export type TypeCoreEventArgMap = {
|
||||
'error': any;
|
||||
'mouseOverScreen': TypePoint,
|
||||
'mouseLeaveScreen': void,
|
||||
'mouseOverElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
|
||||
'mouseLeaveElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
|
||||
'screenClickElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
|
||||
'screenDoubleClickElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
|
||||
'screenSelectElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
|
||||
'screenMoveElementStart': TypeCoreEventSelectBaseArg & TypePoint,
|
||||
'screenMoveElementEnd': TypeCoreEventSelectBaseArg & TypePoint,
|
||||
'screenChangeElement': TypeCoreEventSelectBaseArg & { width: number, height: number, angle: number};
|
||||
'changeData': TypeData;
|
||||
'changeScreen': TypeScreenData,
|
||||
'drawFrameComplete': void;
|
||||
'drawFrame': void;
|
||||
}
|
||||
|
||||
export interface TypeCoreEvent {
|
||||
on<T extends keyof TypeCoreEventArgMap >(key: T, callback: (p: TypeCoreEventArgMap[T]) => void): void
|
||||
off<T extends keyof TypeCoreEventArgMap >(key: T, callback: (p: TypeCoreEventArgMap[T]) => void): void
|
||||
trigger<T extends keyof TypeCoreEventArgMap >(key: T, p: TypeCoreEventArgMap[T]): void
|
||||
on<T extends keyof TypeCoreEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeCoreEventArgMap[T]) => void
|
||||
): void;
|
||||
off<T extends keyof TypeCoreEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeCoreEventArgMap[T]) => void
|
||||
): void;
|
||||
trigger<T extends keyof TypeCoreEventArgMap>(
|
||||
key: T,
|
||||
p: TypeCoreEventArgMap[T]
|
||||
): void;
|
||||
}
|
||||
|
||||
|
||||
export class CoreEvent implements TypeCoreEvent {
|
||||
|
||||
private _listeners: Map<string, ((p: any) => void)[]>;
|
||||
|
||||
constructor() {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
|
||||
on<T extends keyof TypeCoreEventArgMap >(eventKey: T, callback: (p: TypeCoreEventArgMap[T]) => void) {
|
||||
on<T extends keyof TypeCoreEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeCoreEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
callbacks?.push(callback);
|
||||
|
|
@ -53,8 +77,11 @@ export class CoreEvent implements TypeCoreEvent {
|
|||
this._listeners.set(eventKey, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
off<T extends keyof TypeCoreEventArgMap >(eventKey: T, callback: (p: TypeCoreEventArgMap[T]) => void) {
|
||||
|
||||
off<T extends keyof TypeCoreEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeCoreEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
|
|
@ -69,7 +96,10 @@ export class CoreEvent implements TypeCoreEvent {
|
|||
}
|
||||
}
|
||||
|
||||
trigger<T extends keyof TypeCoreEventArgMap >(eventKey: T, arg: TypeCoreEventArgMap[T]) {
|
||||
trigger<T extends keyof TypeCoreEventArgMap>(
|
||||
eventKey: T,
|
||||
arg: TypeCoreEventArgMap[T]
|
||||
) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
callbacks.forEach((cb) => {
|
||||
|
|
@ -81,14 +111,14 @@ export class CoreEvent implements TypeCoreEvent {
|
|||
}
|
||||
}
|
||||
|
||||
has<T extends keyof TypeCoreEventArgMap> (name: string) {
|
||||
has<T extends keyof TypeCoreEventArgMap>(name: string) {
|
||||
if (this._listeners.has(name)) {
|
||||
const list: ((p: TypeCoreEventArgMap[T]) => void)[] | undefined = this._listeners.get(name);
|
||||
const list: ((p: TypeCoreEventArgMap[T]) => void)[] | undefined =
|
||||
this._listeners.get(name);
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,65 +1,64 @@
|
|||
import { TypeElement, TypeData, TypeElemDesc } from '@idraw/types';
|
||||
|
||||
type TypeElementMap = {
|
||||
[uuid: string]: TypeElement<keyof TypeElemDesc>
|
||||
}
|
||||
import { DataElement, IDrawData, DataElemDesc } from '@idraw/types';
|
||||
|
||||
type DataElementMap = {
|
||||
[uuid: string]: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
|
||||
export function isChangeImageElementResource(
|
||||
before: TypeElement<'image'>,
|
||||
after: TypeElement<'image'>,
|
||||
before: DataElement<'image'>,
|
||||
after: DataElement<'image'>
|
||||
): boolean {
|
||||
return (before?.desc?.src !== after?.desc?.src);
|
||||
return before?.desc?.src !== after?.desc?.src;
|
||||
}
|
||||
|
||||
|
||||
export function isChangeSVGElementResource(
|
||||
before: TypeElement<'svg'>,
|
||||
after: TypeElement<'svg'>,
|
||||
before: DataElement<'svg'>,
|
||||
after: DataElement<'svg'>
|
||||
): boolean {
|
||||
return (before?.desc?.svg !== after?.desc?.svg);
|
||||
return before?.desc?.svg !== after?.desc?.svg;
|
||||
}
|
||||
|
||||
export function isChangeHTMLElementResource(
|
||||
before: TypeElement<'html'>,
|
||||
after: TypeElement<'html'>,
|
||||
before: DataElement<'html'>,
|
||||
after: DataElement<'html'>
|
||||
): boolean {
|
||||
return (
|
||||
before?.desc?.html !== after?.desc?.html
|
||||
|| before?.desc?.width !== after?.desc?.width
|
||||
|| before?.desc?.height !== after?.desc?.height
|
||||
before?.desc?.html !== after?.desc?.html ||
|
||||
before?.desc?.width !== after?.desc?.width ||
|
||||
before?.desc?.height !== after?.desc?.height
|
||||
);
|
||||
}
|
||||
|
||||
export function diffElementResourceChange(
|
||||
before: TypeElement<keyof TypeElemDesc>,
|
||||
after: TypeElement<keyof TypeElemDesc>,
|
||||
before: DataElement<keyof DataElemDesc>,
|
||||
after: DataElement<keyof DataElemDesc>
|
||||
): string | null {
|
||||
let result = null;
|
||||
let isChange = false;
|
||||
switch (after.type) {
|
||||
case 'image': {
|
||||
isChange = isChangeImageElementResource(
|
||||
before as TypeElement<'image'>,
|
||||
after as TypeElement<'image'>
|
||||
before as DataElement<'image'>,
|
||||
after as DataElement<'image'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'svg': {
|
||||
isChange = isChangeSVGElementResource(
|
||||
before as TypeElement<'svg'>,
|
||||
after as TypeElement<'svg'>
|
||||
before as DataElement<'svg'>,
|
||||
after as DataElement<'svg'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'html': {
|
||||
isChange = isChangeHTMLElementResource(
|
||||
before as TypeElement<'html'>,
|
||||
after as TypeElement<'html'>
|
||||
before as DataElement<'html'>,
|
||||
after as DataElement<'html'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (isChange === true) {
|
||||
result = after.uuid;
|
||||
|
|
@ -68,8 +67,8 @@ export function diffElementResourceChange(
|
|||
}
|
||||
|
||||
export function diffElementResourceChangeList(
|
||||
before: TypeData,
|
||||
after: TypeData,
|
||||
before: IDrawData,
|
||||
after: IDrawData
|
||||
): string[] {
|
||||
const uuids: string[] = [];
|
||||
const beforeMap = parseDataElementMap(before);
|
||||
|
|
@ -83,26 +82,27 @@ export function diffElementResourceChangeList(
|
|||
switch (beforeMap[uuid].type) {
|
||||
case 'image': {
|
||||
isChange = isChangeImageElementResource(
|
||||
beforeMap[uuid] as TypeElement<'image'>,
|
||||
afterMap[uuid] as TypeElement<'image'>
|
||||
beforeMap[uuid] as DataElement<'image'>,
|
||||
afterMap[uuid] as DataElement<'image'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'svg': {
|
||||
isChange = isChangeSVGElementResource(
|
||||
beforeMap[uuid] as TypeElement<'svg'>,
|
||||
afterMap[uuid] as TypeElement<'svg'>
|
||||
beforeMap[uuid] as DataElement<'svg'>,
|
||||
afterMap[uuid] as DataElement<'svg'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'html': {
|
||||
isChange = isChangeHTMLElementResource(
|
||||
beforeMap[uuid] as TypeElement<'html'>,
|
||||
afterMap[uuid] as TypeElement<'html'>
|
||||
beforeMap[uuid] as DataElement<'html'>,
|
||||
afterMap[uuid] as DataElement<'html'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (isChange === true) {
|
||||
uuids.push(uuid);
|
||||
|
|
@ -114,11 +114,10 @@ export function diffElementResourceChangeList(
|
|||
return uuids;
|
||||
}
|
||||
|
||||
|
||||
function parseDataElementMap(data: TypeData): TypeElementMap {
|
||||
const elemMap: TypeElementMap = {};
|
||||
function parseDataElementMap(data: IDrawData): DataElementMap {
|
||||
const elemMap: DataElementMap = {};
|
||||
data.elements.forEach((elem) => {
|
||||
elemMap[elem.uuid] = elem;
|
||||
})
|
||||
});
|
||||
return elemMap;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import {
|
||||
TypeContext,
|
||||
// TypeElemDesc,
|
||||
TypeElement,
|
||||
IDrawContext,
|
||||
// DataElemDesc,
|
||||
DataElement
|
||||
} from '@idraw/types';
|
||||
import { istype, isColorStr } from '@idraw/util';
|
||||
import { rotateElement } from './../transform';
|
||||
import is from './../is';
|
||||
|
||||
export function clearContext(ctx: TypeContext) {
|
||||
export function clearContext(ctx: IDrawContext) {
|
||||
// ctx.setFillStyle('rgb(0 0 0 / 100%)');
|
||||
// ctx.setStrokeStyle('rgb(0 0 0 / 100%)');
|
||||
ctx.setFillStyle('#000000');
|
||||
|
|
@ -15,21 +15,21 @@ export function clearContext(ctx: TypeContext) {
|
|||
ctx.setLineDash([]);
|
||||
ctx.setGlobalAlpha(1);
|
||||
ctx.setShadowColor('#00000000');
|
||||
ctx.setShadowOffsetX(0)
|
||||
ctx.setShadowOffsetX(0);
|
||||
ctx.setShadowOffsetY(0);
|
||||
ctx.setShadowBlur(0);
|
||||
}
|
||||
|
||||
export function drawBgColor(ctx: TypeContext, color: string) {
|
||||
export function drawBgColor(ctx: IDrawContext, color: string) {
|
||||
const size = ctx.getSize();
|
||||
ctx.setFillStyle(color);
|
||||
ctx.fillRect(0, 0, size.contextWidth, size.contextHeight);
|
||||
}
|
||||
|
||||
export function drawBox(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'text' | 'rect'>,
|
||||
pattern: string | CanvasPattern | null,
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'text' | 'rect'>,
|
||||
pattern: string | CanvasPattern | null
|
||||
): void {
|
||||
clearContext(ctx);
|
||||
drawBoxBorder(ctx, elem);
|
||||
|
|
@ -47,20 +47,19 @@ export function drawBox(
|
|||
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||
ctx.arcTo(x, y + h, x, y, r);
|
||||
ctx.arcTo(x, y, x + w, y, r);
|
||||
ctx.closePath();
|
||||
ctx.closePath();
|
||||
if (typeof pattern === 'string') {
|
||||
ctx.setFillStyle(pattern);
|
||||
ctx.setFillStyle(pattern);
|
||||
} else if (['CanvasPattern'].includes(istype.type(pattern))) {
|
||||
ctx.setFillStyle(pattern as CanvasPattern);
|
||||
}
|
||||
ctx.fill();
|
||||
ctx.fill();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function drawBoxBorder(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'text'|'rect'>,
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'text' | 'rect'>
|
||||
): void {
|
||||
clearContext(ctx);
|
||||
rotateElement(ctx, elem, () => {
|
||||
|
|
@ -103,7 +102,7 @@ export function drawBoxBorder(
|
|||
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||
ctx.arcTo(x, y + h, x, y, r);
|
||||
ctx.arcTo(x, y, x + w, y, r);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { TypeContext, TypeHelperConfig } from '@idraw/types';
|
||||
import { IDrawContext, HelperConfig } from '@idraw/types';
|
||||
import { rotateContext } from './../transform';
|
||||
import { clearContext } from './base';
|
||||
|
||||
export function drawElementWrapper(ctx: TypeContext, config: TypeHelperConfig) {
|
||||
export function drawElementWrapper(ctx: IDrawContext, config: HelperConfig) {
|
||||
if (!config?.selectedElementWrapper) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -151,7 +151,7 @@ export function drawElementWrapper(ctx: TypeContext, config: TypeHelperConfig) {
|
|||
});
|
||||
}
|
||||
|
||||
export function drawAreaWrapper(ctx: TypeContext, config: TypeHelperConfig) {
|
||||
export function drawAreaWrapper(ctx: IDrawContext, config: HelperConfig) {
|
||||
if (!config?.selectedAreaWrapper) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -178,8 +178,8 @@ export function drawAreaWrapper(ctx: TypeContext, config: TypeHelperConfig) {
|
|||
}
|
||||
|
||||
export function drawElementListWrappers(
|
||||
ctx: TypeContext,
|
||||
config: TypeHelperConfig
|
||||
ctx: IDrawContext,
|
||||
config: HelperConfig
|
||||
) {
|
||||
if (!Array.isArray(config?.selectedElementListWrappers)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import {
|
||||
TypeContext,
|
||||
TypePoint,
|
||||
TypeData,
|
||||
TypeHelperWrapperControllerDirection,
|
||||
TypeElement,
|
||||
TypeElemDesc
|
||||
IDrawContext,
|
||||
Point,
|
||||
IDrawData,
|
||||
HelperWrapperControllerDirection,
|
||||
DataElement,
|
||||
DataElemDesc
|
||||
} from '@idraw/types';
|
||||
import { createUUID } from '@idraw/util';
|
||||
import { rotateElement } from './transform';
|
||||
|
|
@ -16,13 +16,13 @@ import { LIMIT_QBLIQUE_ANGLE } from './../constant/element';
|
|||
const limitQbliqueAngle = LIMIT_QBLIQUE_ANGLE;
|
||||
|
||||
export class Element {
|
||||
private _ctx: TypeContext;
|
||||
private _ctx: IDrawContext;
|
||||
|
||||
constructor(ctx: TypeContext) {
|
||||
constructor(ctx: IDrawContext) {
|
||||
this._ctx = ctx;
|
||||
}
|
||||
|
||||
initData(data: TypeData): TypeData {
|
||||
initData(data: IDrawData): IDrawData {
|
||||
data.elements.forEach((elem) => {
|
||||
if (!(elem.uuid && typeof elem.uuid === 'string')) {
|
||||
elem.uuid = createUUID();
|
||||
|
|
@ -31,7 +31,7 @@ export class Element {
|
|||
return data;
|
||||
}
|
||||
|
||||
isPointInElement(p: TypePoint, data: TypeData): [number, string | null] {
|
||||
isPointInElement(p: Point, data: IDrawData): [number, string | null] {
|
||||
const ctx = this._ctx;
|
||||
let idx = -1;
|
||||
let uuid = null;
|
||||
|
|
@ -67,10 +67,10 @@ export class Element {
|
|||
}
|
||||
|
||||
dragElement(
|
||||
data: TypeData,
|
||||
data: IDrawData,
|
||||
uuid: string,
|
||||
point: TypePoint,
|
||||
prevPoint: TypePoint,
|
||||
point: Point,
|
||||
prevPoint: Point,
|
||||
scale: number
|
||||
): void {
|
||||
const index = this.getElementIndex(data, uuid);
|
||||
|
|
@ -85,12 +85,12 @@ export class Element {
|
|||
}
|
||||
|
||||
transformElement(
|
||||
data: TypeData,
|
||||
data: IDrawData,
|
||||
uuid: string,
|
||||
point: TypePoint,
|
||||
prevPoint: TypePoint,
|
||||
point: Point,
|
||||
prevPoint: Point,
|
||||
scale: number,
|
||||
direction: TypeHelperWrapperControllerDirection
|
||||
direction: HelperWrapperControllerDirection
|
||||
): null | {
|
||||
width: number;
|
||||
height: number;
|
||||
|
|
@ -124,7 +124,7 @@ export class Element {
|
|||
'left'
|
||||
].includes(direction)
|
||||
) {
|
||||
const p = calcuScaleElemPosition(elem, moveX, moveY, direction, scale);
|
||||
const p = calcuScaleElemPosition(elem, moveX, moveY, direction);
|
||||
elem.x = p.x;
|
||||
elem.y = p.y;
|
||||
elem.w = p.w;
|
||||
|
|
@ -144,7 +144,7 @@ export class Element {
|
|||
};
|
||||
}
|
||||
|
||||
getElementIndex(data: TypeData, uuid: string): number {
|
||||
getElementIndex(data: IDrawData, uuid: string): number {
|
||||
let idx = -1;
|
||||
for (let i = 0; i < data.elements.length; i++) {
|
||||
if (data.elements[i].uuid === uuid) {
|
||||
|
|
@ -155,7 +155,7 @@ export class Element {
|
|||
return idx;
|
||||
}
|
||||
|
||||
limitElementAttrs(elem: TypeElement<keyof TypeElemDesc>) {
|
||||
limitElementAttrs(elem: DataElement<keyof DataElemDesc>) {
|
||||
elem.x = limitNum(elem.x);
|
||||
elem.y = limitNum(elem.y);
|
||||
elem.w = limitNum(elem.w);
|
||||
|
|
@ -165,14 +165,14 @@ export class Element {
|
|||
}
|
||||
|
||||
function calcuScaleElemPosition(
|
||||
elem: TypeElement<keyof TypeElemDesc>,
|
||||
elem: DataElement<keyof DataElemDesc>,
|
||||
moveX: number,
|
||||
moveY: number,
|
||||
direction: TypeHelperWrapperControllerDirection,
|
||||
scale: number
|
||||
): TypePoint & { w: number; h: number } {
|
||||
direction: HelperWrapperControllerDirection
|
||||
// scale: number
|
||||
): Point & { w: number; h: number } {
|
||||
const p = { x: elem.x, y: elem.y, w: elem.w, h: elem.h };
|
||||
let angle = elem.angle;
|
||||
let angle = elem.angle || 0;
|
||||
if (angle < 0) {
|
||||
angle = Math.max(0, 360 + angle);
|
||||
}
|
||||
|
|
@ -219,7 +219,7 @@ function calcuScaleElemPosition(
|
|||
break;
|
||||
}
|
||||
case 'top': {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle) < limitQbliqueAngle) {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle || 0) < limitQbliqueAngle) {
|
||||
if (p.h - moveY > 0) {
|
||||
p.y += moveY;
|
||||
p.h -= moveY;
|
||||
|
|
@ -228,7 +228,10 @@ function calcuScaleElemPosition(
|
|||
p.w -= (moveY / elem.h) * elem.w;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle > 0 || elem.angle < 0) {
|
||||
} else if (
|
||||
elem.angle !== undefined &&
|
||||
(elem.angle > 0 || elem.angle < 0)
|
||||
) {
|
||||
const angle =
|
||||
elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
|
|
@ -364,7 +367,7 @@ function calcuScaleElemPosition(
|
|||
break;
|
||||
}
|
||||
case 'right': {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle) < limitQbliqueAngle) {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle || 0) < limitQbliqueAngle) {
|
||||
if (elem.w + moveX > 0) {
|
||||
p.w += moveX;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
|
|
@ -372,7 +375,10 @@ function calcuScaleElemPosition(
|
|||
p.h += (moveX * elem.h) / elem.w;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle > 0 || elem.angle < 0) {
|
||||
} else if (
|
||||
elem.angle !== undefined &&
|
||||
(elem.angle > 0 || elem.angle < 0)
|
||||
) {
|
||||
const angle =
|
||||
elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
|
|
@ -447,7 +453,7 @@ function calcuScaleElemPosition(
|
|||
break;
|
||||
}
|
||||
case 'bottom': {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle) < limitQbliqueAngle) {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle || 0) < limitQbliqueAngle) {
|
||||
if (elem.h + moveY > 0) {
|
||||
p.h += moveY;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
|
|
@ -455,7 +461,10 @@ function calcuScaleElemPosition(
|
|||
p.w += (moveY / elem.h) * elem.w;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle > 0 || elem.angle < 0) {
|
||||
} else if (
|
||||
elem.angle !== undefined &&
|
||||
(elem.angle > 0 || elem.angle < 0)
|
||||
) {
|
||||
const angle =
|
||||
elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
|
|
@ -530,7 +539,7 @@ function calcuScaleElemPosition(
|
|||
break;
|
||||
}
|
||||
case 'left': {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle) < limitQbliqueAngle) {
|
||||
if (elem.angle === 0 || Math.abs(elem.angle || 0) < limitQbliqueAngle) {
|
||||
if (elem.w - moveX > 0) {
|
||||
p.x += moveX;
|
||||
p.w -= moveX;
|
||||
|
|
@ -539,7 +548,10 @@ function calcuScaleElemPosition(
|
|||
p.y += ((moveX / elem.w) * elem.h) / 2;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle > 0 || elem.angle < 0) {
|
||||
} else if (
|
||||
elem.angle !== undefined &&
|
||||
(elem.angle > 0 || elem.angle < 0)
|
||||
) {
|
||||
const angle =
|
||||
elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
class ElementBase {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
class ElementController {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export default ElementController;
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import ElementController from './element-controller';
|
||||
|
||||
class ElementHub {
|
||||
|
||||
private _controllerMap: Map<string, ElementController> = new Map();
|
||||
|
||||
constructor() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
register(type: string, controller: ElementController) {
|
||||
if (this._controllerMap.has(type) !== true) {
|
||||
this._controllerMap.set(type, controller)
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._controllerMap.clear();
|
||||
}
|
||||
|
||||
getDrawActions() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
export default ElementHub;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { TypeHelperWrapperControllerDirection, TypePoint } from '@idraw/types';
|
||||
import { HelperWrapperControllerDirection, Point } from '@idraw/types';
|
||||
import { Mode, CursorStatus } from '../constant/static';
|
||||
|
||||
type TempDataDesc = {
|
||||
|
|
@ -8,9 +8,9 @@ type TempDataDesc = {
|
|||
selectedUUID: string | null;
|
||||
selectedUUIDList: string[];
|
||||
hoverUUID: string | null;
|
||||
selectedControllerDirection: TypeHelperWrapperControllerDirection | null;
|
||||
hoverControllerDirection: TypeHelperWrapperControllerDirection | null;
|
||||
prevPoint: TypePoint | null;
|
||||
selectedControllerDirection: HelperWrapperControllerDirection | null;
|
||||
hoverControllerDirection: HelperWrapperControllerDirection | null;
|
||||
prevPoint: Point | null;
|
||||
hasChangedElement: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import {
|
||||
TypePoint,
|
||||
TypeHelperWrapperControllerDirection,
|
||||
Point,
|
||||
HelperWrapperControllerDirection,
|
||||
InterfaceHelperPlugin,
|
||||
TypeConfigStrict,
|
||||
TypeData,
|
||||
TypeHelperConfig
|
||||
IDrawConfigStrict,
|
||||
IDrawData,
|
||||
HelperConfig
|
||||
} from '@idraw/types';
|
||||
import { deepClone, throttle } from '@idraw/util';
|
||||
import Board from '@idraw/board';
|
||||
|
|
@ -19,9 +19,9 @@ type Options = {
|
|||
coreEvent: CoreEvent;
|
||||
board: Board;
|
||||
element: Element;
|
||||
config: TypeConfigStrict;
|
||||
config: IDrawConfigStrict;
|
||||
drawFeekback: () => void;
|
||||
getDataFeekback: () => TypeData;
|
||||
getDataFeekback: () => IDrawData;
|
||||
selectElementByIndex: (index: number, opts?: { useMode?: boolean }) => void;
|
||||
emitChangeScreen: () => void;
|
||||
emitChangeData: () => void;
|
||||
|
|
@ -48,7 +48,7 @@ export class Engine {
|
|||
this._plugins.push(plugin);
|
||||
}
|
||||
|
||||
getHelperConfig(): TypeHelperConfig {
|
||||
getHelperConfig(): HelperConfig {
|
||||
return this.helper.getConfig();
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ export class Engine {
|
|||
board.on('moveEnd', this._handleMoveEnd.bind(this));
|
||||
}
|
||||
|
||||
private _handleDoubleClick(point: TypePoint) {
|
||||
private _handleDoubleClick(point: Point) {
|
||||
const { element, getDataFeekback, drawFeekback, coreEvent } = this._opts;
|
||||
const data = getDataFeekback();
|
||||
const [index, uuid] = element.isPointInElement(point, data);
|
||||
|
|
@ -110,7 +110,7 @@ export class Engine {
|
|||
drawFeekback();
|
||||
}
|
||||
|
||||
_handlePoint(point: TypePoint): void {
|
||||
_handlePoint(point: Point): void {
|
||||
if (!this._mapper.isEffectivePoint(point)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ export class Engine {
|
|||
drawFeekback();
|
||||
}
|
||||
|
||||
private _handleClick(point: TypePoint): void {
|
||||
private _handleClick(point: Point): void {
|
||||
const { element, getDataFeekback, coreEvent, drawFeekback } = this._opts;
|
||||
const data = getDataFeekback();
|
||||
const [index, uuid] = element.isPointInElement(point, data);
|
||||
|
|
@ -180,7 +180,7 @@ export class Engine {
|
|||
drawFeekback();
|
||||
}
|
||||
|
||||
private _handleMoveStart(point: TypePoint): void {
|
||||
private _handleMoveStart(point: Point): void {
|
||||
const { element, getDataFeekback, coreEvent } = this._opts;
|
||||
const data = getDataFeekback();
|
||||
const helper = this.helper;
|
||||
|
|
@ -204,7 +204,7 @@ export class Engine {
|
|||
}
|
||||
}
|
||||
|
||||
private _handleMove(point: TypePoint): void {
|
||||
private _handleMove(point: Point): void {
|
||||
const { drawFeekback } = this._opts;
|
||||
const helper = this.helper;
|
||||
if (this.temp.get('mode') === Mode.SELECT_ELEMENT_LIST) {
|
||||
|
|
@ -236,7 +236,7 @@ export class Engine {
|
|||
this.temp.get('prevPoint'),
|
||||
this.temp.get(
|
||||
'selectedControllerDirection'
|
||||
) as TypeHelperWrapperControllerDirection
|
||||
) as HelperWrapperControllerDirection
|
||||
);
|
||||
this.temp.set('cursorStatus', CursorStatus.DRAGGING);
|
||||
}
|
||||
|
|
@ -249,8 +249,8 @@ export class Engine {
|
|||
|
||||
private _dragElements(
|
||||
uuids: string[],
|
||||
point: TypePoint,
|
||||
prevPoint: TypePoint | null
|
||||
point: Point,
|
||||
prevPoint: Point | null
|
||||
): void {
|
||||
if (!prevPoint) {
|
||||
return;
|
||||
|
|
@ -280,9 +280,9 @@ export class Engine {
|
|||
|
||||
private _transfromElement(
|
||||
uuid: string,
|
||||
point: TypePoint,
|
||||
prevPoint: TypePoint | null,
|
||||
direction: TypeHelperWrapperControllerDirection
|
||||
point: Point,
|
||||
prevPoint: Point | null,
|
||||
direction: HelperWrapperControllerDirection
|
||||
): null | { width: number; height: number; angle: number } {
|
||||
if (!prevPoint) {
|
||||
return null;
|
||||
|
|
@ -301,7 +301,7 @@ export class Engine {
|
|||
return result;
|
||||
}
|
||||
|
||||
private _handleMoveEnd(point: TypePoint): void {
|
||||
private _handleMoveEnd(point: Point): void {
|
||||
const {
|
||||
element,
|
||||
getDataFeekback,
|
||||
|
|
@ -359,7 +359,7 @@ export class Engine {
|
|||
}
|
||||
}
|
||||
|
||||
private _handleHover(point: TypePoint): void {
|
||||
private _handleHover(point: Point): void {
|
||||
let isMouseOverElement = false;
|
||||
const { board, getDataFeekback, coreEvent } = this._opts;
|
||||
const data = getDataFeekback();
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import {
|
||||
TypeData,
|
||||
TypeHelper,
|
||||
TypeHelperConfig,
|
||||
TypeHelperUpdateOpts,
|
||||
TypeHelperWrapperControllerDirection,
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
TypeContext,
|
||||
TypePoint,
|
||||
TypeConfigStrict,
|
||||
TypeHeplerSelectedElementWrapper
|
||||
IDrawData,
|
||||
HelperConfig,
|
||||
HelperUpdateOpts,
|
||||
HelperWrapperControllerDirection,
|
||||
DataElement,
|
||||
DataElemDesc,
|
||||
IDrawContext,
|
||||
Point,
|
||||
IDrawConfigStrict,
|
||||
HeplerSelectedElementWrapper
|
||||
} from '@idraw/types';
|
||||
import Board from '@idraw/board';
|
||||
import { deepClone } from '@idraw/util';
|
||||
|
|
@ -20,15 +19,15 @@ import { LIMIT_QBLIQUE_ANGLE } from './../constant/element';
|
|||
|
||||
const limitQbliqueAngle = LIMIT_QBLIQUE_ANGLE;
|
||||
|
||||
export class Helper implements TypeHelper {
|
||||
private _helperConfig: TypeHelperConfig;
|
||||
private _coreConfig: TypeConfigStrict;
|
||||
private _ctx: TypeContext;
|
||||
export class Helper {
|
||||
private _helperConfig: HelperConfig;
|
||||
private _coreConfig: IDrawConfigStrict;
|
||||
private _ctx: IDrawContext;
|
||||
private _board: Board;
|
||||
private _areaStart: TypePoint = { x: 0, y: 0 };
|
||||
private _areaEnd: TypePoint = { x: 0, y: 0 };
|
||||
private _areaStart: Point = { x: 0, y: 0 };
|
||||
private _areaEnd: Point = { x: 0, y: 0 };
|
||||
|
||||
constructor(board: Board, config: TypeConfigStrict) {
|
||||
constructor(board: Board, config: IDrawConfigStrict) {
|
||||
this._board = board;
|
||||
this._ctx = this._board.getContext();
|
||||
this._coreConfig = config;
|
||||
|
|
@ -37,13 +36,13 @@ export class Helper implements TypeHelper {
|
|||
};
|
||||
}
|
||||
|
||||
updateConfig(data: TypeData, opts: TypeHelperUpdateOpts): void {
|
||||
updateConfig(data: IDrawData, opts: HelperUpdateOpts): void {
|
||||
this._updateElementIndex(data);
|
||||
this._updateSelectedElementWrapper(data, opts);
|
||||
this._updateSelectedElementListWrapper(data, opts);
|
||||
}
|
||||
|
||||
getConfig(): TypeHelperConfig {
|
||||
getConfig(): HelperConfig {
|
||||
return deepClone(this._helperConfig);
|
||||
}
|
||||
|
||||
|
|
@ -56,20 +55,20 @@ export class Helper implements TypeHelper {
|
|||
}
|
||||
|
||||
isPointInElementWrapperController(
|
||||
p: TypePoint,
|
||||
data?: TypeData
|
||||
p: Point,
|
||||
data?: IDrawData
|
||||
): {
|
||||
uuid: string | null | undefined;
|
||||
selectedControllerDirection: TypeHelperWrapperControllerDirection | null;
|
||||
hoverControllerDirection: TypeHelperWrapperControllerDirection | null;
|
||||
selectedControllerDirection: HelperWrapperControllerDirection | null;
|
||||
hoverControllerDirection: HelperWrapperControllerDirection | null;
|
||||
directIndex: number | null;
|
||||
} {
|
||||
const ctx = this._ctx;
|
||||
const uuid = this._helperConfig?.selectedElementWrapper?.uuid || null;
|
||||
let directIndex = null;
|
||||
let selectedControllerDirection: TypeHelperWrapperControllerDirection | null =
|
||||
let selectedControllerDirection: HelperWrapperControllerDirection | null =
|
||||
null;
|
||||
let hoverControllerDirection: TypeHelperWrapperControllerDirection | null =
|
||||
let hoverControllerDirection: HelperWrapperControllerDirection | null =
|
||||
null;
|
||||
if (!this._helperConfig.selectedElementWrapper) {
|
||||
return {
|
||||
|
|
@ -90,7 +89,7 @@ export class Helper implements TypeHelper {
|
|||
wrapper.controllers.bottom,
|
||||
wrapper.controllers.bottomRight
|
||||
];
|
||||
const directionNames: TypeHelperWrapperControllerDirection[] = [
|
||||
const directionNames: HelperWrapperControllerDirection[] = [
|
||||
'right',
|
||||
'top-right',
|
||||
'top',
|
||||
|
|
@ -191,7 +190,7 @@ export class Helper implements TypeHelper {
|
|||
};
|
||||
}
|
||||
|
||||
isPointInElementList(p: TypePoint, data: TypeData): boolean {
|
||||
isPointInElementList(p: Point, data: IDrawData): boolean {
|
||||
const ctx = this._ctx;
|
||||
let idx = -1;
|
||||
let uuid = null;
|
||||
|
|
@ -232,12 +231,12 @@ export class Helper implements TypeHelper {
|
|||
}
|
||||
}
|
||||
|
||||
startSelectArea(p: TypePoint) {
|
||||
startSelectArea(p: Point) {
|
||||
this._areaStart = p;
|
||||
this._areaEnd = p;
|
||||
}
|
||||
|
||||
changeSelectArea(p: TypePoint) {
|
||||
changeSelectArea(p: Point) {
|
||||
this._areaEnd = p;
|
||||
this._calcSelectedArea();
|
||||
}
|
||||
|
|
@ -248,7 +247,7 @@ export class Helper implements TypeHelper {
|
|||
this._calcSelectedArea();
|
||||
}
|
||||
|
||||
calcSelectedElements(data: TypeData) {
|
||||
calcSelectedElements(data: IDrawData) {
|
||||
const transform = this._ctx.getTransform();
|
||||
const { scale = 1, scrollX = 0, scrollY = 0 } = transform;
|
||||
const start = this._areaStart;
|
||||
|
|
@ -303,16 +302,16 @@ export class Helper implements TypeHelper {
|
|||
};
|
||||
}
|
||||
|
||||
private _updateElementIndex(data: TypeData) {
|
||||
private _updateElementIndex(data: IDrawData) {
|
||||
this._helperConfig.elementIndexMap = {};
|
||||
data.elements.forEach((elem: TypeElement<keyof TypeElemDesc>, i) => {
|
||||
data.elements.forEach((elem: DataElement<keyof DataElemDesc>, i) => {
|
||||
this._helperConfig.elementIndexMap[elem.uuid] = i;
|
||||
});
|
||||
}
|
||||
|
||||
private _updateSelectedElementWrapper(
|
||||
data: TypeData,
|
||||
opts: TypeHelperUpdateOpts
|
||||
data: IDrawData,
|
||||
opts: HelperUpdateOpts
|
||||
) {
|
||||
const { selectedUUID: uuid } = opts;
|
||||
if (
|
||||
|
|
@ -334,11 +333,11 @@ export class Helper implements TypeHelper {
|
|||
}
|
||||
|
||||
private _updateSelectedElementListWrapper(
|
||||
data: TypeData,
|
||||
opts: TypeHelperUpdateOpts
|
||||
data: IDrawData,
|
||||
opts: HelperUpdateOpts
|
||||
) {
|
||||
const { selectedUUIDList } = opts;
|
||||
const wrapperList: TypeHeplerSelectedElementWrapper[] = [];
|
||||
const wrapperList: HeplerSelectedElementWrapper[] = [];
|
||||
data.elements.forEach((elem) => {
|
||||
if (selectedUUIDList?.includes(elem.uuid)) {
|
||||
const wrapper = this._createSelectedElementWrapper(elem, opts);
|
||||
|
|
@ -349,9 +348,9 @@ export class Helper implements TypeHelper {
|
|||
}
|
||||
|
||||
private _createSelectedElementWrapper(
|
||||
elem: TypeElement<keyof TypeElemDesc>,
|
||||
opts: TypeHelperUpdateOpts
|
||||
): TypeHeplerSelectedElementWrapper {
|
||||
elem: DataElement<keyof DataElemDesc>,
|
||||
opts: HelperUpdateOpts
|
||||
): HeplerSelectedElementWrapper {
|
||||
const { scale } = opts;
|
||||
const elemWrapper = this._coreConfig.elementWrapper;
|
||||
const controllerSize = elemWrapper.controllerSize / scale;
|
||||
|
|
@ -372,7 +371,7 @@ export class Helper implements TypeHelper {
|
|||
// const controllerOffset = controllerSize;
|
||||
const controllerOffset = lineWidth;
|
||||
|
||||
const wrapper: TypeHeplerSelectedElementWrapper = {
|
||||
const wrapper: HeplerSelectedElementWrapper = {
|
||||
uuid: elem.uuid,
|
||||
controllerSize: controllerSize,
|
||||
controllerOffset: controllerOffset,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ function fontWeight(value: any) {
|
|||
return ['bold'].includes(value);
|
||||
}
|
||||
|
||||
const is: TypeIs = {
|
||||
const is: IsTypeUtil = {
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
|
|
@ -124,7 +124,7 @@ const is: TypeIs = {
|
|||
strokeWidth
|
||||
};
|
||||
|
||||
type TypeIs = {
|
||||
type IsTypeUtil = {
|
||||
x: (value: any) => boolean;
|
||||
y: (value: any) => boolean;
|
||||
w: (value: any) => boolean;
|
||||
|
|
@ -150,4 +150,4 @@ type TypeIs = {
|
|||
|
||||
export default is;
|
||||
|
||||
export { TypeIs };
|
||||
export { IsTypeUtil };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TypeData, TypePoint, TypePointCursor } from '@idraw/types';
|
||||
import { IDrawData, Point, PointCursor } from '@idraw/types';
|
||||
import Board from '@idraw/board';
|
||||
import { Helper } from './helper';
|
||||
import { Element } from './element';
|
||||
|
|
@ -27,7 +27,7 @@ export class Mapper {
|
|||
this[_helper] = this[_opts].helper;
|
||||
}
|
||||
|
||||
isEffectivePoint(p: TypePoint): boolean {
|
||||
isEffectivePoint(p: Point): boolean {
|
||||
const scrollLineWidth = this[_board].getScrollLineWidth();
|
||||
const screenInfo = this[_board].getScreenInfo();
|
||||
if (
|
||||
|
|
@ -40,13 +40,13 @@ export class Mapper {
|
|||
}
|
||||
|
||||
judgePointCursor(
|
||||
p: TypePoint,
|
||||
data: TypeData
|
||||
p: Point,
|
||||
data: IDrawData
|
||||
): {
|
||||
cursor: TypePointCursor;
|
||||
cursor: PointCursor;
|
||||
elementUUID: string | null;
|
||||
} {
|
||||
let cursor: TypePointCursor = 'auto';
|
||||
let cursor: PointCursor = 'auto';
|
||||
let elementUUID: string | null = null;
|
||||
if (!this.isEffectivePoint(p)) {
|
||||
return { cursor, elementUUID };
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { TypeData, TypeElement, TypeElemDesc } from '@idraw/types';
|
||||
import { IDrawData, DataElement, DataElemDesc } from '@idraw/types';
|
||||
import { elementNames } from './../constant/element';
|
||||
|
||||
export function parseData(data: any): TypeData {
|
||||
const result: TypeData = {
|
||||
elements: [],
|
||||
export function parseData(data: any): IDrawData {
|
||||
const result: IDrawData = {
|
||||
elements: []
|
||||
};
|
||||
if (Array.isArray(data?.elements)) {
|
||||
data?.elements.forEach((elem: any = {}) => {
|
||||
|
|
@ -13,24 +13,28 @@ export function parseData(data: any): TypeData {
|
|||
});
|
||||
}
|
||||
if (typeof data.bgColor === 'string') {
|
||||
result.bgColor = data.bgColor;
|
||||
result.bgColor = data.bgColor;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isElement(
|
||||
elem: TypeElement<keyof TypeElemDesc>
|
||||
): boolean{
|
||||
if (!(isNumber(elem.x) && isNumber(elem.y) && isNumber(elem.w) && isNumber(elem.h))) {
|
||||
function isElement(elem: DataElement<keyof DataElemDesc>): boolean {
|
||||
if (
|
||||
!(
|
||||
isNumber(elem.x) &&
|
||||
isNumber(elem.y) &&
|
||||
isNumber(elem.w) &&
|
||||
isNumber(elem.h)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!(typeof elem.type === 'string' && elementNames.includes(elem.type))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isNumber(num: any) {
|
||||
return (num >= 0 || num < 0);
|
||||
}
|
||||
return num >= 0 || num < 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,35 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypePoint,
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
} from '@idraw/types';
|
||||
import { IDrawContext, Point, DataElement, DataElemDesc } from '@idraw/types';
|
||||
import { calcElementCenter, parseAngleToRadian } from './calculate';
|
||||
|
||||
function rotateElement(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<keyof TypeElemDesc>,
|
||||
callback: (ctx: TypeContext) => void
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<keyof DataElemDesc>,
|
||||
callback: (ctx: IDrawContext) => void
|
||||
): void {
|
||||
const center: TypePoint = calcElementCenter(elem);
|
||||
const center: Point = calcElementCenter(elem);
|
||||
const radian = parseAngleToRadian(elem.angle || 0);
|
||||
return rotateContext(ctx, center, radian || 0, callback);
|
||||
}
|
||||
|
||||
|
||||
function rotateContext(
|
||||
ctx: TypeContext,
|
||||
center: TypePoint | undefined,
|
||||
ctx: IDrawContext,
|
||||
center: Point | undefined,
|
||||
radian: number,
|
||||
callback: (ctx: TypeContext) => void
|
||||
callback: (ctx: IDrawContext) => void
|
||||
): void {
|
||||
if (center && (radian > 0 || radian < 0)) {
|
||||
ctx.translate(center.x, center.y);
|
||||
ctx.rotate(radian);
|
||||
ctx.translate(- center.x, - center.y);
|
||||
ctx.translate(-center.x, -center.y);
|
||||
}
|
||||
|
||||
callback(ctx);
|
||||
|
||||
if (center && (radian > 0 || radian < 0)) {
|
||||
ctx.translate(center.x, center.y);
|
||||
ctx.rotate(- radian);
|
||||
ctx.translate(- center.x, - center.y);
|
||||
ctx.rotate(-radian);
|
||||
ctx.translate(-center.x, -center.y);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
rotateContext,
|
||||
rotateElement,
|
||||
};
|
||||
export { rotateContext, rotateElement };
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import { TypeElement, TypeElemDesc, TypeElementBase } from '@idraw/types';
|
||||
import { DataElement, DataElemDesc, DataElementBase } from '@idraw/types';
|
||||
import { deepClone, createUUID } from '@idraw/util';
|
||||
import { _data, _element, _engine, _draw, _emitChangeData } from './../names';
|
||||
import { diffElementResourceChange } from './../lib/diff';
|
||||
import Core from './../index';
|
||||
import { Mode } from './../constant/static';
|
||||
import { diffElementResourceChange } from '../lib/diff';
|
||||
import Core from '../index';
|
||||
import { Mode } from '../constant/static';
|
||||
|
||||
export function getSelectedElements(
|
||||
core: Core
|
||||
): TypeElement<keyof TypeElemDesc>[] {
|
||||
const elems: TypeElement<keyof TypeElemDesc>[] = [];
|
||||
): DataElement<keyof DataElemDesc>[] {
|
||||
const elems: DataElement<keyof DataElemDesc>[] = [];
|
||||
let list: string[] = [];
|
||||
const uuid = core[_engine].temp.get('selectedUUID');
|
||||
const uuid = core.getEngine().temp.get('selectedUUID');
|
||||
if (typeof uuid === 'string' && uuid) {
|
||||
list.push(uuid);
|
||||
} else {
|
||||
list = core[_engine].temp.get('selectedUUIDList');
|
||||
list = core.getEngine().temp.get('selectedUUIDList');
|
||||
}
|
||||
list.forEach((uuid) => {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(uuid);
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(uuid);
|
||||
if (index !== null && index >= 0) {
|
||||
const elem = core[_data]?.elements[index];
|
||||
const elem = core.$data.elements[index];
|
||||
if (elem) elems.push(elem);
|
||||
}
|
||||
});
|
||||
|
|
@ -29,11 +28,11 @@ export function getSelectedElements(
|
|||
export function getElement(
|
||||
core: Core,
|
||||
uuid: string
|
||||
): TypeElement<keyof TypeElemDesc> | null {
|
||||
let elem: TypeElement<keyof TypeElemDesc> | null = null;
|
||||
const index = core[_engine].helper.getElementIndexByUUID(uuid);
|
||||
if (index !== null && core[_data].elements[index]) {
|
||||
elem = deepClone(core[_data].elements[index]);
|
||||
): DataElement<keyof DataElemDesc> | null {
|
||||
let elem: DataElement<keyof DataElemDesc> | null = null;
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(uuid);
|
||||
if (index !== null && core.$data.elements[index]) {
|
||||
elem = deepClone(core.$data.elements[index]);
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
|
@ -41,20 +40,20 @@ export function getElement(
|
|||
export function getElementByIndex(
|
||||
core: Core,
|
||||
index: number
|
||||
): TypeElement<keyof TypeElemDesc> | null {
|
||||
let elem: TypeElement<keyof TypeElemDesc> | null = null;
|
||||
if (index >= 0 && core[_data].elements[index]) {
|
||||
elem = deepClone(core[_data].elements[index]);
|
||||
): DataElement<keyof DataElemDesc> | null {
|
||||
let elem: DataElement<keyof DataElemDesc> | null = null;
|
||||
if (index >= 0 && core.$data.elements[index]) {
|
||||
elem = deepClone(core.$data.elements[index]);
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
export function updateElement(
|
||||
core: Core,
|
||||
elem: TypeElement<keyof TypeElemDesc>
|
||||
elem: DataElement<keyof DataElemDesc>
|
||||
) {
|
||||
const _elem = deepClone(elem) as TypeElement<keyof TypeElemDesc>;
|
||||
const data = core[_data];
|
||||
const _elem = deepClone(elem) as DataElement<keyof DataElemDesc>;
|
||||
const data = core.getData();
|
||||
const resourceChangeUUIDs: string[] = [];
|
||||
for (let i = 0; i < data.elements.length; i++) {
|
||||
if (_elem.uuid === data.elements[i]?.uuid) {
|
||||
|
|
@ -66,110 +65,106 @@ export function updateElement(
|
|||
break;
|
||||
}
|
||||
}
|
||||
core[_emitChangeData]();
|
||||
core[_draw]({ resourceChangeUUIDs });
|
||||
core.$emitChangeData();
|
||||
core.$draw({ resourceChangeUUIDs });
|
||||
}
|
||||
|
||||
export function selectElementByIndex(core: Core, index: number): void {
|
||||
if (core[_data].elements[index]) {
|
||||
const uuid = core[_data].elements[index].uuid;
|
||||
core[_engine].temp.set('mode', Mode.NULL);
|
||||
if (core.$data.elements[index]) {
|
||||
const uuid = core.$data.elements[index].uuid;
|
||||
core.getEngine().temp.set('mode', Mode.NULL);
|
||||
if (typeof uuid === 'string') {
|
||||
core[_engine].temp.set('selectedUUID', uuid);
|
||||
core[_engine].temp.set('selectedUUIDList', []);
|
||||
core.getEngine().temp.set('selectedUUID', uuid);
|
||||
core.getEngine().temp.set('selectedUUIDList', []);
|
||||
}
|
||||
core[_draw]();
|
||||
core.$draw();
|
||||
}
|
||||
}
|
||||
|
||||
export function selectElement(core: Core, uuid: string): void {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(uuid);
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(uuid);
|
||||
if (typeof index === 'number' && index >= 0) {
|
||||
core.selectElementByIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
export function cancelElementByIndex(core: Core, index: number): void {
|
||||
if (core[_data].elements[index]) {
|
||||
const uuid = core[_data].elements[index].uuid;
|
||||
const selectedUUID = core[_engine].temp.get('selectedUUID');
|
||||
if (core.$data.elements[index]) {
|
||||
const uuid = core.$data.elements[index].uuid;
|
||||
const selectedUUID = core.getEngine().temp.get('selectedUUID');
|
||||
if (typeof uuid === 'string' && uuid === selectedUUID) {
|
||||
core[_engine].temp.set('mode', Mode.NULL);
|
||||
core[_engine].temp.set('selectedUUID', null);
|
||||
core[_engine].temp.set('selectedUUIDList', []);
|
||||
core.getEngine().temp.set('mode', Mode.NULL);
|
||||
core.getEngine().temp.set('selectedUUID', null);
|
||||
core.getEngine().temp.set('selectedUUIDList', []);
|
||||
}
|
||||
core[_draw]();
|
||||
core.$draw();
|
||||
}
|
||||
}
|
||||
|
||||
export function cancelElement(
|
||||
core: Core,
|
||||
uuid: string,
|
||||
opts?: { useMode?: boolean }
|
||||
): void {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(uuid);
|
||||
export function cancelElement(core: Core, uuid: string): void {
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(uuid);
|
||||
if (typeof index === 'number' && index >= 0) {
|
||||
core.cancelElementByIndex(index, opts);
|
||||
core.cancelElementByIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
export function moveUpElement(core: Core, uuid: string): void {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(uuid);
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(uuid);
|
||||
if (
|
||||
typeof index === 'number' &&
|
||||
index >= 0 &&
|
||||
index < core[_data].elements.length - 1
|
||||
index < core.$data.elements.length - 1
|
||||
) {
|
||||
const temp = core[_data].elements[index];
|
||||
core[_data].elements[index] = core[_data].elements[index + 1];
|
||||
core[_data].elements[index + 1] = temp;
|
||||
const temp = core.$data.elements[index];
|
||||
core.$data.elements[index] = core.$data.elements[index + 1];
|
||||
core.$data.elements[index + 1] = temp;
|
||||
}
|
||||
core[_emitChangeData]();
|
||||
core[_draw]();
|
||||
core.$emitChangeData();
|
||||
core.$draw();
|
||||
}
|
||||
|
||||
export function moveDownElement(core: Core, uuid: string): void {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(uuid);
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(uuid);
|
||||
if (
|
||||
typeof index === 'number' &&
|
||||
index > 0 &&
|
||||
index < core[_data].elements.length
|
||||
index < core.$data.elements.length
|
||||
) {
|
||||
const temp = core[_data].elements[index];
|
||||
core[_data].elements[index] = core[_data].elements[index - 1];
|
||||
core[_data].elements[index - 1] = temp;
|
||||
const temp = core.$data.elements[index];
|
||||
core.$data.elements[index] = core.$data.elements[index - 1];
|
||||
core.$data.elements[index - 1] = temp;
|
||||
}
|
||||
core[_emitChangeData]();
|
||||
core[_draw]();
|
||||
core.$emitChangeData();
|
||||
core.$draw();
|
||||
}
|
||||
|
||||
export function addElement(
|
||||
core: Core,
|
||||
elem: TypeElementBase<keyof TypeElemDesc>
|
||||
elem: DataElementBase<keyof DataElemDesc>
|
||||
): string | null {
|
||||
const _elem = deepClone(elem);
|
||||
_elem.uuid = createUUID();
|
||||
core[_data].elements.push(_elem);
|
||||
core[_emitChangeData]();
|
||||
core[_draw]();
|
||||
core.$data.elements.push(_elem);
|
||||
core.$emitChangeData();
|
||||
core.$draw();
|
||||
return _elem.uuid;
|
||||
}
|
||||
|
||||
export function deleteElement(core: Core, uuid: string) {
|
||||
const index = core[_element].getElementIndex(core[_data], uuid);
|
||||
const index = core.$getElementHandler().getElementIndex(core.getData(), uuid);
|
||||
if (index >= 0) {
|
||||
core[_data].elements.splice(index, 1);
|
||||
core[_emitChangeData]();
|
||||
core[_draw]();
|
||||
core.$data.elements.splice(index, 1);
|
||||
core.$emitChangeData();
|
||||
core.$draw();
|
||||
}
|
||||
}
|
||||
|
||||
export function insertElementBefore(
|
||||
core: Core,
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
beforeUUID: string
|
||||
) {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(beforeUUID);
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(beforeUUID);
|
||||
if (index !== null) {
|
||||
return core.insertElementBeforeIndex(elem, index);
|
||||
}
|
||||
|
|
@ -178,15 +173,15 @@ export function insertElementBefore(
|
|||
|
||||
export function insertElementBeforeIndex(
|
||||
core: Core,
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
index: number
|
||||
) {
|
||||
const _elem = deepClone(elem);
|
||||
_elem.uuid = createUUID();
|
||||
if (index >= 0) {
|
||||
core[_data].elements.splice(index, 0, _elem);
|
||||
core[_emitChangeData]();
|
||||
core[_draw]();
|
||||
core.$data.elements.splice(index, 0, _elem);
|
||||
core.$emitChangeData();
|
||||
core.$draw();
|
||||
return _elem.uuid;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -194,10 +189,10 @@ export function insertElementBeforeIndex(
|
|||
|
||||
export function insertElementAfter(
|
||||
core: Core,
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
beforeUUID: string
|
||||
) {
|
||||
const index = core[_engine].helper.getElementIndexByUUID(beforeUUID);
|
||||
const index = core.getEngine().helper.getElementIndexByUUID(beforeUUID);
|
||||
if (index !== null) {
|
||||
return core.insertElementAfterIndex(elem, index);
|
||||
}
|
||||
|
|
@ -206,15 +201,15 @@ export function insertElementAfter(
|
|||
|
||||
export function insertElementAfterIndex(
|
||||
core: Core,
|
||||
elem: TypeElementBase<keyof TypeElemDesc>,
|
||||
elem: DataElementBase<keyof DataElemDesc>,
|
||||
index: number
|
||||
) {
|
||||
const _elem = deepClone(elem);
|
||||
_elem.uuid = createUUID();
|
||||
if (index >= 0) {
|
||||
core[_data].elements.splice(index + 1, 0, _elem);
|
||||
core[_emitChangeData]();
|
||||
core[_draw]();
|
||||
core.$data.elements.splice(index + 1, 0, _elem);
|
||||
core.$emitChangeData();
|
||||
core.$draw();
|
||||
return _elem.uuid;
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
const _board = Symbol('_board');
|
||||
const _data = Symbol('_data');
|
||||
const _opts = Symbol('_opts');
|
||||
const _config = Symbol('_config');
|
||||
const _renderer = Symbol('_renderer');
|
||||
const _element = Symbol('_element');
|
||||
const _helper = Symbol('_helper');
|
||||
const _tempData = Symbol('_tempData');
|
||||
const _draw = Symbol('_draw');
|
||||
const _coreEvent = Symbol('_coreEvent');
|
||||
const _mapper = Symbol('_mapper');
|
||||
const _emitChangeScreen = Symbol('_emitChangeScreen');
|
||||
const _emitChangeData = Symbol('_emitChangeData');
|
||||
const _engine = Symbol('_engine');
|
||||
|
||||
export {
|
||||
_board, _data, _opts, _config, _renderer, _element, _helper,
|
||||
_tempData, _draw, _coreEvent, _mapper,
|
||||
_emitChangeScreen, _emitChangeData, _engine,
|
||||
};
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import {
|
||||
InterfaceHelperPlugin, TypeHelperPluginEventDetail,
|
||||
TypeHelperPluginEventResult,
|
||||
InterfaceHelperPlugin,
|
||||
HelperPluginEventDetail,
|
||||
HelperPluginEventResult
|
||||
} from '@idraw/types';
|
||||
import { createUUID } from '@idraw/util';
|
||||
|
||||
|
||||
export class HelperPlugin implements Required<InterfaceHelperPlugin> {
|
||||
|
||||
readonly name: string = 'helper-plugin';
|
||||
|
||||
readonly uuid: string;
|
||||
|
|
@ -16,31 +15,20 @@ export class HelperPlugin implements Required<InterfaceHelperPlugin> {
|
|||
this.uuid = createUUID();
|
||||
}
|
||||
|
||||
onHover(detail: TypeHelperPluginEventDetail): void | TypeHelperPluginEventResult {
|
||||
onHover(detail: HelperPluginEventDetail): void | HelperPluginEventResult {
|
||||
if (detail.controller === null) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPoint(detail: TypeHelperPluginEventDetail): void | TypeHelperPluginEventResult {
|
||||
|
||||
}
|
||||
onPoint(detail: HelperPluginEventDetail): void | HelperPluginEventResult {}
|
||||
|
||||
onClick(detail: TypeHelperPluginEventDetail): void | TypeHelperPluginEventResult {
|
||||
|
||||
}
|
||||
onClick(detail: HelperPluginEventDetail): void | HelperPluginEventResult {}
|
||||
|
||||
onMoveStart(detail: TypeHelperPluginEventDetail): void | TypeHelperPluginEventResult {
|
||||
|
||||
}
|
||||
onMoveStart(
|
||||
detail: HelperPluginEventDetail
|
||||
): void | HelperPluginEventResult {}
|
||||
|
||||
onMove(detail: TypeHelperPluginEventDetail): void | TypeHelperPluginEventResult {
|
||||
|
||||
onMove(detail: HelperPluginEventDetail): void | HelperPluginEventResult {}
|
||||
|
||||
}
|
||||
|
||||
onMoveEnd(detail: TypeHelperPluginEventDetail): void | TypeHelperPluginEventResult {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
onMoveEnd(detail: HelperPluginEventDetail): void | HelperPluginEventResult {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,16 @@ function delay(time: number): Promise<void> {
|
|||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, time)
|
||||
}, time);
|
||||
});
|
||||
}
|
||||
|
||||
describe("idraw", () => {
|
||||
|
||||
describe('idraw', () => {
|
||||
beforeEach(() => {
|
||||
requestAnimationFrameMock.reset();
|
||||
})
|
||||
});
|
||||
|
||||
test('context', async () => {
|
||||
test('context', async () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="mount"></div>
|
||||
`;
|
||||
|
|
@ -28,26 +27,26 @@ describe("idraw", () => {
|
|||
contextWidth: 600,
|
||||
contextHeight: 400,
|
||||
devicePixelRatio: 4
|
||||
}
|
||||
};
|
||||
const mount = document.querySelector('#mount') as HTMLDivElement;
|
||||
const idraw = new IDraw(mount, opts);
|
||||
const data = getData();
|
||||
idraw.setData(data, { triggerChangeEvent: true });
|
||||
|
||||
requestAnimationFrameMock.triggerNextAnimationFrame();
|
||||
|
||||
const originCtx = idraw.__getOriginContext2D();
|
||||
|
||||
const originCtx = idraw.$getOriginContext2D();
|
||||
// @ts-ignore;
|
||||
const originCalls = originCtx.__getDrawCalls();
|
||||
expect(originCalls).toMatchSnapshot();
|
||||
|
||||
const displayCtx = idraw.__getDisplayContext2D();
|
||||
|
||||
const displayCtx = idraw.$getDisplayContext2D();
|
||||
// @ts-ignore;
|
||||
const displayCalls = displayCtx.__getDrawCalls();
|
||||
expect(displayCalls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('undo/redo', async () => {
|
||||
test('undo/redo', async () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="mount"></div>
|
||||
`;
|
||||
|
|
@ -57,7 +56,7 @@ describe("idraw", () => {
|
|||
contextWidth: 600,
|
||||
contextHeight: 400,
|
||||
devicePixelRatio: 4
|
||||
}
|
||||
};
|
||||
const mount = document.querySelector('#mount') as HTMLDivElement;
|
||||
const idraw = new IDraw(mount, opts);
|
||||
const data = getData();
|
||||
|
|
@ -69,7 +68,6 @@ describe("idraw", () => {
|
|||
const undo1 = idraw.undo();
|
||||
expect(undo1.doRecordCount).toBe(2);
|
||||
expect(undo1.data?.elements?.length).toBe(4);
|
||||
|
||||
|
||||
const undo2 = idraw.undo();
|
||||
expect(undo2.doRecordCount).toBe(1);
|
||||
|
|
@ -86,20 +84,15 @@ describe("idraw", () => {
|
|||
expect(redo2.data).toBe(null);
|
||||
|
||||
requestAnimationFrameMock.triggerNextAnimationFrame();
|
||||
|
||||
const originCtx = idraw.__getOriginContext2D();
|
||||
|
||||
const originCtx = idraw.$getOriginContext2D();
|
||||
// @ts-ignore;
|
||||
const originCalls = originCtx.__getDrawCalls();
|
||||
expect(originCalls).toMatchSnapshot();
|
||||
|
||||
const displayCtx = idraw.__getDisplayContext2D();
|
||||
|
||||
const displayCtx = idraw.$getDisplayContext2D();
|
||||
// @ts-ignore;
|
||||
const displayCalls = displayCtx.__getDrawCalls();
|
||||
expect(displayCalls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { TypeDataBase } from '@idraw/types';
|
||||
import type { IDrawDataBase } from '@idraw/types';
|
||||
|
||||
const data: TypeDataBase = {
|
||||
const data: IDrawDataBase = {
|
||||
bgColor: '#ffffff',
|
||||
elements: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
import Core from '@idraw/core';
|
||||
import { TypeData, TypeConfig } from '@idraw/types';
|
||||
import { IDrawData, IDrawConfig } from '@idraw/types';
|
||||
import { Options, PrivateOptions } from './types';
|
||||
import { defaultOptions } from './config';
|
||||
import { TempData } from './lib/temp';
|
||||
import { KeyboardWatcher } from './lib/keyboard-watcher';
|
||||
import {
|
||||
_opts,
|
||||
_hasInited,
|
||||
_initEvent,
|
||||
_tempData,
|
||||
_createOpts,
|
||||
_pushRecord,
|
||||
_keyboardWatcher
|
||||
} from './names';
|
||||
|
||||
import { redo, undo } from './mixins/record';
|
||||
import { exportDataURL, toDataURL } from './mixins/file';
|
||||
import {
|
||||
|
|
@ -29,14 +21,14 @@ import {
|
|||
// import { version } from './../package.json';
|
||||
|
||||
export default class iDraw extends Core {
|
||||
private [_opts]: PrivateOptions;
|
||||
private [_hasInited] = false;
|
||||
private [_tempData] = new TempData();
|
||||
private [_keyboardWatcher] = new KeyboardWatcher();
|
||||
private _opts: PrivateOptions;
|
||||
private _hasInited = false;
|
||||
private _tempData = new TempData();
|
||||
private _keyboardWatcher = new KeyboardWatcher();
|
||||
|
||||
// static version = version;
|
||||
|
||||
constructor(mount: HTMLDivElement, opts: Options, config?: TypeConfig) {
|
||||
constructor(mount: HTMLDivElement, opts: Options, config?: IDrawConfig) {
|
||||
super(
|
||||
mount,
|
||||
{
|
||||
|
|
@ -50,15 +42,15 @@ export default class iDraw extends Core {
|
|||
},
|
||||
config || {}
|
||||
);
|
||||
this[_opts] = this[_createOpts](opts);
|
||||
this[_initEvent]();
|
||||
this._opts = this._createOpts(opts);
|
||||
this._initEvent();
|
||||
}
|
||||
|
||||
undo(): { doRecordCount: number; data: TypeData | null } {
|
||||
undo(): { doRecordCount: number; data: IDrawData | null } {
|
||||
return undo(this);
|
||||
}
|
||||
|
||||
redo(): { undoRecordCount: number; data: TypeData | null } {
|
||||
redo(): { undoRecordCount: number; data: IDrawData | null } {
|
||||
return redo(this);
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +58,10 @@ export default class iDraw extends Core {
|
|||
return toDataURL(this, type, quality);
|
||||
}
|
||||
|
||||
getTempData() {
|
||||
return this._tempData;
|
||||
}
|
||||
|
||||
async exportDataURL(
|
||||
type: 'image/png' | 'image/jpeg',
|
||||
quality?: number
|
||||
|
|
@ -73,21 +69,21 @@ export default class iDraw extends Core {
|
|||
return exportDataURL(this, type, quality);
|
||||
}
|
||||
|
||||
private [_initEvent]() {
|
||||
if (this[_hasInited] === true) {
|
||||
private _initEvent() {
|
||||
if (this._hasInited === true) {
|
||||
return;
|
||||
}
|
||||
this.on('changeData', (data: TypeData) => {
|
||||
this[_pushRecord](data);
|
||||
this.on('changeData', (data: IDrawData) => {
|
||||
this._pushRecord(data);
|
||||
});
|
||||
this.on('mouseLeaveScreen', () => {
|
||||
this[_tempData].set('isFocus', false);
|
||||
this._tempData.set('isFocus', false);
|
||||
});
|
||||
this.on('mouseOverScreen', () => {
|
||||
this[_tempData].set('isFocus', true);
|
||||
this._tempData.set('isFocus', true);
|
||||
});
|
||||
if (this[_opts].disableKeyboard === false) {
|
||||
this[_keyboardWatcher]
|
||||
if (this._opts.disableKeyboard === false) {
|
||||
this._keyboardWatcher
|
||||
.on('keyboardCopy', () => copyElements(this))
|
||||
.on('keyboardPaste', () => pasteElements(this))
|
||||
.on('keyboardCut', () => cutElements(this))
|
||||
|
|
@ -98,20 +94,20 @@ export default class iDraw extends Core {
|
|||
.on('keyboardArrowRight', () => keyArrowRight(this))
|
||||
.on('keyboardUndo', () => keyUndo(this));
|
||||
}
|
||||
this[_hasInited] = true;
|
||||
this._hasInited = true;
|
||||
}
|
||||
|
||||
private [_pushRecord](data: TypeData) {
|
||||
const doRecords = this[_tempData].get('doRecords');
|
||||
if (doRecords.length >= this[_opts].maxRecords) {
|
||||
private _pushRecord(data: IDrawData) {
|
||||
const doRecords = this._tempData.get('doRecords');
|
||||
if (doRecords.length >= this._opts.maxRecords) {
|
||||
doRecords.shift();
|
||||
}
|
||||
doRecords.push({ data, time: Date.now() });
|
||||
this[_tempData].set('doRecords', doRecords);
|
||||
this[_tempData].set('unDoRecords', []);
|
||||
this._tempData.set('doRecords', doRecords);
|
||||
this._tempData.set('unDoRecords', []);
|
||||
}
|
||||
|
||||
private [_createOpts](opts: Options): PrivateOptions {
|
||||
private _createOpts(opts: Options): PrivateOptions {
|
||||
return { ...{}, ...defaultOptions, ...opts };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { TypeElemDesc, TypeElement } from '@idraw/types';
|
||||
import { DataElemDesc, DataElement } from '@idraw/types';
|
||||
import { Record } from './../types';
|
||||
|
||||
|
||||
type TempDataDesc = {
|
||||
isDownloading: boolean;
|
||||
isFocus: boolean,
|
||||
doRecords: Record[],
|
||||
unDoRecords: Record[],
|
||||
clipboardElements: TypeElement<keyof TypeElemDesc>[]
|
||||
}
|
||||
isFocus: boolean;
|
||||
doRecords: Record[];
|
||||
unDoRecords: Record[];
|
||||
clipboardElements: DataElement<keyof DataElemDesc>[];
|
||||
};
|
||||
|
||||
function createDefaultData() {
|
||||
return {
|
||||
|
|
@ -16,27 +15,26 @@ function createDefaultData() {
|
|||
doRecords: [],
|
||||
unDoRecords: [],
|
||||
clipboardElements: [],
|
||||
isDownloading: false,
|
||||
}
|
||||
isDownloading: false
|
||||
};
|
||||
}
|
||||
|
||||
export class TempData {
|
||||
|
||||
private _temp: TempDataDesc
|
||||
private _temp: TempDataDesc;
|
||||
|
||||
constructor() {
|
||||
this._temp = createDefaultData();
|
||||
}
|
||||
|
||||
set<T extends keyof TempDataDesc >(name: T, value: TempDataDesc[T]) {
|
||||
set<T extends keyof TempDataDesc>(name: T, value: TempDataDesc[T]) {
|
||||
this._temp[name] = value;
|
||||
}
|
||||
|
||||
get<T extends keyof TempDataDesc >(name: T): TempDataDesc[T] {
|
||||
get<T extends keyof TempDataDesc>(name: T): TempDataDesc[T] {
|
||||
return this._temp[name];
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._temp = createDefaultData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { _tempData } from '../names';
|
||||
import iDraw from './../index';
|
||||
|
||||
export async function exportDataURL(
|
||||
|
|
@ -6,17 +5,17 @@ export async function exportDataURL(
|
|||
type: 'image/png' | 'image/jpeg',
|
||||
quality?: number
|
||||
): Promise<string> {
|
||||
if (idraw[_tempData].get('isDownloading') === true) {
|
||||
if (idraw.getTempData().get('isDownloading') === true) {
|
||||
return Promise.reject('Busy!');
|
||||
}
|
||||
|
||||
idraw[_tempData].set('isDownloading', true);
|
||||
idraw.getTempData().set('isDownloading', true);
|
||||
return new Promise((resolve, reject) => {
|
||||
let dataURL: string = '';
|
||||
let dataURL = '';
|
||||
function listenRenderFrameComplete() {
|
||||
idraw.off('drawFrameComplete', listenRenderFrameComplete);
|
||||
idraw[_tempData].set('isDownloading', false);
|
||||
const ctx = idraw.__getOriginContext2D();
|
||||
idraw.getTempData().set('isDownloading', false);
|
||||
const ctx = idraw.$getOriginContext2D();
|
||||
const canvas = ctx.canvas;
|
||||
dataURL = canvas.toDataURL(type, quality);
|
||||
resolve(dataURL);
|
||||
|
|
@ -30,15 +29,13 @@ export async function exportDataURL(
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function toDataURL(
|
||||
idraw: iDraw,
|
||||
type: 'image/png' | 'image/jpeg',
|
||||
quality?: number
|
||||
): string {
|
||||
const ctx = idraw.__getOriginContext2D();
|
||||
const ctx = idraw.$getOriginContext2D();
|
||||
const canvas = ctx.canvas;
|
||||
const dataURL: string = canvas.toDataURL(type, quality);
|
||||
return dataURL;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,56 @@
|
|||
import { deepClone } from '@idraw/util';
|
||||
import { TypeElement, TypeElemDesc } from '@idraw/types';
|
||||
import { DataElement, DataElemDesc } from '@idraw/types';
|
||||
import iDraw from './../index';
|
||||
import { _tempData } from './../names';
|
||||
|
||||
export function copyElements(idraw: iDraw) {
|
||||
if (idraw[_tempData].get('isFocus') !== true) {
|
||||
if (idraw.getTempData().get('isFocus') !== true) {
|
||||
return;
|
||||
}
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
idraw[_tempData].set('clipboardElements', elems);
|
||||
idraw.getTempData().set('clipboardElements', elems);
|
||||
}
|
||||
|
||||
export function pasteElements(idraw: iDraw) {
|
||||
if (idraw[_tempData].get('isFocus') !== true) {
|
||||
if (idraw.getTempData().get('isFocus') !== true) {
|
||||
return;
|
||||
}
|
||||
const elems = idraw[_tempData].get('clipboardElements');
|
||||
const elems = idraw.getTempData().get('clipboardElements');
|
||||
const moveRate = 0.1;
|
||||
elems.forEach((elem) => {
|
||||
elem.x += elem.w * moveRate;
|
||||
elem.y += elem.w * moveRate;
|
||||
idraw.addElement(elem);
|
||||
});
|
||||
idraw[_tempData].set('clipboardElements', []);
|
||||
idraw.getTempData().set('clipboardElements', []);
|
||||
}
|
||||
|
||||
export function cutElements(idraw: iDraw) {
|
||||
if (idraw[_tempData].get('isFocus') !== true) {
|
||||
if (idraw.getTempData().get('isFocus') !== true) {
|
||||
return;
|
||||
}
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
elems.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
elems.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
idraw.deleteElement(elem.uuid);
|
||||
})
|
||||
idraw[_tempData].set('clipboardElements', elems);
|
||||
});
|
||||
idraw.getTempData().set('clipboardElements', elems);
|
||||
}
|
||||
|
||||
export function deleteElements(idraw: iDraw) {
|
||||
if (idraw[_tempData].get('isFocus') !== true) {
|
||||
if (idraw.getTempData().get('isFocus') !== true) {
|
||||
return;
|
||||
}
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
elems.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
elems.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
idraw.deleteElement(elem.uuid);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const keyArrowMoveDistance = 4;
|
||||
|
||||
export function keyArrowUp(idraw: iDraw) {
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
if (elems.length > 0) {
|
||||
elems.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
elems.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
elem.y -= keyArrowMoveDistance;
|
||||
idraw.updateElement(elem);
|
||||
});
|
||||
|
|
@ -65,7 +63,7 @@ export function keyArrowUp(idraw: iDraw) {
|
|||
export function keyArrowDown(idraw: iDraw) {
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
if (elems.length > 0) {
|
||||
elems.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
elems.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
elem.y += keyArrowMoveDistance;
|
||||
idraw.updateElement(elem);
|
||||
});
|
||||
|
|
@ -78,7 +76,7 @@ export function keyArrowDown(idraw: iDraw) {
|
|||
export function keyArrowLeft(idraw: iDraw) {
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
if (elems.length > 0) {
|
||||
elems.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
elems.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
elem.x -= keyArrowMoveDistance;
|
||||
idraw.updateElement(elem);
|
||||
});
|
||||
|
|
@ -91,7 +89,7 @@ export function keyArrowLeft(idraw: iDraw) {
|
|||
export function keyArrowRight(idraw: iDraw) {
|
||||
const elems = deepClone(idraw.getSelectedElements());
|
||||
if (elems.length > 0) {
|
||||
elems.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
elems.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
elem.x += keyArrowMoveDistance;
|
||||
idraw.updateElement(elem);
|
||||
});
|
||||
|
|
@ -103,4 +101,4 @@ export function keyArrowRight(idraw: iDraw) {
|
|||
|
||||
export function keyUndo(idraw: iDraw) {
|
||||
idraw.undo();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import { TypeData } from '@idraw/types';
|
||||
import { IDrawData } from '@idraw/types';
|
||||
import iDraw from './../index';
|
||||
import { _tempData } from './../names';
|
||||
|
||||
export function undo(idraw: iDraw): {
|
||||
doRecordCount: number,
|
||||
data: TypeData | null,
|
||||
doRecordCount: number;
|
||||
data: IDrawData | null;
|
||||
} {
|
||||
const doRecords = idraw[_tempData].get('doRecords');
|
||||
const unDoRecords = idraw[_tempData].get('unDoRecords');
|
||||
const doRecords = idraw.getTempData().get('doRecords');
|
||||
const unDoRecords = idraw.getTempData().get('unDoRecords');
|
||||
if (!(doRecords.length > 1)) {
|
||||
return {
|
||||
doRecordCount: doRecords.length,
|
||||
data: null,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
const popRecord = doRecords.pop();
|
||||
|
|
@ -22,32 +21,32 @@ export function undo(idraw: iDraw): {
|
|||
if (record?.data) {
|
||||
idraw.setData(record.data);
|
||||
}
|
||||
idraw[_tempData].set('doRecords', doRecords);
|
||||
idraw[_tempData].set('unDoRecords', unDoRecords);
|
||||
idraw.getTempData().set('doRecords', doRecords);
|
||||
idraw.getTempData().set('unDoRecords', unDoRecords);
|
||||
return {
|
||||
doRecordCount: doRecords.length,
|
||||
data: record?.data || null,
|
||||
data: record?.data || null
|
||||
};
|
||||
}
|
||||
|
||||
export function redo(idraw: iDraw): {
|
||||
undoRecordCount: number,
|
||||
data: TypeData | null,
|
||||
undoRecordCount: number;
|
||||
data: IDrawData | null;
|
||||
} {
|
||||
const unDoRecords = idraw[_tempData].get('unDoRecords');
|
||||
const unDoRecords = idraw.getTempData().get('unDoRecords');
|
||||
if (!(unDoRecords.length > 0)) {
|
||||
return {
|
||||
undoRecordCount: unDoRecords.length,
|
||||
data: null,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
const record = unDoRecords.pop();
|
||||
if (record?.data) {
|
||||
idraw.setData(record.data);
|
||||
}
|
||||
idraw[_tempData].set('unDoRecords', unDoRecords);
|
||||
idraw.getTempData().set('unDoRecords', unDoRecords);
|
||||
return {
|
||||
undoRecordCount: unDoRecords.length,
|
||||
data: record?.data || null,
|
||||
data: record?.data || null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
const _opts = Symbol('_opts');
|
||||
const _hasInited = Symbol('_hasInited');
|
||||
const _initEvent = Symbol('_initEvent');
|
||||
const _tempData = Symbol('_tempData');
|
||||
const _createOpts = Symbol('_createOpts');
|
||||
const _pushRecord = Symbol('_pushRecord');
|
||||
const _keyboardWatcher = Symbol('_keyboardWatcher');
|
||||
|
||||
export {
|
||||
_opts, _hasInited, _initEvent, _tempData,
|
||||
_createOpts, _pushRecord, _keyboardWatcher,
|
||||
}
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
import {
|
||||
TypeData,
|
||||
TypeCoreOptions,
|
||||
} from '@idraw/types';
|
||||
import { IDrawData, CoreOptions } from '@idraw/types';
|
||||
|
||||
export type Options = {
|
||||
maxRecords?: number;
|
||||
disableKeyboard?: boolean;
|
||||
} & TypeCoreOptions;
|
||||
} & CoreOptions;
|
||||
|
||||
export type PrivateOptions = {
|
||||
maxRecords: number;
|
||||
|
|
@ -14,6 +11,6 @@ export type PrivateOptions = {
|
|||
} & Options;
|
||||
|
||||
export type Record = {
|
||||
data: TypeData;
|
||||
data: IDrawData;
|
||||
time: number;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
import { TypeData, TypeContext, TypeElement, TypeElemDesc } from '@idraw/types';
|
||||
import {
|
||||
IDrawData,
|
||||
IDrawContext,
|
||||
DataElement,
|
||||
DataElemDesc
|
||||
} from '@idraw/types';
|
||||
import { createUUID, deepClone, Context } from '@idraw/util';
|
||||
import { drawContext } from './lib/draw';
|
||||
import { TypeLoadDataItem } from './lib/loader-event';
|
||||
import Loader from './lib/loader';
|
||||
import { RendererEvent } from './lib/renderer-event';
|
||||
import {
|
||||
_queue,
|
||||
_ctx,
|
||||
_status,
|
||||
_loader,
|
||||
_opts,
|
||||
_freeze,
|
||||
_drawFrame,
|
||||
_retainQueueOneItem
|
||||
} from './names';
|
||||
|
||||
const { requestAnimationFrame } = window;
|
||||
|
||||
type QueueItem = { data: TypeData };
|
||||
type QueueItem = { data: IDrawData };
|
||||
enum DrawStatus {
|
||||
NULL = 'null',
|
||||
FREE = 'free',
|
||||
|
|
@ -35,68 +30,68 @@ type Options = {
|
|||
};
|
||||
|
||||
export default class Renderer extends RendererEvent {
|
||||
private [_queue]: QueueItem[] = [];
|
||||
private [_ctx]: TypeContext | null = null;
|
||||
private [_status]: DrawStatus = DrawStatus.NULL;
|
||||
private [_loader]: Loader;
|
||||
private [_opts]?: Options;
|
||||
private _queue: QueueItem[] = [];
|
||||
private _ctx: IDrawContext | null = null;
|
||||
private _status: DrawStatus = DrawStatus.NULL;
|
||||
private _loader: Loader;
|
||||
private _opts?: Options;
|
||||
|
||||
constructor(opts?: Options) {
|
||||
super();
|
||||
this[_opts] = opts;
|
||||
this[_loader] = new Loader({
|
||||
this._opts = opts;
|
||||
this._loader = new Loader({
|
||||
maxParallelNum: 6
|
||||
});
|
||||
this[_loader].on('load', (res: TypeLoadDataItem) => {
|
||||
this[_drawFrame]();
|
||||
this._loader.on('load', (res: TypeLoadDataItem) => {
|
||||
this._drawFrame();
|
||||
this.trigger('load', { element: res.element });
|
||||
});
|
||||
this[_loader].on('error', (res: TypeLoadDataItem) => {
|
||||
this._loader.on('error', (res: TypeLoadDataItem) => {
|
||||
this.trigger('error', { element: res.element, error: res.error });
|
||||
});
|
||||
this[_loader].on('complete', () => {
|
||||
this._loader.on('complete', () => {
|
||||
this.trigger('loadComplete', { t: Date.now() });
|
||||
});
|
||||
}
|
||||
|
||||
render(
|
||||
target: HTMLCanvasElement | TypeContext,
|
||||
originData: TypeData,
|
||||
target: HTMLCanvasElement | IDrawContext,
|
||||
originData: IDrawData,
|
||||
opts?: {
|
||||
// forceUpdate?: boolean,
|
||||
changeResourceUUIDs?: string[];
|
||||
}
|
||||
): void {
|
||||
// if ([DrawStatus.STOP, DrawStatus.FREEZE].includes(this[_status])) {
|
||||
// if ([DrawStatus.STOP, DrawStatus.FREEZE].includes(this._status)) {
|
||||
// return;
|
||||
// }
|
||||
// this[_status] = DrawStatus.FREE;
|
||||
// this._status = DrawStatus.FREE;
|
||||
|
||||
const { changeResourceUUIDs = [] } = opts || {};
|
||||
this[_status] = DrawStatus.FREE;
|
||||
this._status = DrawStatus.FREE;
|
||||
|
||||
const data = deepClone(originData);
|
||||
if (Array.isArray(data.elements)) {
|
||||
data.elements.forEach((elem: TypeElement<keyof TypeElemDesc>) => {
|
||||
data.elements.forEach((elem: DataElement<keyof DataElemDesc>) => {
|
||||
if (!(typeof elem.uuid === 'string' && elem.uuid)) {
|
||||
elem.uuid = createUUID();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!this[_ctx]) {
|
||||
if (!this._ctx) {
|
||||
// TODO
|
||||
if (
|
||||
this[_opts] &&
|
||||
this._opts &&
|
||||
Object.prototype.toString.call(target) === '[object HTMLCanvasElement]'
|
||||
) {
|
||||
const { width, height, contextWidth, contextHeight, devicePixelRatio } =
|
||||
this[_opts] as Options;
|
||||
this._opts as Options;
|
||||
const canvas = target as HTMLCanvasElement;
|
||||
canvas.width = width * devicePixelRatio;
|
||||
canvas.height = height * devicePixelRatio;
|
||||
const ctx2d = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
this[_ctx] = new Context(ctx2d, {
|
||||
this._ctx = new Context(ctx2d, {
|
||||
width,
|
||||
height,
|
||||
contextWidth: contextWidth || width,
|
||||
|
|
@ -105,93 +100,93 @@ export default class Renderer extends RendererEvent {
|
|||
});
|
||||
} else if (target) {
|
||||
// TODO
|
||||
this[_ctx] = target as TypeContext;
|
||||
this._ctx = target as IDrawContext;
|
||||
}
|
||||
}
|
||||
|
||||
if ([DrawStatus.FREEZE].includes(this[_status])) {
|
||||
if ([DrawStatus.FREEZE].includes(this._status)) {
|
||||
return;
|
||||
}
|
||||
const _data: QueueItem = deepClone({ data }) as QueueItem;
|
||||
this[_queue].push(_data);
|
||||
// if (this[_status] !== DrawStatus.DRAWING) {
|
||||
// this[_status] = DrawStatus.DRAWING;
|
||||
// this[_drawFrame]();
|
||||
this._queue.push(_data);
|
||||
// if (this._status !== DrawStatus.DRAWING) {
|
||||
// this._status = DrawStatus.DRAWING;
|
||||
// this._drawFrame();
|
||||
// }
|
||||
this[_drawFrame]();
|
||||
this[_loader].load(data, changeResourceUUIDs || []);
|
||||
this._drawFrame();
|
||||
this._loader.load(data, changeResourceUUIDs || []);
|
||||
}
|
||||
|
||||
getContext(): TypeContext | null {
|
||||
return this[_ctx];
|
||||
getContext(): IDrawContext | null {
|
||||
return this._ctx;
|
||||
}
|
||||
|
||||
thaw() {
|
||||
this[_status] = DrawStatus.FREE;
|
||||
this._status = DrawStatus.FREE;
|
||||
}
|
||||
|
||||
private [_freeze]() {
|
||||
this[_status] = DrawStatus.FREEZE;
|
||||
private _freeze() {
|
||||
this._status = DrawStatus.FREEZE;
|
||||
}
|
||||
|
||||
private [_drawFrame]() {
|
||||
if (this[_status] === DrawStatus.FREEZE) {
|
||||
private _drawFrame() {
|
||||
if (this._status === DrawStatus.FREEZE) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
if (this[_status] === DrawStatus.FREEZE) {
|
||||
if (this._status === DrawStatus.FREEZE) {
|
||||
return;
|
||||
}
|
||||
const ctx = this[_ctx];
|
||||
const ctx = this._ctx;
|
||||
|
||||
let item: QueueItem | undefined = this[_queue][0];
|
||||
let item: QueueItem | undefined = this._queue[0];
|
||||
let isLastFrame = false;
|
||||
if (this[_queue].length > 1) {
|
||||
item = this[_queue].shift();
|
||||
if (this._queue.length > 1) {
|
||||
item = this._queue.shift();
|
||||
} else {
|
||||
isLastFrame = true;
|
||||
}
|
||||
if (this[_loader].isComplete() !== true) {
|
||||
this[_drawFrame]();
|
||||
if (this._loader.isComplete() !== true) {
|
||||
this._drawFrame();
|
||||
if (item && ctx) {
|
||||
drawContext(ctx, item.data, this[_loader]);
|
||||
drawContext(ctx, item.data, this._loader);
|
||||
// this._board.draw();
|
||||
// this.trigger('drawFrame', { t: Date.now() })
|
||||
}
|
||||
} else if (item && ctx) {
|
||||
drawContext(ctx, item.data, this[_loader]);
|
||||
drawContext(ctx, item.data, this._loader);
|
||||
// this._board.draw();
|
||||
// this.trigger('drawFrame', { t: Date.now() })
|
||||
this[_retainQueueOneItem]();
|
||||
this._retainQueueOneItem();
|
||||
if (!isLastFrame) {
|
||||
this[_drawFrame]();
|
||||
this._drawFrame();
|
||||
} else {
|
||||
this[_status] = DrawStatus.FREE;
|
||||
this._status = DrawStatus.FREE;
|
||||
}
|
||||
} else {
|
||||
this[_status] = DrawStatus.FREE;
|
||||
this._status = DrawStatus.FREE;
|
||||
}
|
||||
this.trigger('drawFrame', { t: Date.now() });
|
||||
|
||||
if (
|
||||
this[_loader].isComplete() === true &&
|
||||
this[_queue].length === 1 &&
|
||||
this[_status] === DrawStatus.FREE
|
||||
this._loader.isComplete() === true &&
|
||||
this._queue.length === 1 &&
|
||||
this._status === DrawStatus.FREE
|
||||
) {
|
||||
if (ctx && this[_queue][0] && this[_queue][0].data) {
|
||||
drawContext(ctx, this[_queue][0].data, this[_loader]);
|
||||
if (ctx && this._queue[0] && this._queue[0].data) {
|
||||
drawContext(ctx, this._queue[0].data, this._loader);
|
||||
}
|
||||
this.trigger('drawFrameComplete', { t: Date.now() });
|
||||
this[_freeze]();
|
||||
this._freeze();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private [_retainQueueOneItem]() {
|
||||
if (this[_queue].length <= 1) {
|
||||
private _retainQueueOneItem() {
|
||||
if (this._queue.length <= 1) {
|
||||
return;
|
||||
}
|
||||
const lastOne = deepClone(this[_queue][this[_queue].length - 1]);
|
||||
this[_queue] = [lastOne];
|
||||
const lastOne = deepClone(this._queue[this._queue.length - 1]);
|
||||
this._queue = [lastOne];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,30 @@
|
|||
import {
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
TypePoint,
|
||||
} from '@idraw/types';
|
||||
|
||||
import { DataElement, DataElemDesc, Point } from '@idraw/types';
|
||||
|
||||
export function parseRadianToAngle(radian: number): number {
|
||||
return radian / Math.PI * 180;
|
||||
return (radian / Math.PI) * 180;
|
||||
}
|
||||
|
||||
export function parseAngleToRadian(angle: number): number {
|
||||
return angle / 180 * Math.PI;
|
||||
return (angle / 180) * Math.PI;
|
||||
}
|
||||
|
||||
export function calcElementCenter(elem: TypeElement<keyof TypeElemDesc>): TypePoint {
|
||||
export function calcElementCenter(
|
||||
elem: DataElement<keyof DataElemDesc>
|
||||
): Point {
|
||||
const p = {
|
||||
x: elem.x + elem.w / 2,
|
||||
y: elem.y + elem.h / 2,
|
||||
y: elem.y + elem.h / 2
|
||||
};
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
export function calcRadian(center: TypePoint, start: TypePoint, end: TypePoint): number {
|
||||
export function calcRadian(center: Point, start: Point, end: Point): number {
|
||||
const startAngle = calcLineAngle(center, start);
|
||||
const endAngle = calcLineAngle(center, end);
|
||||
if (endAngle !== null && startAngle !== null ) {
|
||||
if (startAngle > Math.PI * 3 / 2 && endAngle < Math.PI / 2) {
|
||||
if (endAngle !== null && startAngle !== null) {
|
||||
if (startAngle > (Math.PI * 3) / 2 && endAngle < Math.PI / 2) {
|
||||
return endAngle + (Math.PI * 2 - startAngle);
|
||||
} else if (endAngle > Math.PI * 3 / 2 && startAngle < Math.PI / 2) {
|
||||
} else if (endAngle > (Math.PI * 3) / 2 && startAngle < Math.PI / 2) {
|
||||
return startAngle + (Math.PI * 2 - endAngle);
|
||||
} else {
|
||||
return endAngle - startAngle;
|
||||
|
|
@ -38,14 +34,14 @@ export function calcRadian(center: TypePoint, start: TypePoint, end: TypePoint):
|
|||
}
|
||||
}
|
||||
|
||||
function calcLineAngle(center: TypePoint, p: TypePoint): number | null {
|
||||
function calcLineAngle(center: Point, p: Point): number | null {
|
||||
const x = p.x - center.x;
|
||||
const y = center.y - p.y;
|
||||
if (x === 0) {
|
||||
if (y < 0) {
|
||||
return Math.PI / 2;
|
||||
} else if (y > 0) {
|
||||
return Math.PI * ( 3 / 2 );
|
||||
return Math.PI * (3 / 2);
|
||||
}
|
||||
} else if (y === 0) {
|
||||
if (x < 0) {
|
||||
|
|
@ -64,4 +60,4 @@ function calcLineAngle(center: TypePoint, p: TypePoint): number | null {
|
|||
return Math.PI * 2 - Math.atan(Math.abs(y) / Math.abs(x));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,65 +1,64 @@
|
|||
import { TypeElement, TypeData, TypeElemDesc } from '@idraw/types';
|
||||
|
||||
type TypeElementMap = {
|
||||
[uuid: string]: TypeElement<keyof TypeElemDesc>
|
||||
}
|
||||
import { DataElement, IDrawData, DataElemDesc } from '@idraw/types';
|
||||
|
||||
type DataElementMap = {
|
||||
[uuid: string]: DataElement<keyof DataElemDesc>;
|
||||
};
|
||||
|
||||
export function isChangeImageElementResource(
|
||||
before: TypeElement<'image'>,
|
||||
after: TypeElement<'image'>,
|
||||
before: DataElement<'image'>,
|
||||
after: DataElement<'image'>
|
||||
): boolean {
|
||||
return (before?.desc?.src !== after?.desc?.src);
|
||||
return before?.desc?.src !== after?.desc?.src;
|
||||
}
|
||||
|
||||
|
||||
export function isChangeSVGElementResource(
|
||||
before: TypeElement<'svg'>,
|
||||
after: TypeElement<'svg'>,
|
||||
before: DataElement<'svg'>,
|
||||
after: DataElement<'svg'>
|
||||
): boolean {
|
||||
return (before?.desc?.svg !== after?.desc?.svg);
|
||||
return before?.desc?.svg !== after?.desc?.svg;
|
||||
}
|
||||
|
||||
export function isChangeHTMLElementResource(
|
||||
before: TypeElement<'html'>,
|
||||
after: TypeElement<'html'>,
|
||||
before: DataElement<'html'>,
|
||||
after: DataElement<'html'>
|
||||
): boolean {
|
||||
return (
|
||||
before?.desc?.html !== after?.desc?.html
|
||||
|| before?.desc?.width !== after?.desc?.width
|
||||
|| before?.desc?.height !== after?.desc?.height
|
||||
before?.desc?.html !== after?.desc?.html ||
|
||||
before?.desc?.width !== after?.desc?.width ||
|
||||
before?.desc?.height !== after?.desc?.height
|
||||
);
|
||||
}
|
||||
|
||||
export function diffElementResourceChange(
|
||||
before: TypeElement<keyof TypeElemDesc>,
|
||||
after: TypeElement<keyof TypeElemDesc>,
|
||||
before: DataElement<keyof DataElemDesc>,
|
||||
after: DataElement<keyof DataElemDesc>
|
||||
): string | null {
|
||||
let result = null;
|
||||
let isChange = false;
|
||||
switch (after.type) {
|
||||
case 'image': {
|
||||
isChange = isChangeImageElementResource(
|
||||
before as TypeElement<'image'>,
|
||||
after as TypeElement<'image'>
|
||||
before as DataElement<'image'>,
|
||||
after as DataElement<'image'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'svg': {
|
||||
isChange = isChangeSVGElementResource(
|
||||
before as TypeElement<'svg'>,
|
||||
after as TypeElement<'svg'>
|
||||
before as DataElement<'svg'>,
|
||||
after as DataElement<'svg'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'html': {
|
||||
isChange = isChangeHTMLElementResource(
|
||||
before as TypeElement<'html'>,
|
||||
after as TypeElement<'html'>
|
||||
before as DataElement<'html'>,
|
||||
after as DataElement<'html'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (isChange === true) {
|
||||
result = after.uuid;
|
||||
|
|
@ -68,8 +67,8 @@ export function diffElementResourceChange(
|
|||
}
|
||||
|
||||
export function diffElementResourceChangeList(
|
||||
before: TypeData,
|
||||
after: TypeData,
|
||||
before: IDrawData,
|
||||
after: IDrawData
|
||||
): string[] {
|
||||
const uuids: string[] = [];
|
||||
const beforeMap = parseDataElementMap(before);
|
||||
|
|
@ -83,26 +82,27 @@ export function diffElementResourceChangeList(
|
|||
switch (beforeMap[uuid].type) {
|
||||
case 'image': {
|
||||
isChange = isChangeImageElementResource(
|
||||
beforeMap[uuid] as TypeElement<'image'>,
|
||||
afterMap[uuid] as TypeElement<'image'>
|
||||
beforeMap[uuid] as DataElement<'image'>,
|
||||
afterMap[uuid] as DataElement<'image'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'svg': {
|
||||
isChange = isChangeSVGElementResource(
|
||||
beforeMap[uuid] as TypeElement<'svg'>,
|
||||
afterMap[uuid] as TypeElement<'svg'>
|
||||
beforeMap[uuid] as DataElement<'svg'>,
|
||||
afterMap[uuid] as DataElement<'svg'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'html': {
|
||||
isChange = isChangeHTMLElementResource(
|
||||
beforeMap[uuid] as TypeElement<'html'>,
|
||||
afterMap[uuid] as TypeElement<'html'>
|
||||
beforeMap[uuid] as DataElement<'html'>,
|
||||
afterMap[uuid] as DataElement<'html'>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (isChange === true) {
|
||||
uuids.push(uuid);
|
||||
|
|
@ -114,11 +114,10 @@ export function diffElementResourceChangeList(
|
|||
return uuids;
|
||||
}
|
||||
|
||||
|
||||
function parseDataElementMap(data: TypeData): TypeElementMap {
|
||||
const elemMap: TypeElementMap = {};
|
||||
function parseDataElementMap(data: IDrawData): DataElementMap {
|
||||
const elemMap: DataElementMap = {};
|
||||
data.elements.forEach((elem) => {
|
||||
elemMap[elem.uuid] = elem;
|
||||
})
|
||||
});
|
||||
return elemMap;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
TypeContext,
|
||||
// TypeElemDesc,
|
||||
TypeElement
|
||||
IDrawContext,
|
||||
// DataElemDesc,
|
||||
DataElement
|
||||
} from '@idraw/types';
|
||||
import { is, istype, isColorStr } from '@idraw/util';
|
||||
import { rotateElement } from './../transform';
|
||||
|
||||
export function clearContext(ctx: TypeContext) {
|
||||
export function clearContext(ctx: IDrawContext) {
|
||||
// ctx.setFillStyle('rgb(0 0 0 / 100%)');
|
||||
// ctx.setStrokeStyle('rgb(0 0 0 / 100%)');
|
||||
ctx.setFillStyle('#000000');
|
||||
|
|
@ -19,15 +19,15 @@ export function clearContext(ctx: TypeContext) {
|
|||
ctx.setShadowBlur(0);
|
||||
}
|
||||
|
||||
export function drawBgColor(ctx: TypeContext, color: string) {
|
||||
export function drawBgColor(ctx: IDrawContext, color: string) {
|
||||
const size = ctx.getSize();
|
||||
ctx.setFillStyle(color);
|
||||
ctx.fillRect(0, 0, size.contextWidth, size.contextHeight);
|
||||
}
|
||||
|
||||
export function drawBox(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'text' | 'rect'>,
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'text' | 'rect'>,
|
||||
pattern: string | CanvasPattern | null
|
||||
): void {
|
||||
clearContext(ctx);
|
||||
|
|
@ -57,8 +57,8 @@ export function drawBox(
|
|||
}
|
||||
|
||||
export function drawBoxBorder(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'text' | 'rect'>
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'text' | 'rect'>
|
||||
): void {
|
||||
clearContext(ctx);
|
||||
rotateElement(ctx, elem, () => {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { TypeContext, TypeElement, } from '@idraw/types';
|
||||
import { IDrawContext, DataElement } from '@idraw/types';
|
||||
import { rotateElement } from './../transform';
|
||||
import { clearContext } from './base';
|
||||
|
||||
export function drawCircle(ctx: TypeContext, elem: TypeElement<'circle'>) {
|
||||
export function drawCircle(ctx: IDrawContext, elem: DataElement<'circle'>) {
|
||||
clearContext(ctx);
|
||||
rotateElement(ctx, elem, (ctx) => {
|
||||
const { x, y, w, h, desc } = elem;
|
||||
const {
|
||||
bgColor = '#000000',
|
||||
borderColor = '#000000',
|
||||
borderWidth = 0,
|
||||
borderWidth = 0
|
||||
} = desc;
|
||||
|
||||
const a = w / 2;
|
||||
|
|
@ -19,14 +19,13 @@ export function drawCircle(ctx: TypeContext, elem: TypeElement<'circle'>) {
|
|||
|
||||
// draw border
|
||||
if (borderWidth && borderWidth > 0) {
|
||||
|
||||
const ba = borderWidth / 2 + a;
|
||||
const bb = borderWidth / 2 + b;
|
||||
ctx.beginPath();
|
||||
ctx.setStrokeStyle(borderColor);
|
||||
ctx.setLineWidth(borderWidth);
|
||||
ctx.ellipse(centerX, centerY, ba, bb, 0, 0, 2 * Math.PI)
|
||||
|
||||
ctx.ellipse(centerX, centerY, ba, bb, 0, 0, 2 * Math.PI);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
|
@ -34,10 +33,10 @@ export function drawCircle(ctx: TypeContext, elem: TypeElement<'circle'>) {
|
|||
// draw content
|
||||
ctx.beginPath();
|
||||
ctx.setFillStyle(bgColor);
|
||||
ctx.ellipse(centerX, centerY, a, b, 0, 0, 2 * Math.PI)
|
||||
ctx.ellipse(centerX, centerY, a, b, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
|
||||
// // draw shadow
|
||||
// clearContext(ctx);
|
||||
// if ((desc.shadowOffsetX !== undefined && is.number(desc.shadowOffsetX)) || desc.shadowOffsetY !== undefined && is.number(desc.shadowOffsetY)) {
|
||||
|
|
@ -70,6 +69,5 @@ export function drawCircle(ctx: TypeContext, elem: TypeElement<'circle'>) {
|
|||
// ctx.closePath();
|
||||
// ctx.fill();
|
||||
// }
|
||||
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypeElement,
|
||||
} from '@idraw/types';
|
||||
import { IDrawContext, DataElement } from '@idraw/types';
|
||||
import { rotateElement } from '../transform';
|
||||
import Loader from '../loader';
|
||||
|
||||
|
||||
export function drawHTML(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'html'>,
|
||||
loader: Loader,
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'html'>,
|
||||
loader: Loader
|
||||
) {
|
||||
const content = loader.getContent(elem.uuid);
|
||||
rotateElement(ctx, elem, () => {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypeElement,
|
||||
} from '@idraw/types';
|
||||
import { IDrawContext, DataElement } from '@idraw/types';
|
||||
import { rotateElement } from '../transform';
|
||||
import Loader from '../loader';
|
||||
|
||||
|
||||
export function drawImage (
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'image'>,
|
||||
loader: Loader,
|
||||
export function drawImage(
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'image'>,
|
||||
loader: Loader
|
||||
) {
|
||||
// const desc = elem.desc as TypeElemDesc['rect'];
|
||||
// const desc = elem.desc as DataElemDesc['rect'];
|
||||
const content = loader.getContent(elem.uuid);
|
||||
rotateElement(ctx, elem, () => {
|
||||
// ctx.setFillStyle(desc.color);
|
||||
|
|
@ -23,31 +19,23 @@ export function drawImage (
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// import {
|
||||
// TypeContext,
|
||||
// TypeElement,
|
||||
// TypeHelperConfig,
|
||||
// TypeElemDesc,
|
||||
// IDrawContext,
|
||||
// DataElement,
|
||||
// HelperConfig,
|
||||
// DataElemDesc,
|
||||
// } from '@idraw/types';
|
||||
// import Loader from '../loader';
|
||||
// import { drawBox } from './base';
|
||||
|
||||
// export function drawImage(
|
||||
// ctx: TypeContext,
|
||||
// elem: TypeElement<'image'>,
|
||||
// ctx: IDrawContext,
|
||||
// elem: DataElement<'image'>,
|
||||
// loader: Loader,
|
||||
// helperConfig: TypeHelperConfig
|
||||
// helperConfig: HelperConfig
|
||||
// ) {
|
||||
// const content = loader.getPattern(elem, {
|
||||
// forceUpdate: helperConfig?.selectedElementWrapper?.uuid === elem.uuid
|
||||
// });
|
||||
// drawBox(ctx, elem, content);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypeData,
|
||||
TypeElement,
|
||||
// TypePoint,
|
||||
IDrawContext,
|
||||
IDrawData,
|
||||
DataElement
|
||||
// Point,
|
||||
} from '@idraw/types';
|
||||
import { isColorStr } from '@idraw/util';
|
||||
import Loader from '../loader';
|
||||
|
|
@ -15,9 +15,9 @@ import { drawText } from './text';
|
|||
import { drawCircle } from './circle';
|
||||
|
||||
export function drawContext(
|
||||
ctx: TypeContext,
|
||||
data: TypeData,
|
||||
loader: Loader,
|
||||
ctx: IDrawContext,
|
||||
data: IDrawData,
|
||||
loader: Loader
|
||||
): void {
|
||||
clearContext(ctx);
|
||||
const size = ctx.getSize();
|
||||
|
|
@ -37,27 +37,27 @@ export function drawContext(
|
|||
}
|
||||
switch (elem.type) {
|
||||
case 'rect': {
|
||||
drawRect(ctx, elem as TypeElement<'rect'>);
|
||||
drawRect(ctx, elem as DataElement<'rect'>);
|
||||
break;
|
||||
}
|
||||
case 'text': {
|
||||
drawText(ctx, elem as TypeElement<'text'>, loader);
|
||||
drawText(ctx, elem as DataElement<'text'>, loader);
|
||||
break;
|
||||
}
|
||||
case 'image': {
|
||||
drawImage(ctx, elem as TypeElement<'image'>, loader);
|
||||
drawImage(ctx, elem as DataElement<'image'>, loader);
|
||||
break;
|
||||
}
|
||||
case 'svg': {
|
||||
drawSVG(ctx, elem as TypeElement<'svg'>, loader);
|
||||
drawSVG(ctx, elem as DataElement<'svg'>, loader);
|
||||
break;
|
||||
}
|
||||
case 'html': {
|
||||
drawHTML(ctx, elem as TypeElement<'html'>, loader);
|
||||
drawHTML(ctx, elem as DataElement<'html'>, loader);
|
||||
break;
|
||||
}
|
||||
case 'circle': {
|
||||
drawCircle(ctx, elem as TypeElement<'circle'>);
|
||||
drawCircle(ctx, elem as DataElement<'circle'>);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -66,6 +66,4 @@ export function drawContext(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypeElement,
|
||||
} from '@idraw/types';
|
||||
import { IDrawContext, DataElement } from '@idraw/types';
|
||||
import { drawBox } from './base';
|
||||
|
||||
export function drawRect(ctx: TypeContext, elem: TypeElement<'rect'>) {
|
||||
|
||||
export function drawRect(ctx: IDrawContext, elem: DataElement<'rect'>) {
|
||||
drawBox(ctx, elem, elem.desc.bgColor as string);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,17 +1,13 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypeElement,
|
||||
} from '@idraw/types';
|
||||
import { IDrawContext, DataElement } from '@idraw/types';
|
||||
import { rotateElement } from '../transform';
|
||||
import Loader from '../loader';
|
||||
|
||||
|
||||
export function drawSVG (
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'svg'>,
|
||||
loader: Loader,
|
||||
export function drawSVG(
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'svg'>,
|
||||
loader: Loader
|
||||
) {
|
||||
// const desc = elem.desc as TypeElemDesc['rect'];
|
||||
// const desc = elem.desc as DataElemDesc['rect'];
|
||||
const content = loader.getContent(elem.uuid);
|
||||
rotateElement(ctx, elem, () => {
|
||||
// ctx.setFillStyle(desc.color);
|
||||
|
|
@ -24,24 +20,21 @@ export function drawSVG (
|
|||
}
|
||||
|
||||
// import {
|
||||
// TypeContext,
|
||||
// TypeElement,
|
||||
// TypeHelperConfig,
|
||||
// IDrawContext,
|
||||
// DataElement,
|
||||
// HelperConfig,
|
||||
// } from '@idraw/types';
|
||||
// import Loader from '../loader';
|
||||
// import { drawBox } from './base';
|
||||
|
||||
// export function drawSVG(
|
||||
// ctx: TypeContext,
|
||||
// elem: TypeElement<'svg'>,
|
||||
// ctx: IDrawContext,
|
||||
// elem: DataElement<'svg'>,
|
||||
// loader: Loader,
|
||||
// helperConfig: TypeHelperConfig
|
||||
// helperConfig: HelperConfig
|
||||
// ) {
|
||||
// const content = loader.getPattern(elem, {
|
||||
// forceUpdate: helperConfig?.selectedElementWrapper?.uuid === elem.uuid
|
||||
// });
|
||||
// drawBox(ctx, elem, content);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { TypeContext, TypeElemDescText, TypeElement } from '@idraw/types';
|
||||
import { IDrawContext, DataElemDescText, DataElement } from '@idraw/types';
|
||||
import { is, isColorStr } from '@idraw/util';
|
||||
import Loader from '../loader';
|
||||
import { clearContext, drawBox } from './base';
|
||||
import { rotateElement } from './../transform';
|
||||
|
||||
export function drawText(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<'text'>,
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<'text'>,
|
||||
loader: Loader
|
||||
) {
|
||||
clearContext(ctx);
|
||||
drawBox(ctx, elem, elem.desc.bgColor || 'transparent');
|
||||
rotateElement(ctx, elem, () => {
|
||||
const desc: TypeElemDescText = {
|
||||
const desc: DataElemDescText = {
|
||||
...{
|
||||
fontSize: 12,
|
||||
fontFamily: 'sans-serif',
|
||||
|
|
@ -149,7 +149,7 @@ export function drawText(
|
|||
});
|
||||
}
|
||||
|
||||
// export function createTextSVG(elem: TypeElement<'text'>): string {
|
||||
// export function createTextSVG(elem: DataElement<'text'>): string {
|
||||
// const svg = `
|
||||
// <svg xmlns="http://www.w3.org/2000/svg" width="${elem.w}" height = "${elem.h}">
|
||||
// <foreignObject width="100%" height="100%">
|
||||
|
|
|
|||
|
|
@ -1,43 +1,53 @@
|
|||
import { TypeElement, TypeElemDesc } from '@idraw/types';
|
||||
import { DataElement, DataElemDesc } from '@idraw/types';
|
||||
|
||||
export type TypeLoadDataItem = {
|
||||
uuid: string,
|
||||
type: 'image' | 'svg' | 'html',
|
||||
status: 'null' | 'loaded' | 'fail',
|
||||
content: null | HTMLImageElement | HTMLCanvasElement,
|
||||
uuid: string;
|
||||
type: 'image' | 'svg' | 'html';
|
||||
status: 'null' | 'loaded' | 'fail';
|
||||
content: null | HTMLImageElement | HTMLCanvasElement;
|
||||
elemW: number;
|
||||
elemH: number;
|
||||
source: string,
|
||||
element: TypeElement<keyof TypeElemDesc>
|
||||
error?: any,
|
||||
}
|
||||
source: string;
|
||||
element: DataElement<keyof DataElemDesc>;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export type TypeLoadData = {
|
||||
[uuid: string]: TypeLoadDataItem
|
||||
}
|
||||
[uuid: string]: TypeLoadDataItem;
|
||||
};
|
||||
|
||||
export type TypeLoaderEventArgMap = {
|
||||
'complete': void;
|
||||
'load': TypeLoadData[string];
|
||||
'error': TypeLoadData[string];
|
||||
}
|
||||
|
||||
export interface TypeLoaderEvent {
|
||||
on<T extends keyof TypeLoaderEventArgMap >(key: T, callback: (p: TypeLoaderEventArgMap[T]) => void): void
|
||||
off<T extends keyof TypeLoaderEventArgMap >(key: T, callback: (p: TypeLoaderEventArgMap[T]) => void): void
|
||||
trigger<T extends keyof TypeLoaderEventArgMap >(key: T, p: TypeLoaderEventArgMap[T]): void
|
||||
}
|
||||
complete: void;
|
||||
load: TypeLoadData[string];
|
||||
error: TypeLoadData[string];
|
||||
};
|
||||
|
||||
export interface TypeLoaderEvent {
|
||||
on<T extends keyof TypeLoaderEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeLoaderEventArgMap[T]) => void
|
||||
): void;
|
||||
off<T extends keyof TypeLoaderEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeLoaderEventArgMap[T]) => void
|
||||
): void;
|
||||
trigger<T extends keyof TypeLoaderEventArgMap>(
|
||||
key: T,
|
||||
p: TypeLoaderEventArgMap[T]
|
||||
): void;
|
||||
}
|
||||
|
||||
export class LoaderEvent implements TypeLoaderEvent {
|
||||
|
||||
private _listeners: Map<string, ((p: any) => void)[]>;
|
||||
|
||||
constructor() {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
|
||||
on<T extends keyof TypeLoaderEventArgMap >(eventKey: T, callback: (p: TypeLoaderEventArgMap[T]) => void) {
|
||||
on<T extends keyof TypeLoaderEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeLoaderEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
callbacks?.push(callback);
|
||||
|
|
@ -46,8 +56,11 @@ export class LoaderEvent implements TypeLoaderEvent {
|
|||
this._listeners.set(eventKey, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
off<T extends keyof TypeLoaderEventArgMap >(eventKey: T, callback: (p: TypeLoaderEventArgMap[T]) => void) {
|
||||
|
||||
off<T extends keyof TypeLoaderEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeLoaderEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
|
|
@ -62,7 +75,10 @@ export class LoaderEvent implements TypeLoaderEvent {
|
|||
}
|
||||
}
|
||||
|
||||
trigger<T extends keyof TypeLoaderEventArgMap >(eventKey: T, arg: TypeLoaderEventArgMap[T]) {
|
||||
trigger<T extends keyof TypeLoaderEventArgMap>(
|
||||
eventKey: T,
|
||||
arg: TypeLoaderEventArgMap[T]
|
||||
) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
callbacks.forEach((cb) => {
|
||||
|
|
@ -74,14 +90,14 @@ export class LoaderEvent implements TypeLoaderEvent {
|
|||
}
|
||||
}
|
||||
|
||||
has<T extends keyof TypeLoaderEventArgMap> (name: string) {
|
||||
has<T extends keyof TypeLoaderEventArgMap>(name: string) {
|
||||
if (this._listeners.has(name)) {
|
||||
const list: ((p: TypeLoaderEventArgMap[T]) => void)[] | undefined = this._listeners.get(name);
|
||||
const list: ((p: TypeLoaderEventArgMap[T]) => void)[] | undefined =
|
||||
this._listeners.get(name);
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
import { TypeData, TypeElement } from '@idraw/types';
|
||||
import { IDrawData, DataElement } from '@idraw/types';
|
||||
import { loadImage, loadSVG, loadHTML, deepClone } from '@idraw/util';
|
||||
import { LoaderEvent, TypeLoadData, TypeLoaderEventArgMap } from './loader-event';
|
||||
import {
|
||||
LoaderEvent,
|
||||
TypeLoadData,
|
||||
TypeLoaderEventArgMap
|
||||
} from './loader-event';
|
||||
import { filterScript } from './../util/filter';
|
||||
|
||||
type Options = {
|
||||
maxParallelNum: number
|
||||
}
|
||||
maxParallelNum: number;
|
||||
};
|
||||
|
||||
enum LoaderStatus {
|
||||
FREE = 'free',
|
||||
LOADING = 'loading',
|
||||
COMPLETE = 'complete',
|
||||
COMPLETE = 'complete'
|
||||
}
|
||||
|
||||
export default class Loader {
|
||||
|
||||
private _opts: Options;
|
||||
private _event: LoaderEvent;
|
||||
// private _patternMap: {[uuid: string]: CanvasPattern} = {}
|
||||
|
|
@ -24,8 +27,8 @@ export default class Loader {
|
|||
private _status: LoaderStatus = LoaderStatus.FREE;
|
||||
|
||||
private _waitingLoadQueue: Array<{
|
||||
uuidQueue: string[],
|
||||
loadData: TypeLoadData,
|
||||
uuidQueue: string[];
|
||||
loadData: TypeLoadData;
|
||||
}> = [];
|
||||
|
||||
constructor(opts: Options) {
|
||||
|
|
@ -34,31 +37,37 @@ export default class Loader {
|
|||
this._waitingLoadQueue = [];
|
||||
}
|
||||
|
||||
load(data: TypeData, changeResourceUUIDs: string[]): void {
|
||||
const [uuidQueue, loadData] = this._resetLoadData(data, changeResourceUUIDs);
|
||||
if (this._status === LoaderStatus.FREE || this._status === LoaderStatus.COMPLETE) {
|
||||
load(data: IDrawData, changeResourceUUIDs: string[]): void {
|
||||
const [uuidQueue, loadData] = this._resetLoadData(
|
||||
data,
|
||||
changeResourceUUIDs
|
||||
);
|
||||
if (
|
||||
this._status === LoaderStatus.FREE ||
|
||||
this._status === LoaderStatus.COMPLETE
|
||||
) {
|
||||
this._currentUUIDQueue = uuidQueue;
|
||||
this._currentLoadData = loadData;
|
||||
this._loadTask();
|
||||
} else if (this._status === LoaderStatus.LOADING && uuidQueue.length > 0) {
|
||||
this._waitingLoadQueue.push({
|
||||
uuidQueue,
|
||||
loadData,
|
||||
loadData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
on<T extends keyof TypeLoaderEventArgMap>(
|
||||
name: T,
|
||||
callback: (arg: TypeLoaderEventArgMap[T]
|
||||
) => void) {
|
||||
callback: (arg: TypeLoaderEventArgMap[T]) => void
|
||||
) {
|
||||
this._event.on(name, callback);
|
||||
}
|
||||
|
||||
off<T extends keyof TypeLoaderEventArgMap>(
|
||||
name: T,
|
||||
callback: (arg: TypeLoaderEventArgMap[T]
|
||||
) => void) {
|
||||
callback: (arg: TypeLoaderEventArgMap[T]) => void
|
||||
) {
|
||||
this._event.off(name, callback);
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +83,7 @@ export default class Loader {
|
|||
}
|
||||
|
||||
// getPattern(
|
||||
// elem: TypeElement<keyof TypeElemDesc>,
|
||||
// elem: DataElement<keyof DataElemDesc>,
|
||||
// opts?: {
|
||||
// forceUpdate: boolean
|
||||
// }
|
||||
|
|
@ -91,7 +100,7 @@ export default class Loader {
|
|||
// const tempCtx = board.createContext(tempCanvas);
|
||||
// const image = this.getContent(elem.uuid);
|
||||
// tempCtx.drawImage(image, elem.x, elem.y, elem.w, elem.h);
|
||||
|
||||
|
||||
// const canvas = board.createCanvas();
|
||||
// const ctx = board.createContext(canvas);
|
||||
// const pattern = ctx.createPattern(tempCanvas, 'no-repeat');
|
||||
|
|
@ -101,7 +110,10 @@ export default class Loader {
|
|||
// return null;
|
||||
// }
|
||||
|
||||
private _resetLoadData(data: TypeData, changeResourceUUIDs: string[]): [string[], TypeLoadData] {
|
||||
private _resetLoadData(
|
||||
data: IDrawData,
|
||||
changeResourceUUIDs: string[]
|
||||
): [string[], TypeLoadData] {
|
||||
const loadData: TypeLoadData = {};
|
||||
const uuidQueue: string[] = [];
|
||||
|
||||
|
|
@ -109,10 +121,10 @@ export default class Loader {
|
|||
// const currentUUIDs: string[] = []
|
||||
|
||||
// add new load-data
|
||||
for (let i = data.elements.length - 1; i >= 0; i --) {
|
||||
const elem = data.elements[i] as TypeElement<'image' | 'svg' | 'html'>;
|
||||
for (let i = data.elements.length - 1; i >= 0; i--) {
|
||||
const elem = data.elements[i] as DataElement<'image' | 'svg' | 'html'>;
|
||||
// currentUUIDs.push(elem.uuid);
|
||||
if (['image', 'svg', 'html', ].includes(elem.type)) {
|
||||
if (['image', 'svg', 'html'].includes(elem.type)) {
|
||||
if (!storageLoadData[elem.uuid]) {
|
||||
loadData[elem.uuid] = this._createEmptyLoadItem(elem);
|
||||
uuidQueue.push(elem.uuid);
|
||||
|
|
@ -122,24 +134,24 @@ export default class Loader {
|
|||
uuidQueue.push(elem.uuid);
|
||||
}
|
||||
// if (elem.type === 'image') {
|
||||
// const _ele = elem as TypeElement<'image'>;
|
||||
// const _ele = elem as DataElement<'image'>;
|
||||
// if (_ele.desc.src !== storageLoadData[elem.uuid].source) {
|
||||
// loadData[elem.uuid] = this._createEmptyLoadItem(elem);
|
||||
// uuidQueue.push(elem.uuid);
|
||||
// }
|
||||
// } else if (elem.type === 'svg') {
|
||||
// const _ele = elem as TypeElement<'svg'>;
|
||||
// const _ele = elem as DataElement<'svg'>;
|
||||
// if (_ele.desc.svg !== storageLoadData[elem.uuid].source) {
|
||||
// loadData[elem.uuid] = this._createEmptyLoadItem(elem);
|
||||
// uuidQueue.push(elem.uuid);
|
||||
// }
|
||||
// } else if (elem.type === 'html') {
|
||||
// const _ele = elem as TypeElement<'html'>;
|
||||
// const _ele = elem as DataElement<'html'>;
|
||||
// if (filterScript(_ele.desc.html) !== storageLoadData[elem.uuid].source) {
|
||||
// loadData[elem.uuid] = this._createEmptyLoadItem(elem);
|
||||
// uuidQueue.push(elem.uuid);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -154,20 +166,23 @@ export default class Loader {
|
|||
return [uuidQueue, loadData];
|
||||
}
|
||||
|
||||
private _createEmptyLoadItem(elem: TypeElement<'image' | 'svg' | 'html'>): TypeLoadData[string] {
|
||||
private _createEmptyLoadItem(
|
||||
elem: DataElement<'image' | 'svg' | 'html'>
|
||||
): TypeLoadData[string] {
|
||||
let source = '';
|
||||
|
||||
const type: TypeLoadData[string]['type'] = elem.type as TypeLoadData[string]['type'];
|
||||
const type: TypeLoadData[string]['type'] =
|
||||
elem.type as TypeLoadData[string]['type'];
|
||||
let elemW: number = elem.w;
|
||||
let elemH: number = elem.h;
|
||||
if (elem.type === 'image') {
|
||||
const _elem = elem as TypeElement<'image'>;
|
||||
const _elem = elem as DataElement<'image'>;
|
||||
source = _elem.desc.src || '';
|
||||
} else if (elem.type === 'svg') {
|
||||
const _elem = elem as TypeElement<'svg'>;
|
||||
const _elem = elem as DataElement<'svg'>;
|
||||
source = _elem.desc.svg || '';
|
||||
} else if (elem.type === 'html') {
|
||||
const _elem = elem as TypeElement<'html'>;
|
||||
const _elem = elem as DataElement<'html'>;
|
||||
source = filterScript(_elem.desc.html || '');
|
||||
elemW = _elem.desc.width || elem.w;
|
||||
elemH = _elem.desc.height || elem.h;
|
||||
|
|
@ -180,7 +195,7 @@ export default class Loader {
|
|||
source,
|
||||
elemW,
|
||||
elemH,
|
||||
element: deepClone(elem),
|
||||
element: deepClone(elem)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -207,14 +222,13 @@ export default class Loader {
|
|||
|
||||
const { maxParallelNum } = this._opts;
|
||||
const uuids = this._currentUUIDQueue.splice(0, maxParallelNum);
|
||||
const uuidMap: {[uuid: string]: number} = {};
|
||||
const uuidMap: { [uuid: string]: number } = {};
|
||||
|
||||
uuids.forEach((url, i) => {
|
||||
uuidMap[url] = i;
|
||||
});
|
||||
const loadUUIDList: string[] = [];
|
||||
const _loadAction = () => {
|
||||
|
||||
if (loadUUIDList.length >= maxParallelNum) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -229,75 +243,83 @@ export default class Loader {
|
|||
}
|
||||
loadUUIDList.push(uuid);
|
||||
|
||||
this._loadElementSource(this._currentLoadData[uuid]).then((image) => {
|
||||
loadUUIDList.splice(loadUUIDList.indexOf(uuid), 1);
|
||||
const status = _loadAction();
|
||||
this._loadElementSource(this._currentLoadData[uuid])
|
||||
.then((image) => {
|
||||
loadUUIDList.splice(loadUUIDList.indexOf(uuid), 1);
|
||||
const status = _loadAction();
|
||||
|
||||
this._storageLoadData[uuid] = {
|
||||
uuid,
|
||||
type: this._currentLoadData[uuid].type,
|
||||
status: 'loaded',
|
||||
content: image,
|
||||
source: this._currentLoadData[uuid].source,
|
||||
elemW: this._currentLoadData[uuid].elemW,
|
||||
elemH: this._currentLoadData[uuid].elemH,
|
||||
element: this._currentLoadData[uuid].element,
|
||||
};
|
||||
|
||||
if (loadUUIDList.length === 0 && uuids.length === 0 && status === true) {
|
||||
this._status = LoaderStatus.FREE;
|
||||
this._loadTask();
|
||||
}
|
||||
this._event.trigger('load', {
|
||||
uuid: this._storageLoadData[uuid]?.uuid,
|
||||
type: this._storageLoadData[uuid].type,
|
||||
status: this._storageLoadData[uuid].status,
|
||||
content: this._storageLoadData[uuid].content,
|
||||
source: this._storageLoadData[uuid].source,
|
||||
elemW: this._storageLoadData[uuid].elemW,
|
||||
elemH: this._storageLoadData[uuid].elemH,
|
||||
element: this._storageLoadData[uuid]?.element,
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.warn(err);
|
||||
|
||||
loadUUIDList.splice(loadUUIDList.indexOf(uuid), 1);
|
||||
const status = _loadAction();
|
||||
|
||||
if (this._currentLoadData[uuid]) {
|
||||
this._storageLoadData[uuid] = {
|
||||
uuid,
|
||||
type: this._currentLoadData[uuid]?.type,
|
||||
status: 'fail',
|
||||
content: null,
|
||||
error: err,
|
||||
source: this._currentLoadData[uuid]?.source,
|
||||
elemW: this._currentLoadData[uuid]?.elemW,
|
||||
elemH: this._currentLoadData[uuid]?.elemH,
|
||||
element: this._currentLoadData[uuid]?.element,
|
||||
type: this._currentLoadData[uuid].type,
|
||||
status: 'loaded',
|
||||
content: image,
|
||||
source: this._currentLoadData[uuid].source,
|
||||
elemW: this._currentLoadData[uuid].elemW,
|
||||
elemH: this._currentLoadData[uuid].elemH,
|
||||
element: this._currentLoadData[uuid].element
|
||||
};
|
||||
}
|
||||
|
||||
if (loadUUIDList.length === 0 && uuids.length === 0 && status === true) {
|
||||
this._status = LoaderStatus.FREE;
|
||||
this._loadTask();
|
||||
}
|
||||
|
||||
if (this._currentLoadData[uuid]) {
|
||||
this._event.trigger('error', {
|
||||
uuid: uuid,
|
||||
type: this._storageLoadData[uuid]?.type,
|
||||
status: this._storageLoadData[uuid]?.status,
|
||||
content: this._storageLoadData[uuid]?.content,
|
||||
source: this._storageLoadData[uuid]?.source,
|
||||
elemW: this._storageLoadData[uuid]?.elemW,
|
||||
elemH: this._storageLoadData[uuid]?.elemH,
|
||||
element: this._storageLoadData[uuid]?.element,
|
||||
if (
|
||||
loadUUIDList.length === 0 &&
|
||||
uuids.length === 0 &&
|
||||
status === true
|
||||
) {
|
||||
this._status = LoaderStatus.FREE;
|
||||
this._loadTask();
|
||||
}
|
||||
this._event.trigger('load', {
|
||||
uuid: this._storageLoadData[uuid]?.uuid,
|
||||
type: this._storageLoadData[uuid].type,
|
||||
status: this._storageLoadData[uuid].status,
|
||||
content: this._storageLoadData[uuid].content,
|
||||
source: this._storageLoadData[uuid].source,
|
||||
elemW: this._storageLoadData[uuid].elemW,
|
||||
elemH: this._storageLoadData[uuid].elemH,
|
||||
element: this._storageLoadData[uuid]?.element
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn(err);
|
||||
|
||||
loadUUIDList.splice(loadUUIDList.indexOf(uuid), 1);
|
||||
const status = _loadAction();
|
||||
|
||||
if (this._currentLoadData[uuid]) {
|
||||
this._storageLoadData[uuid] = {
|
||||
uuid,
|
||||
type: this._currentLoadData[uuid]?.type,
|
||||
status: 'fail',
|
||||
content: null,
|
||||
error: err,
|
||||
source: this._currentLoadData[uuid]?.source,
|
||||
elemW: this._currentLoadData[uuid]?.elemW,
|
||||
elemH: this._currentLoadData[uuid]?.elemH,
|
||||
element: this._currentLoadData[uuid]?.element
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
loadUUIDList.length === 0 &&
|
||||
uuids.length === 0 &&
|
||||
status === true
|
||||
) {
|
||||
this._status = LoaderStatus.FREE;
|
||||
this._loadTask();
|
||||
}
|
||||
|
||||
if (this._currentLoadData[uuid]) {
|
||||
this._event.trigger('error', {
|
||||
uuid: uuid,
|
||||
type: this._storageLoadData[uuid]?.type,
|
||||
status: this._storageLoadData[uuid]?.status,
|
||||
content: this._storageLoadData[uuid]?.content,
|
||||
source: this._storageLoadData[uuid]?.source,
|
||||
elemW: this._storageLoadData[uuid]?.elemW,
|
||||
elemH: this._storageLoadData[uuid]?.elemH,
|
||||
element: this._storageLoadData[uuid]?.element
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
@ -311,20 +333,15 @@ export default class Loader {
|
|||
const image = await loadImage(params.source);
|
||||
return image;
|
||||
} else if (params && params.type === 'svg') {
|
||||
const image = await loadSVG(
|
||||
params.source
|
||||
);
|
||||
const image = await loadSVG(params.source);
|
||||
return image;
|
||||
} else if (params && params.type === 'html') {
|
||||
const image = await loadHTML(
|
||||
params.source, {
|
||||
width: params.elemW, height: params.elemH
|
||||
}
|
||||
);
|
||||
const image = await loadHTML(params.source, {
|
||||
width: params.elemW,
|
||||
height: params.elemH
|
||||
});
|
||||
return image;
|
||||
}
|
||||
throw Error('Element\'s source is not support!');
|
||||
throw Error("Element's source is not support!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { TypeData, TypeElement, TypeElemDesc } from '@idraw/types';
|
||||
import { IDrawData, DataElement, DataElemDesc } from '@idraw/types';
|
||||
import { elementNames } from './../constant/element';
|
||||
|
||||
export function parseData(data: any): TypeData {
|
||||
const result: TypeData = {
|
||||
export function parseData(data: any): IDrawData {
|
||||
const result: IDrawData = {
|
||||
elements: []
|
||||
};
|
||||
if (Array.isArray(data?.elements)) {
|
||||
|
|
@ -18,7 +18,7 @@ export function parseData(data: any): TypeData {
|
|||
return result;
|
||||
}
|
||||
|
||||
function isElement(elem: TypeElement<keyof TypeElemDesc>): boolean {
|
||||
function isElement(elem: DataElement<keyof DataElemDesc>): boolean {
|
||||
if (
|
||||
!(
|
||||
isNumber(elem.x) &&
|
||||
|
|
|
|||
|
|
@ -1,30 +1,39 @@
|
|||
import { TypeElement, TypeElemDesc } from '@idraw/types';
|
||||
|
||||
import { DataElement, DataElemDesc } from '@idraw/types';
|
||||
|
||||
export type TypeRendererEventArgMap = {
|
||||
'drawFrame': { t: number };
|
||||
'drawFrameComplete': { t: number };
|
||||
'load': { element: TypeElement<keyof TypeElemDesc> },
|
||||
'loadComplete': { t: number },
|
||||
'error': { element: TypeElement<keyof TypeElemDesc>, error: any }
|
||||
}
|
||||
|
||||
export interface TypeRendererEvent {
|
||||
on<T extends keyof TypeRendererEventArgMap >(key: T, callback: (p: TypeRendererEventArgMap[T]) => void): void
|
||||
off<T extends keyof TypeRendererEventArgMap >(key: T, callback: (p: TypeRendererEventArgMap[T]) => void): void
|
||||
trigger<T extends keyof TypeRendererEventArgMap >(key: T, p: TypeRendererEventArgMap[T]): void
|
||||
}
|
||||
drawFrame: { t: number };
|
||||
drawFrameComplete: { t: number };
|
||||
load: { element: DataElement<keyof DataElemDesc> };
|
||||
loadComplete: { t: number };
|
||||
error: { element: DataElement<keyof DataElemDesc>; error: any };
|
||||
};
|
||||
|
||||
export interface TypeRendererEvent {
|
||||
on<T extends keyof TypeRendererEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeRendererEventArgMap[T]) => void
|
||||
): void;
|
||||
off<T extends keyof TypeRendererEventArgMap>(
|
||||
key: T,
|
||||
callback: (p: TypeRendererEventArgMap[T]) => void
|
||||
): void;
|
||||
trigger<T extends keyof TypeRendererEventArgMap>(
|
||||
key: T,
|
||||
p: TypeRendererEventArgMap[T]
|
||||
): void;
|
||||
}
|
||||
|
||||
export class RendererEvent implements TypeRendererEvent {
|
||||
|
||||
private _listeners: Map<string, ((p: any) => void)[]>;
|
||||
|
||||
constructor() {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
|
||||
on<T extends keyof TypeRendererEventArgMap >(eventKey: T, callback: (p: TypeRendererEventArgMap[T]) => void) {
|
||||
on<T extends keyof TypeRendererEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeRendererEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
callbacks?.push(callback);
|
||||
|
|
@ -33,8 +42,11 @@ export class RendererEvent implements TypeRendererEvent {
|
|||
this._listeners.set(eventKey, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
off<T extends keyof TypeRendererEventArgMap >(eventKey: T, callback: (p: TypeRendererEventArgMap[T]) => void) {
|
||||
|
||||
off<T extends keyof TypeRendererEventArgMap>(
|
||||
eventKey: T,
|
||||
callback: (p: TypeRendererEventArgMap[T]) => void
|
||||
) {
|
||||
if (this._listeners.has(eventKey)) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
|
|
@ -49,7 +61,10 @@ export class RendererEvent implements TypeRendererEvent {
|
|||
}
|
||||
}
|
||||
|
||||
trigger<T extends keyof TypeRendererEventArgMap >(eventKey: T, arg: TypeRendererEventArgMap[T]) {
|
||||
trigger<T extends keyof TypeRendererEventArgMap>(
|
||||
eventKey: T,
|
||||
arg: TypeRendererEventArgMap[T]
|
||||
) {
|
||||
const callbacks = this._listeners.get(eventKey);
|
||||
if (Array.isArray(callbacks)) {
|
||||
callbacks.forEach((cb) => {
|
||||
|
|
@ -61,14 +76,14 @@ export class RendererEvent implements TypeRendererEvent {
|
|||
}
|
||||
}
|
||||
|
||||
has<T extends keyof TypeRendererEventArgMap> (name: string) {
|
||||
has<T extends keyof TypeRendererEventArgMap>(name: string) {
|
||||
if (this._listeners.has(name)) {
|
||||
const list: ((p: TypeRendererEventArgMap[T]) => void)[] | undefined = this._listeners.get(name);
|
||||
const list: ((p: TypeRendererEventArgMap[T]) => void)[] | undefined =
|
||||
this._listeners.get(name);
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { TypeHelperWrapperControllerDirection, TypePoint } from '@idraw/types';
|
||||
import { HelperWrapperControllerDirection, Point } from '@idraw/types';
|
||||
import { Mode, CursorStatus } from './../constant/static';
|
||||
|
||||
type TempDataDesc = {
|
||||
hasInited: boolean;
|
||||
onlyRender: boolean;
|
||||
mode: Mode,
|
||||
cursorStatus: CursorStatus
|
||||
selectedUUID: string | null,
|
||||
selectedUUIDList: string[],
|
||||
hoverUUID: string | null,
|
||||
selectedControllerDirection: TypeHelperWrapperControllerDirection | null,
|
||||
hoverControllerDirection: TypeHelperWrapperControllerDirection | null,
|
||||
prevPoint: TypePoint | null,
|
||||
}
|
||||
mode: Mode;
|
||||
cursorStatus: CursorStatus;
|
||||
selectedUUID: string | null;
|
||||
selectedUUIDList: string[];
|
||||
hoverUUID: string | null;
|
||||
selectedControllerDirection: HelperWrapperControllerDirection | null;
|
||||
hoverControllerDirection: HelperWrapperControllerDirection | null;
|
||||
prevPoint: Point | null;
|
||||
};
|
||||
|
||||
function createData(): TempDataDesc {
|
||||
return {
|
||||
|
|
@ -25,28 +25,26 @@ function createData(): TempDataDesc {
|
|||
hoverUUID: null,
|
||||
selectedControllerDirection: null,
|
||||
hoverControllerDirection: null,
|
||||
prevPoint: null,
|
||||
}
|
||||
prevPoint: null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export class TempData {
|
||||
|
||||
private _temp: TempDataDesc
|
||||
private _temp: TempDataDesc;
|
||||
|
||||
constructor() {
|
||||
this._temp = createData();
|
||||
}
|
||||
|
||||
set<T extends keyof TempDataDesc >(name: T, value: TempDataDesc[T]) {
|
||||
set<T extends keyof TempDataDesc>(name: T, value: TempDataDesc[T]) {
|
||||
this._temp[name] = value;
|
||||
}
|
||||
|
||||
get<T extends keyof TempDataDesc >(name: T): TempDataDesc[T] {
|
||||
get<T extends keyof TempDataDesc>(name: T): TempDataDesc[T] {
|
||||
return this._temp[name];
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._temp = createData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,35 @@
|
|||
import {
|
||||
TypeContext,
|
||||
TypePoint,
|
||||
TypeElement,
|
||||
TypeElemDesc,
|
||||
} from '@idraw/types';
|
||||
import { IDrawContext, Point, DataElement, DataElemDesc } from '@idraw/types';
|
||||
import { calcElementCenter, parseAngleToRadian } from './calculate';
|
||||
|
||||
function rotateElement(
|
||||
ctx: TypeContext,
|
||||
elem: TypeElement<keyof TypeElemDesc>,
|
||||
callback: (ctx: TypeContext) => void
|
||||
ctx: IDrawContext,
|
||||
elem: DataElement<keyof DataElemDesc>,
|
||||
callback: (ctx: IDrawContext) => void
|
||||
): void {
|
||||
const center: TypePoint = calcElementCenter(elem);
|
||||
const center: Point = calcElementCenter(elem);
|
||||
const radian = parseAngleToRadian(elem.angle || 0);
|
||||
return rotateContext(ctx, center, radian || 0, callback);
|
||||
}
|
||||
|
||||
|
||||
function rotateContext(
|
||||
ctx: TypeContext,
|
||||
center: TypePoint | undefined,
|
||||
ctx: IDrawContext,
|
||||
center: Point | undefined,
|
||||
radian: number,
|
||||
callback: (ctx: TypeContext) => void
|
||||
callback: (ctx: IDrawContext) => void
|
||||
): void {
|
||||
if (center && (radian > 0 || radian < 0)) {
|
||||
ctx.translate(center.x, center.y);
|
||||
ctx.rotate(radian);
|
||||
ctx.translate(- center.x, - center.y);
|
||||
ctx.translate(-center.x, -center.y);
|
||||
}
|
||||
|
||||
callback(ctx);
|
||||
|
||||
if (center && (radian > 0 || radian < 0)) {
|
||||
ctx.translate(center.x, center.y);
|
||||
ctx.rotate(- radian);
|
||||
ctx.translate(- center.x, - center.y);
|
||||
ctx.rotate(-radian);
|
||||
ctx.translate(-center.x, -center.y);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
rotateContext,
|
||||
rotateElement,
|
||||
};
|
||||
export { rotateContext, rotateElement };
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
const _queue = Symbol('_queue');
|
||||
const _ctx = Symbol('_ctx');
|
||||
const _status = Symbol('_status');
|
||||
const _loader = Symbol('_loader');
|
||||
const _opts = Symbol('_opts');
|
||||
const _freeze = Symbol('_freeze');
|
||||
const _drawFrame = Symbol('_drawFrame');
|
||||
const _retainQueueOneItem = Symbol('_retainQueueOneItem');
|
||||
|
||||
export {
|
||||
_queue, _ctx, _status, _loader, _opts, _freeze,
|
||||
_drawFrame, _retainQueueOneItem
|
||||
}
|
||||
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
type TypePoint = {
|
||||
type Point = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
type TypeBoardScrollConfig = {
|
||||
type BoardScrollConfig = {
|
||||
color: string;
|
||||
width: number;
|
||||
showBackground?: boolean;
|
||||
};
|
||||
|
||||
type TypeBoardSizeOptions = {
|
||||
type BoardSizeOptions = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
contextWidth?: number;
|
||||
|
|
@ -17,16 +17,16 @@ type TypeBoardSizeOptions = {
|
|||
devicePixelRatio?: number;
|
||||
};
|
||||
|
||||
type TypeBoardOptions = TypeBoardSizeOptions & {
|
||||
type BoardOptions = BoardSizeOptions & {
|
||||
width: number;
|
||||
height: number;
|
||||
contextWidth: number;
|
||||
contextHeight: number;
|
||||
canScroll?: boolean;
|
||||
scrollConfig?: TypeBoardScrollConfig;
|
||||
scrollConfig?: BoardScrollConfig;
|
||||
};
|
||||
|
||||
type TypePointCursor =
|
||||
type PointCursor =
|
||||
| 'auto'
|
||||
| 'move'
|
||||
| 'n-resize'
|
||||
|
|
@ -40,9 +40,9 @@ type TypePointCursor =
|
|||
| 'grab';
|
||||
|
||||
export {
|
||||
TypePoint,
|
||||
TypePointCursor,
|
||||
TypeBoardSizeOptions,
|
||||
TypeBoardOptions,
|
||||
TypeBoardScrollConfig
|
||||
Point,
|
||||
PointCursor,
|
||||
BoardSizeOptions,
|
||||
BoardOptions,
|
||||
BoardScrollConfig
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,38 +1,35 @@
|
|||
type TypeIs = {
|
||||
x: (value: any) => boolean,
|
||||
y: (value: any) => boolean,
|
||||
w: (value: any) => boolean,
|
||||
h: (value: any) => boolean,
|
||||
angle: (value: any) => boolean,
|
||||
number: (value: any) => boolean,
|
||||
borderWidth: (value: any) => boolean,
|
||||
borderRadius: (value: any) => boolean,
|
||||
color: (value: any) => boolean,
|
||||
imageSrc: (value: any) => boolean,
|
||||
imageURL: (value: any) => boolean,
|
||||
imageBase64: (value: any) => boolean,
|
||||
svg: (value: any) => boolean,
|
||||
html: (value: any) => boolean,
|
||||
text: (value: any) => boolean,
|
||||
fontSize: (value: any) => boolean,
|
||||
fontWeight: (value: any) => boolean,
|
||||
lineHeight: (value: any) => boolean,
|
||||
textAlign: (value: any) => boolean,
|
||||
fontFamily: (value: any) => boolean,
|
||||
strokeWidth: (value: any) => boolean,
|
||||
}
|
||||
type IsTypeUtil = {
|
||||
x: (value: any) => boolean;
|
||||
y: (value: any) => boolean;
|
||||
w: (value: any) => boolean;
|
||||
h: (value: any) => boolean;
|
||||
angle: (value: any) => boolean;
|
||||
number: (value: any) => boolean;
|
||||
borderWidth: (value: any) => boolean;
|
||||
borderRadius: (value: any) => boolean;
|
||||
color: (value: any) => boolean;
|
||||
imageSrc: (value: any) => boolean;
|
||||
imageURL: (value: any) => boolean;
|
||||
imageBase64: (value: any) => boolean;
|
||||
svg: (value: any) => boolean;
|
||||
html: (value: any) => boolean;
|
||||
text: (value: any) => boolean;
|
||||
fontSize: (value: any) => boolean;
|
||||
fontWeight: (value: any) => boolean;
|
||||
lineHeight: (value: any) => boolean;
|
||||
textAlign: (value: any) => boolean;
|
||||
fontFamily: (value: any) => boolean;
|
||||
strokeWidth: (value: any) => boolean;
|
||||
};
|
||||
|
||||
type TypeCheck = {
|
||||
attrs: (value: any) => boolean,
|
||||
rectDesc: (value: any) => boolean,
|
||||
circleDesc: (value: any) => boolean,
|
||||
imageDesc: (value: any) => boolean,
|
||||
svgDesc: (value: any) => boolean,
|
||||
htmlDesc: (value: any) => boolean,
|
||||
textDesc: (value: any) => boolean,
|
||||
}
|
||||
type CheckTypeUtil = {
|
||||
attrs: (value: any) => boolean;
|
||||
rectDesc: (value: any) => boolean;
|
||||
circleDesc: (value: any) => boolean;
|
||||
imageDesc: (value: any) => boolean;
|
||||
svgDesc: (value: any) => boolean;
|
||||
htmlDesc: (value: any) => boolean;
|
||||
textDesc: (value: any) => boolean;
|
||||
};
|
||||
|
||||
export {
|
||||
TypeIs,
|
||||
TypeCheck,
|
||||
}
|
||||
export { IsTypeUtil, CheckTypeUtil };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
type TypeConfig = {
|
||||
type IDrawConfig = {
|
||||
elementWrapper?: {
|
||||
color?: string;
|
||||
controllerSize?: number;
|
||||
|
|
@ -13,7 +13,7 @@ type TypeConfig = {
|
|||
};
|
||||
};
|
||||
|
||||
type TypeConfigStrict = TypeConfig & {
|
||||
type IDrawConfigStrict = IDrawConfig & {
|
||||
elementWrapper: {
|
||||
color: string;
|
||||
lockColor: string;
|
||||
|
|
@ -23,4 +23,4 @@ type TypeConfigStrict = TypeConfig & {
|
|||
};
|
||||
};
|
||||
|
||||
export { TypeConfig, TypeConfigStrict };
|
||||
export { IDrawConfig, IDrawConfigStrict };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
interface TypeContext {
|
||||
interface IDrawContext {
|
||||
getContext(): CanvasRenderingContext2D;
|
||||
setTransform(config: {
|
||||
scale?: number;
|
||||
|
|
@ -67,5 +67,5 @@ interface TypeContext {
|
|||
}
|
||||
|
||||
export {
|
||||
TypeContext
|
||||
IDrawContext
|
||||
};
|
||||
|
|
@ -1,13 +1,10 @@
|
|||
|
||||
type TypeCoreOptions = {
|
||||
type CoreOptions = {
|
||||
width: number;
|
||||
height: number;
|
||||
devicePixelRatio: number;
|
||||
contextWidth: number;
|
||||
contextHeight: number;
|
||||
onlyRender?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
TypeCoreOptions
|
||||
};
|
||||
export { CoreOptions };
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
import { TypeElemDesc, TypeElement, TypeElementBase } from './element';
|
||||
import { DataElemDesc, DataElement, DataElementBase } from './element';
|
||||
|
||||
type TypeDataBase = {
|
||||
elements: TypeElementBase<keyof TypeElemDesc>[];
|
||||
type IDrawDataBase = {
|
||||
elements: DataElementBase<keyof DataElemDesc>[];
|
||||
bgColor?: string;
|
||||
}
|
||||
};
|
||||
|
||||
type TypeData = {
|
||||
elements: TypeElement<keyof TypeElemDesc>[];
|
||||
type IDrawData = {
|
||||
elements: DataElement<keyof DataElemDesc>[];
|
||||
bgColor?: string;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export {
|
||||
TypeData,
|
||||
TypeDataBase,
|
||||
};
|
||||
export { IDrawData, IDrawDataBase };
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
type TypeDeviceSize = {
|
||||
type DeviceSize = {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
TypeDeviceSize,
|
||||
};
|
||||
export { DeviceSize };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// import { TypePaintData } from './paint';
|
||||
// import { PaintData } from './paint';
|
||||
|
||||
type TypeElementAttrs = {
|
||||
type DataElementAttrs = {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
|
|
@ -16,43 +16,43 @@ type TypeElementAttrs = {
|
|||
extension?: { [key: string]: any } | any;
|
||||
};
|
||||
|
||||
type TypeElementBase<T extends keyof TypeElemDesc | TypeElemType> =
|
||||
TypeElementAttrs & {
|
||||
type DataElementBase<T extends keyof DataElemDesc | DataElemType> =
|
||||
DataElementAttrs & {
|
||||
name?: string;
|
||||
uuid?: string;
|
||||
type: T | TypeElemType;
|
||||
desc: TypeElemDesc[T];
|
||||
type: T | DataElemType;
|
||||
desc: DataElemDesc[T];
|
||||
};
|
||||
|
||||
type TypeElement<T extends keyof TypeElemDesc | TypeElemType> =
|
||||
TypeElementBase<T> & {
|
||||
type DataElement<T extends keyof DataElemDesc | DataElemType> =
|
||||
DataElementBase<T> & {
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
type TypeElemDescBase = {
|
||||
type DataElemDescBase = {
|
||||
shadowColor?: string;
|
||||
shadowOffsetX?: number;
|
||||
shadowOffsetY?: number;
|
||||
shadowBlur?: number;
|
||||
};
|
||||
|
||||
type TypeElemBoxDesc = {
|
||||
type DataElemBoxDesc = {
|
||||
borderRadius?: number;
|
||||
borderWidth?: number;
|
||||
borderColor?: string;
|
||||
} & TypeElemDescBase;
|
||||
} & DataElemDescBase;
|
||||
|
||||
type TypeElemDesc = {
|
||||
text: TypeElemDescText;
|
||||
rect: TypeElemDescRect;
|
||||
circle: TypeElemDescCircle;
|
||||
image: TypeElemDescImage;
|
||||
svg: TypeElemDescSVG;
|
||||
html: TypeElemDescHTML;
|
||||
// paint: TypeElemDescPaint,
|
||||
type DataElemDesc = {
|
||||
text: DataElemDescText;
|
||||
rect: DataElemDescRect;
|
||||
circle: DataElemDescCircle;
|
||||
image: DataElemDescImage;
|
||||
svg: DataElemDescSVG;
|
||||
html: DataElemDescHTML;
|
||||
// paint: DataElemDescPaint,
|
||||
};
|
||||
|
||||
// enum TypeElemType {
|
||||
// enum DataElemType {
|
||||
// text = 'text',
|
||||
// rect = 'rect',
|
||||
// circle = 'circle',
|
||||
|
|
@ -61,13 +61,13 @@ type TypeElemDesc = {
|
|||
// html = 'html',
|
||||
// }
|
||||
|
||||
type TypeElemType = 'text' | 'rect' | 'circle' | 'image' | 'svg' | 'html';
|
||||
type DataElemType = 'text' | 'rect' | 'circle' | 'image' | 'svg' | 'html';
|
||||
|
||||
type TypeElemDescRect = {
|
||||
type DataElemDescRect = {
|
||||
bgColor?: string;
|
||||
} & TypeElemBoxDesc;
|
||||
} & DataElemBoxDesc;
|
||||
|
||||
type TypeElemDescText = {
|
||||
type DataElemDescText = {
|
||||
text: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
|
|
@ -83,38 +83,38 @@ type TypeElemDescText = {
|
|||
textShadowOffsetX?: number;
|
||||
textShadowOffsetY?: number;
|
||||
textShadowBlur?: number;
|
||||
} & TypeElemBoxDesc;
|
||||
} & DataElemBoxDesc;
|
||||
|
||||
type TypeElemDescCircle = {
|
||||
type DataElemDescCircle = {
|
||||
bgColor: string;
|
||||
} & TypeElemBoxDesc;
|
||||
} & DataElemBoxDesc;
|
||||
|
||||
type TypeElemDescImage = {
|
||||
type DataElemDescImage = {
|
||||
src: string;
|
||||
} & TypeElemDescBase;
|
||||
} & DataElemDescBase;
|
||||
|
||||
type TypeElemDescSVG = {
|
||||
type DataElemDescSVG = {
|
||||
svg: string;
|
||||
};
|
||||
|
||||
type TypeElemDescHTML = {
|
||||
type DataElemDescHTML = {
|
||||
html: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
// type TypeElemDescPaint = TypePaintData
|
||||
// type DataElemDescPaint = PaintData
|
||||
|
||||
export {
|
||||
TypeElementAttrs,
|
||||
TypeElemDescText,
|
||||
TypeElemDescRect,
|
||||
TypeElemDescCircle,
|
||||
TypeElemDescImage,
|
||||
TypeElemDescSVG,
|
||||
TypeElemDescHTML,
|
||||
TypeElemDesc,
|
||||
TypeElemType,
|
||||
TypeElement,
|
||||
TypeElementBase
|
||||
DataElementAttrs,
|
||||
DataElemDescText,
|
||||
DataElemDescRect,
|
||||
DataElemDescCircle,
|
||||
DataElemDescImage,
|
||||
DataElemDescSVG,
|
||||
DataElemDescHTML,
|
||||
DataElemDesc,
|
||||
DataElemType,
|
||||
DataElement,
|
||||
DataElementBase
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,56 +1,55 @@
|
|||
import { TypeData } from './data';
|
||||
import { TypePoint } from './board';
|
||||
import { IDrawData } from './data';
|
||||
import { Point } from './board';
|
||||
|
||||
// type test = {[uuid string]: TypeElement}
|
||||
// type test = {[uuid string]: DataElement}
|
||||
|
||||
type TypeController = TypePoint & {
|
||||
type HelperController = Point & {
|
||||
invisible?: boolean;
|
||||
};
|
||||
|
||||
type TypeHeplerSelectedElementWrapper = {
|
||||
type HeplerSelectedElementWrapper = {
|
||||
uuid: string;
|
||||
controllerSize: number;
|
||||
controllerOffset: number;
|
||||
lock: boolean;
|
||||
controllers: {
|
||||
topLeft: TypeController,
|
||||
top: TypeController,
|
||||
topRight: TypeController,
|
||||
right: TypeController,
|
||||
bottomRight: TypeController,
|
||||
bottom: TypeController,
|
||||
bottomLeft: TypeController,
|
||||
left: TypeController,
|
||||
rotate: TypeController,
|
||||
},
|
||||
topLeft: HelperController;
|
||||
top: HelperController;
|
||||
topRight: HelperController;
|
||||
right: HelperController;
|
||||
bottomRight: HelperController;
|
||||
bottom: HelperController;
|
||||
bottomLeft: HelperController;
|
||||
left: HelperController;
|
||||
rotate: HelperController;
|
||||
};
|
||||
lineDash: number[];
|
||||
lineWidth: number;
|
||||
color: string;
|
||||
radian?: number;
|
||||
translate?: TypePoint;
|
||||
}
|
||||
translate?: Point;
|
||||
};
|
||||
|
||||
type TypeHeplerSelectedAreaWrapper = {
|
||||
type HeplerSelectedAreaWrapper = {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
startPoint: TypePoint;
|
||||
endPoint: TypePoint;
|
||||
startPoint: Point;
|
||||
endPoint: Point;
|
||||
lineWidth: number;
|
||||
lineDash: number[];
|
||||
color: string;
|
||||
}
|
||||
};
|
||||
|
||||
type TypeHelperConfig = {
|
||||
elementIndexMap: {[key: string]: number},
|
||||
selectedAreaWrapper?: TypeHeplerSelectedAreaWrapper;
|
||||
selectedElementWrapper?: TypeHeplerSelectedElementWrapper,
|
||||
selectedElementListWrappers?: Array<TypeHeplerSelectedElementWrapper>;
|
||||
}
|
||||
type HelperConfig = {
|
||||
elementIndexMap: { [key: string]: number };
|
||||
selectedAreaWrapper?: HeplerSelectedAreaWrapper;
|
||||
selectedElementWrapper?: HeplerSelectedElementWrapper;
|
||||
selectedElementListWrappers?: Array<HeplerSelectedElementWrapper>;
|
||||
};
|
||||
|
||||
|
||||
type TypeHelperUpdateOpts = {
|
||||
type HelperUpdateOpts = {
|
||||
width: number;
|
||||
height: number;
|
||||
selectedUUID?: string | null;
|
||||
|
|
@ -60,26 +59,29 @@ type TypeHelperUpdateOpts = {
|
|||
canScroll: boolean;
|
||||
scrollX: number;
|
||||
scrollY: number;
|
||||
}
|
||||
};
|
||||
|
||||
interface TypeHelper {
|
||||
updateConfig(
|
||||
data: TypeData,
|
||||
opts: TypeHelperUpdateOpts
|
||||
): void;
|
||||
getConfig(): TypeHelperConfig;
|
||||
}
|
||||
// interface Helper {
|
||||
// updateConfig(data: IDrawData, opts: HelperUpdateOpts): void;
|
||||
// getConfig(): HelperConfig;
|
||||
// }
|
||||
|
||||
type TypeHelperWrapperControllerDirection
|
||||
= 'top-left' | 'top' | 'top-right' | 'right'
|
||||
| 'bottom-right' | 'bottom' | 'bottom-left' | 'left'
|
||||
| 'rotate';
|
||||
type HelperWrapperControllerDirection =
|
||||
| 'top-left'
|
||||
| 'top'
|
||||
| 'top-right'
|
||||
| 'right'
|
||||
| 'bottom-right'
|
||||
| 'bottom'
|
||||
| 'bottom-left'
|
||||
| 'left'
|
||||
| 'rotate';
|
||||
|
||||
export {
|
||||
TypeHelper,
|
||||
TypeHelperConfig,
|
||||
TypeHelperUpdateOpts,
|
||||
TypeHelperWrapperControllerDirection,
|
||||
TypeHeplerSelectedElementWrapper,
|
||||
TypeHeplerSelectedAreaWrapper,
|
||||
};
|
||||
// Helper,
|
||||
HelperConfig,
|
||||
HelperUpdateOpts,
|
||||
HelperWrapperControllerDirection,
|
||||
HeplerSelectedElementWrapper,
|
||||
HeplerSelectedAreaWrapper
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
export type TypePaintData = {
|
||||
brushMap: {[name: string]: TypePaintBrush},
|
||||
paths: TypePaintPath[],
|
||||
}
|
||||
export type PaintData = {
|
||||
brushMap: { [name: string]: PaintBrush };
|
||||
paths: PaintPath[];
|
||||
};
|
||||
|
||||
export type TypePaintBrush = {
|
||||
export type PaintBrush = {
|
||||
name: string;
|
||||
src: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type TypePaintPath = {
|
||||
brush: string,
|
||||
export type PaintPath = {
|
||||
brush: string;
|
||||
size: number;
|
||||
positions: TypePaintPosition[],
|
||||
positions: PaintPosition[];
|
||||
color: string;
|
||||
pressure: number;
|
||||
}
|
||||
};
|
||||
|
||||
export type TypePaintPosition = {
|
||||
x: number,
|
||||
y: number,
|
||||
t: number,
|
||||
}
|
||||
export type PaintPosition = {
|
||||
x: number;
|
||||
y: number;
|
||||
t: number;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,36 +1,39 @@
|
|||
import { TypeData } from './data';
|
||||
import { TypeElemDesc, TypeElement } from './element';
|
||||
import { TypeContext } from './context';
|
||||
import { TypePoint, TypePointCursor } from './board';
|
||||
import { IDrawData } from './data';
|
||||
import { DataElemDesc, DataElement } from './element';
|
||||
import { IDrawContext } from './context';
|
||||
import { Point, PointCursor } from './board';
|
||||
|
||||
export type TypeHelperPluginEventDetail = {
|
||||
controller: string | null,
|
||||
point: TypePoint,
|
||||
selectedElement: TypeElement<keyof TypeElemDesc> | null,
|
||||
data: TypeData,
|
||||
helperCtx:TypeContext,
|
||||
}
|
||||
export type HelperPluginEventDetail = {
|
||||
controller: string | null;
|
||||
point: Point;
|
||||
selectedElement: DataElement<keyof DataElemDesc> | null;
|
||||
data: IDrawData;
|
||||
helperCtx: IDrawContext;
|
||||
};
|
||||
|
||||
export type TypeHelperPluginEventResult = {
|
||||
cursor?: TypePointCursor,
|
||||
export type HelperPluginEventResult = {
|
||||
cursor?: PointCursor;
|
||||
beController?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export interface InterfaceHelperPlugin {
|
||||
|
||||
readonly name?: string;
|
||||
|
||||
readonly uuid?: string;
|
||||
|
||||
onHover?: (detail: TypeHelperPluginEventDetail) => void | TypeHelperPluginEventResult;
|
||||
|
||||
onPoint?: (detail: TypeHelperPluginEventDetail) => void | TypeHelperPluginEventResult;
|
||||
onHover?: (detail: HelperPluginEventDetail) => void | HelperPluginEventResult;
|
||||
|
||||
onClick?: (detail: TypeHelperPluginEventDetail) => void | TypeHelperPluginEventResult;
|
||||
onPoint?: (detail: HelperPluginEventDetail) => void | HelperPluginEventResult;
|
||||
|
||||
onMoveStart?: (detail: TypeHelperPluginEventDetail) => void | TypeHelperPluginEventResult;
|
||||
onClick?: (detail: HelperPluginEventDetail) => void | HelperPluginEventResult;
|
||||
|
||||
onMove?: (detail: TypeHelperPluginEventDetail) => void | TypeHelperPluginEventResult;
|
||||
onMoveStart?: (
|
||||
detail: HelperPluginEventDetail
|
||||
) => void | HelperPluginEventResult;
|
||||
|
||||
onMoveEnd?: (detail: TypeHelperPluginEventDetail) => void | TypeHelperPluginEventResult;
|
||||
}
|
||||
onMove?: (detail: HelperPluginEventDetail) => void | HelperPluginEventResult;
|
||||
|
||||
onMoveEnd?: (
|
||||
detail: HelperPluginEventDetail
|
||||
) => void | HelperPluginEventResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,27 @@
|
|||
type TypeScreenData = {
|
||||
type ScreenData = {
|
||||
scale: number;
|
||||
scrollLeft: number;
|
||||
scrollTop: number;
|
||||
// selectedElementUUID: string | null;
|
||||
}
|
||||
};
|
||||
|
||||
type TypeScreenPosition = {
|
||||
type ScreenPosition = {
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
}
|
||||
};
|
||||
|
||||
type TypeScreenSize = {
|
||||
type ScreenSize = {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
};
|
||||
|
||||
type TypeScreenContext = {
|
||||
size: TypeScreenSize,
|
||||
position: TypeScreenPosition
|
||||
}
|
||||
type ScreenContext = {
|
||||
size: ScreenSize;
|
||||
position: ScreenPosition;
|
||||
};
|
||||
|
||||
export {
|
||||
TypeScreenData,
|
||||
TypeScreenPosition,
|
||||
TypeScreenSize,
|
||||
TypeScreenContext,
|
||||
};
|
||||
export { ScreenData, ScreenPosition, ScreenSize, ScreenContext };
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
|
||||
// import { TypeElementAttrs } from '@idraw/types';
|
||||
// import { DataElementAttrs } from '@idraw/types';
|
||||
import is from './is';
|
||||
|
||||
function attrs(
|
||||
attrs: any
|
||||
): boolean {
|
||||
function attrs(attrs: any): boolean {
|
||||
const { x, y, w, h, angle } = attrs;
|
||||
if (!(is.x(x) && is.y(y) && is.w(w) && is.h(h) && is.angle(angle))) {
|
||||
return false;
|
||||
}
|
||||
if (!(angle >= -360 && angle <= 360 )) {
|
||||
if (!(angle >= -360 && angle <= 360)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function box(
|
||||
desc: any = {},
|
||||
): boolean {
|
||||
function box(desc: any = {}): boolean {
|
||||
const { borderColor, borderRadius, borderWidth } = desc;
|
||||
if (desc.hasOwnProperty('borderColor') && !is.color(borderColor)) {
|
||||
return false;
|
||||
|
|
@ -31,9 +26,7 @@ function box(
|
|||
return true;
|
||||
}
|
||||
|
||||
function rectDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function rectDesc(desc: any): boolean {
|
||||
const { bgColor } = desc;
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)) {
|
||||
return false;
|
||||
|
|
@ -44,9 +37,7 @@ function rectDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function circleDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function circleDesc(desc: any): boolean {
|
||||
const { bgColor, borderColor, borderWidth } = desc;
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)) {
|
||||
return false;
|
||||
|
|
@ -60,10 +51,7 @@ function circleDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
function imageDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function imageDesc(desc: any): boolean {
|
||||
const { src } = desc;
|
||||
if (!is.imageSrc(src)) {
|
||||
return false;
|
||||
|
|
@ -71,9 +59,7 @@ function imageDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function svgDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function svgDesc(desc: any): boolean {
|
||||
const { svg } = desc;
|
||||
if (!is.svg(svg)) {
|
||||
return false;
|
||||
|
|
@ -81,9 +67,7 @@ function svgDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function htmlDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function htmlDesc(desc: any): boolean {
|
||||
const { html } = desc;
|
||||
if (!is.html(html)) {
|
||||
return false;
|
||||
|
|
@ -91,44 +75,50 @@ function htmlDesc(
|
|||
return true;
|
||||
}
|
||||
|
||||
function textDesc(
|
||||
desc: any
|
||||
): boolean {
|
||||
function textDesc(desc: any): boolean {
|
||||
const {
|
||||
text, color, fontSize, lineHeight, fontFamily, textAlign,
|
||||
fontWeight, bgColor, strokeWidth, strokeColor
|
||||
text,
|
||||
color,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
fontFamily,
|
||||
textAlign,
|
||||
fontWeight,
|
||||
bgColor,
|
||||
strokeWidth,
|
||||
strokeColor
|
||||
} = desc;
|
||||
if (!is.text(text)){
|
||||
if (!is.text(text)) {
|
||||
return false;
|
||||
}
|
||||
if (!is.color(color)){
|
||||
if (!is.color(color)) {
|
||||
return false;
|
||||
}
|
||||
if (!is.fontSize(fontSize)){
|
||||
if (!is.fontSize(fontSize)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)){
|
||||
if (desc.hasOwnProperty('bgColor') && !is.color(bgColor)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('fontWeight') && !is.fontWeight(fontWeight)){
|
||||
if (desc.hasOwnProperty('fontWeight') && !is.fontWeight(fontWeight)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('lineHeight') && !is.lineHeight(lineHeight)){
|
||||
if (desc.hasOwnProperty('lineHeight') && !is.lineHeight(lineHeight)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('fontFamily') && !is.fontFamily(fontFamily)){
|
||||
if (desc.hasOwnProperty('fontFamily') && !is.fontFamily(fontFamily)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('textAlign') && !is.textAlign(textAlign)){
|
||||
if (desc.hasOwnProperty('textAlign') && !is.textAlign(textAlign)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('strokeWidth') && !is.strokeWidth(strokeWidth)){
|
||||
if (desc.hasOwnProperty('strokeWidth') && !is.strokeWidth(strokeWidth)) {
|
||||
return false;
|
||||
}
|
||||
if (desc.hasOwnProperty('strokeColor') && !is.color(strokeColor)){
|
||||
if (desc.hasOwnProperty('strokeColor') && !is.color(strokeColor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!box(desc)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -142,8 +132,7 @@ const check = {
|
|||
circleDesc,
|
||||
imageDesc,
|
||||
svgDesc,
|
||||
htmlDesc,
|
||||
htmlDesc
|
||||
};
|
||||
|
||||
|
||||
export default check;
|
||||
export default check;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TypeContext, TypeBoardSizeOptions } from '@idraw/types';
|
||||
import { IDrawContext, BoardSizeOptions } from '@idraw/types';
|
||||
|
||||
type Options = {
|
||||
width: number;
|
||||
|
|
@ -6,24 +6,24 @@ type Options = {
|
|||
contextWidth: number;
|
||||
contextHeight: number;
|
||||
devicePixelRatio: number;
|
||||
}
|
||||
};
|
||||
|
||||
type Transform = {
|
||||
scale?: number;
|
||||
scrollX?: number;
|
||||
scrollY?: number;
|
||||
}
|
||||
};
|
||||
|
||||
type PrivateTransform = {
|
||||
scale: number;
|
||||
scrollX: number;
|
||||
scrollY: number;
|
||||
}
|
||||
};
|
||||
|
||||
class Context implements TypeContext {
|
||||
class Context implements IDrawContext {
|
||||
private _opts: Options;
|
||||
private _ctx: CanvasRenderingContext2D;
|
||||
private _transform: PrivateTransform;
|
||||
private _transform: PrivateTransform;
|
||||
|
||||
// private _scale: number;
|
||||
// private _scrollX: number;
|
||||
|
|
@ -35,15 +35,15 @@ class Context implements TypeContext {
|
|||
this._transform = {
|
||||
scale: 1,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
scrollY: 0
|
||||
};
|
||||
}
|
||||
getContext(): CanvasRenderingContext2D {
|
||||
return this._ctx;
|
||||
}
|
||||
|
||||
resetSize(opts: TypeBoardSizeOptions) {
|
||||
this._opts = {...this._opts, ...opts};
|
||||
resetSize(opts: BoardSizeOptions) {
|
||||
this._opts = { ...this._opts, ...opts };
|
||||
}
|
||||
|
||||
calcDeviceNum(num: number): number {
|
||||
|
|
@ -55,24 +55,24 @@ class Context implements TypeContext {
|
|||
}
|
||||
|
||||
getSize() {
|
||||
return {
|
||||
return {
|
||||
width: this._opts.width,
|
||||
height: this._opts.height,
|
||||
contextWidth: this._opts.contextWidth,
|
||||
contextHeight: this._opts.contextHeight,
|
||||
devicePixelRatio: this._opts.devicePixelRatio,
|
||||
devicePixelRatio: this._opts.devicePixelRatio
|
||||
};
|
||||
}
|
||||
|
||||
setTransform(config: Transform) {
|
||||
this._transform = {...this._transform, ...config};
|
||||
this._transform = { ...this._transform, ...config };
|
||||
}
|
||||
|
||||
getTransform() {
|
||||
return {
|
||||
scale: this._transform.scale,
|
||||
scrollX: this._transform.scrollX,
|
||||
scrollY: this._transform.scrollY,
|
||||
scrollY: this._transform.scrollY
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -84,17 +84,36 @@ class Context implements TypeContext {
|
|||
return this._ctx.fill(fillRule || 'nonzero');
|
||||
}
|
||||
|
||||
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean | undefined): void {
|
||||
return this._ctx.arc(this._doSize(x), this._doSize(y), this._doSize(radius), startAngle, endAngle, anticlockwise);
|
||||
arc(
|
||||
x: number,
|
||||
y: number,
|
||||
radius: number,
|
||||
startAngle: number,
|
||||
endAngle: number,
|
||||
anticlockwise?: boolean | undefined
|
||||
): void {
|
||||
return this._ctx.arc(
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(radius),
|
||||
startAngle,
|
||||
endAngle,
|
||||
anticlockwise
|
||||
);
|
||||
}
|
||||
|
||||
rect(x: number, y: number, w: number, h: number) {
|
||||
return this._ctx.rect(this._doSize(x), this._doSize(y), this._doSize(w), this._doSize(h));
|
||||
return this._ctx.rect(
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(w),
|
||||
this._doSize(h)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
fillRect(x: number, y: number, w: number, h: number) {
|
||||
return this._ctx.fillRect(
|
||||
this._doSize(x),
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(w),
|
||||
this._doSize(h)
|
||||
|
|
@ -103,7 +122,7 @@ class Context implements TypeContext {
|
|||
|
||||
clearRect(x: number, y: number, w: number, h: number) {
|
||||
return this._ctx.clearRect(
|
||||
this._doSize(x),
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(w),
|
||||
this._doSize(h)
|
||||
|
|
@ -127,15 +146,21 @@ class Context implements TypeContext {
|
|||
}
|
||||
|
||||
arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void {
|
||||
return this._ctx.arcTo(this._doSize(x1), this._doSize(y1), this._doSize(x2), this._doSize(y2), this._doSize(radius));
|
||||
return this._ctx.arcTo(
|
||||
this._doSize(x1),
|
||||
this._doSize(y1),
|
||||
this._doSize(x2),
|
||||
this._doSize(y2),
|
||||
this._doSize(radius)
|
||||
);
|
||||
}
|
||||
|
||||
setLineWidth(w: number) {
|
||||
return this._ctx.lineWidth = this._doSize(w);
|
||||
return (this._ctx.lineWidth = this._doSize(w));
|
||||
}
|
||||
|
||||
setLineDash(nums: number[]) {
|
||||
return this._ctx.setLineDash(nums.map(n => this._doSize(n)));
|
||||
return this._ctx.setLineDash(nums.map((n) => this._doSize(n)));
|
||||
}
|
||||
|
||||
isPointInPath(x: number, y: number) {
|
||||
|
|
@ -157,7 +182,7 @@ class Context implements TypeContext {
|
|||
translate(x: number, y: number) {
|
||||
return this._ctx.translate(this._doSize(x), this._doSize(y));
|
||||
}
|
||||
|
||||
|
||||
rotate(angle: number) {
|
||||
return this._ctx.rotate(angle);
|
||||
}
|
||||
|
|
@ -175,13 +200,32 @@ class Context implements TypeContext {
|
|||
const dh: number = args[args.length - 1];
|
||||
|
||||
if (args.length === 9) {
|
||||
return this._ctx.drawImage(image, this._doSize(sx), this._doSize(sy), this._doSize(sw), this._doSize(sh), this._doSize(dx), this._doSize(dy), this._doSize(dw), this._doSize(dh));
|
||||
return this._ctx.drawImage(
|
||||
image,
|
||||
this._doSize(sx),
|
||||
this._doSize(sy),
|
||||
this._doSize(sw),
|
||||
this._doSize(sh),
|
||||
this._doSize(dx),
|
||||
this._doSize(dy),
|
||||
this._doSize(dw),
|
||||
this._doSize(dh)
|
||||
);
|
||||
} else {
|
||||
return this._ctx.drawImage(image,this._doSize(dx), this._doSize(dy), this._doSize(dw), this._doSize(dh));
|
||||
return this._ctx.drawImage(
|
||||
image,
|
||||
this._doSize(dx),
|
||||
this._doSize(dy),
|
||||
this._doSize(dw),
|
||||
this._doSize(dh)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createPattern(image: CanvasImageSource, repetition: string | null): CanvasPattern | null {
|
||||
createPattern(
|
||||
image: CanvasImageSource,
|
||||
repetition: string | null
|
||||
): CanvasPattern | null {
|
||||
return this._ctx.createPattern(image, repetition);
|
||||
}
|
||||
|
||||
|
|
@ -193,23 +237,47 @@ class Context implements TypeContext {
|
|||
this._ctx.textAlign = align;
|
||||
}
|
||||
|
||||
fillText(text: string, x: number, y: number, maxWidth?: number | undefined): void {
|
||||
fillText(
|
||||
text: string,
|
||||
x: number,
|
||||
y: number,
|
||||
maxWidth?: number | undefined
|
||||
): void {
|
||||
if (maxWidth !== undefined) {
|
||||
return this._ctx.fillText(text, this._doSize(x), this._doSize(y), this._doSize(maxWidth));
|
||||
return this._ctx.fillText(
|
||||
text,
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(maxWidth)
|
||||
);
|
||||
} else {
|
||||
return this._ctx.fillText(text, this._doSize(x), this._doSize(y));
|
||||
}
|
||||
}
|
||||
|
||||
strokeText(text: string, x: number, y: number, maxWidth?: number | undefined): void {
|
||||
strokeText(
|
||||
text: string,
|
||||
x: number,
|
||||
y: number,
|
||||
maxWidth?: number | undefined
|
||||
): void {
|
||||
if (maxWidth !== undefined) {
|
||||
return this._ctx.strokeText(text, this._doSize(x), this._doSize(y), this._doSize(maxWidth));
|
||||
return this._ctx.strokeText(
|
||||
text,
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(maxWidth)
|
||||
);
|
||||
} else {
|
||||
return this._ctx.strokeText(text, this._doSize(x), this._doSize(y));
|
||||
}
|
||||
}
|
||||
|
||||
setFont(opts: { fontSize: number, fontFamily?: string, fontWeight?: 'bold' }): void {
|
||||
setFont(opts: {
|
||||
fontSize: number;
|
||||
fontFamily?: string;
|
||||
fontWeight?: 'bold';
|
||||
}): void {
|
||||
const strList: string[] = [];
|
||||
if (opts.fontWeight === 'bold') {
|
||||
strList.push(`${opts.fontWeight}`);
|
||||
|
|
@ -231,7 +299,7 @@ class Context implements TypeContext {
|
|||
save() {
|
||||
this._ctx.save();
|
||||
}
|
||||
|
||||
|
||||
restore() {
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
|
@ -257,10 +325,25 @@ class Context implements TypeContext {
|
|||
}
|
||||
|
||||
ellipse(
|
||||
x: number,y: number, radiusX: number, radiusY: number,
|
||||
rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean | undefined
|
||||
x: number,
|
||||
y: number,
|
||||
radiusX: number,
|
||||
radiusY: number,
|
||||
rotation: number,
|
||||
startAngle: number,
|
||||
endAngle: number,
|
||||
counterclockwise?: boolean | undefined
|
||||
) {
|
||||
this._ctx.ellipse(this._doSize(x), this._doSize(y), this._doSize(radiusX), this._doSize(radiusY), rotation, startAngle, endAngle, counterclockwise)
|
||||
this._ctx.ellipse(
|
||||
this._doSize(x),
|
||||
this._doSize(y),
|
||||
this._doSize(radiusX),
|
||||
this._doSize(radiusY),
|
||||
rotation,
|
||||
startAngle,
|
||||
endAngle,
|
||||
counterclockwise
|
||||
);
|
||||
}
|
||||
|
||||
private _doSize(num: number) {
|
||||
|
|
@ -278,8 +361,6 @@ class Context implements TypeContext {
|
|||
const _y = (y - scrollY) / scale;
|
||||
return this._doSize(_y);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default Context;
|
||||
export default Context;
|
||||
|
|
|
|||