diff --git a/package.json b/package.json index f106af1..22d24f5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "init": "lerna bootstrap --npm-client=cnpm", "clear": "rm -rf ./packages/*/dist/ & rm -rf ./packages/*/node_modules/", "jest": "jest --config jest.config.js", - "test": "lerna bootstrap --no-ci && npm run build && npm run jest && npm run e2e" + "test": "lerna bootstrap --no-ci && npm run build && npm run jest && npm run e2e", + "serve": "http-server ./" }, "devDependencies": { "@babel/core": "^7.13.14", diff --git a/packages/core/example/main.js b/packages/core/example/main.js index 927b94a..f0ebb7b 100644 --- a/packages/core/example/main.js +++ b/packages/core/example/main.js @@ -9,6 +9,4 @@ const core = new Core(mount, { devicePixelRatio: 4 }); core.setData(data); -core.draw(); - -console.log('hello world =', Core); \ No newline at end of file +core.draw(); \ No newline at end of file diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 6fc308c..9654cf8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,6 @@ import { TypeData } from '@idraw/types'; import Board from '@idraw/board'; +import Renderer from './lib/renderer'; type Options = { width: number; @@ -12,31 +13,17 @@ class Core { private _board: Board; private _data: TypeData; private _opts: Options; + private _renderer: Renderer; constructor(mount: HTMLDivElement, opts: Options) { this._data = { elements: [] }; this._opts = opts; this._board = new Board(mount, this._opts); + this._renderer = new Renderer(this._board); } draw() { - const board = this._board; - const ctx = board.getContext(); - const data = this.getData(); - const { width, height } = this._opts; - board.clear(); - ctx.clearRect(0, 0, width, height); - ctx.setFillStyle('#ffffff'); - ctx.fillRect(0, 0, width, height); - data.elements.forEach(ele => { - // @ts-ignore - if (ele.type === 'rect' && typeof ele.desc.color === 'string') { - // @ts-ignore - ctx.setFillStyle(ele.desc.color); - } - ctx.fillRect(ele.x, ele.y, ele.w, ele.h); - }); - board.draw(); + this._renderer.render(this._data); } getData() { diff --git a/packages/core/src/lib/draw.ts b/packages/core/src/lib/draw.ts new file mode 100644 index 0000000..1bcdef3 --- /dev/null +++ b/packages/core/src/lib/draw.ts @@ -0,0 +1,22 @@ +import { TypeContext, TypeData, TypeElement, TypeElemDesc } from '@idraw/types'; + +export function drawContext(ctx: TypeContext, data: TypeData) { + for (let i = 0; i < data.elements.length; i++) { + const ele = data.elements[i]; + switch (ele.type) { + case 'rect': { + drawRect<'rect'>(ctx, ele as TypeElement<'rect'>); + }; + default: { + // nothing + } + } + } +} + + +function drawRect(ctx: TypeContext, ele: TypeElement) { + const desc = ele.desc as TypeElemDesc['rect']; + ctx.setFillStyle(desc.color); + ctx.fillRect(ele.x, ele.y, ele.w, ele.h); +} \ No newline at end of file diff --git a/packages/core/src/lib/renderer.ts b/packages/core/src/lib/renderer.ts new file mode 100644 index 0000000..535417e --- /dev/null +++ b/packages/core/src/lib/renderer.ts @@ -0,0 +1,21 @@ +import { TypeContext, TypeData } from '@idraw/types'; +import { drawContext } from './draw'; +import Board from '@idraw/board'; + +export default class Renderer { + + private _board: Board; + private _ctx: TypeContext; + private _data: TypeData = { elements: [] }; + + constructor(board: Board) { + this._board = board; + this._ctx = this._board.getContext(); + } + + render(data: TypeData) { + this._data = data; + drawContext(this._ctx, this._data); + this._board.draw(); + } +} \ No newline at end of file diff --git a/packages/core/src/util/color.ts b/packages/core/src/util/color.ts new file mode 100644 index 0000000..4de9f98 --- /dev/null +++ b/packages/core/src/util/color.ts @@ -0,0 +1,7 @@ +export function toColorHexNum(color: string): number { + return parseInt(color.replace(/^\#/, '0x')); +} + +export function toColorHexStr(color: number): string { + return '#' + color.toString(16); +} \ No newline at end of file diff --git a/packages/core/src/util/file.ts b/packages/core/src/util/file.ts new file mode 100644 index 0000000..30b4ce5 --- /dev/null +++ b/packages/core/src/util/file.ts @@ -0,0 +1,16 @@ + +type ImageType = 'image/jpeg' | 'image/png'; + +export function downloadImageFromCanvas ( + canvas: HTMLCanvasElement, + opts: { filename: string, type: ImageType } +) { + const { filename, type = 'image/jpeg' } = opts; + const stream = canvas.toDataURL(type); + const downloadLink = document.createElement('a'); + downloadLink.href = stream; + downloadLink.download = filename; + const downloadClickEvent = document.createEvent('MouseEvents'); + downloadClickEvent.initEvent('click', true, false); + downloadLink.dispatchEvent(downloadClickEvent); +} \ No newline at end of file diff --git a/packages/core/src/util/index.ts b/packages/core/src/util/index.ts new file mode 100644 index 0000000..b99a113 --- /dev/null +++ b/packages/core/src/util/index.ts @@ -0,0 +1,22 @@ +import { loadImage } from './loader'; +import { delay, compose, throttle } from './time'; +import { downloadImageFromCanvas } from './file'; +import { toColorHexStr, toColorHexNum } from './color'; + +export default { + time: { + delay, + compose, + throttle, + }, + loader: { + loadImage + }, + file: { + downloadImageFromCanvas, + }, + color: { + toColorHexStr, + toColorHexNum, + } +} \ No newline at end of file diff --git a/packages/core/src/util/loader.ts b/packages/core/src/util/loader.ts new file mode 100644 index 0000000..7eedec1 --- /dev/null +++ b/packages/core/src/util/loader.ts @@ -0,0 +1,13 @@ + + +export function loadImage(src: string): Promise { + return new Promise((resolve, reject) => { + const img = new Image; + img.onload = function() { + resolve(img); + }; + img.onabort = reject; + img.onerror = reject; + img.src = src; + }); +} \ No newline at end of file diff --git a/packages/core/src/util/time.ts b/packages/core/src/util/time.ts new file mode 100644 index 0000000..8ec52e6 --- /dev/null +++ b/packages/core/src/util/time.ts @@ -0,0 +1,44 @@ + +export function compose (middleware: Function[]) { + return function (context: any, next?: Function) { + // let index = -1; + return dispatch(0); + + function dispatch (i: number): Promise { + // index = i + let fn = middleware[i] + if (i === middleware.length && next) { + fn = next; + } + if (!fn) return Promise.resolve() + try { + return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); + } catch (err) { + return Promise.reject(err) + } + } + } +} + + +export function delay(time: number): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, time); + }) +} + +export function throttle(fn: Function, timeout: number) { + let timer: any = -1; + return function(...args: any[]) { + if (timer > 0) { + return; + } + timer = setTimeout(() => { + fn(...args); + timer = -1; + }, timeout) + } +} + diff --git a/packages/types/src/lib/data.ts b/packages/types/src/lib/data.ts index 0ae7f18..9bc0144 100644 --- a/packages/types/src/lib/data.ts +++ b/packages/types/src/lib/data.ts @@ -1,21 +1,12 @@ -import { TypeElementsDesc } from './element'; +import { TypeElemDesc, TypeElement } from './element'; + -type TypeDataElement = { - uuid?: string; - x: number; - y: number; - w: number; - h: number; - type: T; - desc: TypeElementsDesc[T]; -} type TypeData = { - elements: TypeDataElement[] + elements: TypeElement[] selectedElements?: string[] // uuids } export { - TypeDataElement, TypeData } \ No newline at end of file diff --git a/packages/types/src/lib/element.ts b/packages/types/src/lib/element.ts index b3ac809..b9ce722 100644 --- a/packages/types/src/lib/element.ts +++ b/packages/types/src/lib/element.ts @@ -1,6 +1,16 @@ import { TypePaintData } from './paint'; -type TypeElementsDesc = { +type TypeElement = { + uuid?: string; + x: number; + y: number; + w: number; + h: number; + type: T; + desc: TypeElemDesc[T]; +} + +type TypeElemDesc = { text: TypeElemDescText, rect: TypeElemDescRect, circle: TypeElemDescCircle, @@ -33,5 +43,6 @@ export { TypeElemDescText, TypeElemDescRect, TypeElemDescCircle, - TypeElementsDesc, + TypeElemDesc, + TypeElement, } \ No newline at end of file