Merge pull request #176 from idrawjs/dev-improve-0.3

Refactor and format code
This commit is contained in:
大深海 2023-04-02 13:34:38 +08:00 committed by GitHub
commit a1ad6abeb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 4795 additions and 4683 deletions

2
.npmrc
View file

@ -1 +1 @@
# registry=https://registry.npmmirror.com
registry=https://registry.npmmirror.com

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 KiB

After

Width:  |  Height:  |  Size: 772 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 161 KiB

View file

@ -1,6 +1,4 @@
module.exports = {
// "collectCoverage": true,
testEnvironment: 'jsdom',
testTimeout: 2 * 60 * 1000,
moduleFileExtensions: ['js', 'ts'],
modulePaths: ['<rootDir>'],

View file

@ -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"
}
}

View file

@ -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);
}
}
}

View file

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

View file

@ -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);
}
}

View file

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

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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();
}
}
}

View file

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

View file

@ -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,
};

View file

@ -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();

View file

@ -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 () => {

View file

@ -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();

View file

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

View file

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

View file

@ -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;

View file

@ -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) {

View file

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

View file

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

View file

@ -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();
});
}
}

View file

@ -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;

View file

@ -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);

View file

@ -1,5 +0,0 @@
class ElementBase {
constructor() {
}
}

View file

@ -1,6 +0,0 @@
class ElementController {
// TODO
}
export default ElementController;

View file

@ -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;

View file

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

View file

@ -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();

View file

@ -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,

View file

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

View file

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

View file

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

View file

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

View file

@ -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;

View file

@ -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,
};

View file

@ -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 {}
}

View file

@ -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();
});
});

View file

@ -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: [
{

View file

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

View file

@ -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();
}
}
}

View file

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

View file

@ -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();
}
}

View file

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

View file

@ -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,
}

View file

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

View file

@ -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];
}
}

View file

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

View file

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

View file

@ -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, () => {

View file

@ -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();
// }
})
}
});
}

View file

@ -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, () => {

View file

@ -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);
// }

View file

@ -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(
}
}
}
}

View file

@ -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);
}

View file

@ -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);
// }

View file

@ -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%">

View file

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

View file

@ -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!");
}
}

View file

@ -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) &&

View file

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

View file

@ -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();
}
}
}

View file

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

View file

@ -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
}

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
interface TypeContext {
interface IDrawContext {
getContext(): CanvasRenderingContext2D;
setTransform(config: {
scale?: number;
@ -67,5 +67,5 @@ interface TypeContext {
}
export {
TypeContext
IDrawContext
};

View file

@ -1,13 +1,10 @@
type TypeCoreOptions = {
type CoreOptions = {
width: number;
height: number;
devicePixelRatio: number;
contextWidth: number;
contextHeight: number;
onlyRender?: boolean;
}
};
export {
TypeCoreOptions
};
export { CoreOptions };

View file

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

View file

@ -1,10 +1,8 @@
type TypeDeviceSize = {
type DeviceSize = {
x: number;
y: number;
w: number;
h: number;
}
};
export {
TypeDeviceSize,
};
export { DeviceSize };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;

View file

@ -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;

File diff suppressed because it is too large Load diff