From 796833d13838d836092891c909a531b710bb5bbb Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Mon, 26 Jul 2021 15:56:28 +0800 Subject: [PATCH] feat: make @idraw/core support html element --- .../core/examples/features/lib/data/html.js | 89 +++++++++++++++++++ packages/core/examples/test/html.html | 38 ++++++++ packages/core/src/constant/element.ts | 1 + packages/core/src/lib/draw/html.ts | 20 +++++ packages/core/src/lib/draw/image.ts | 6 +- packages/core/src/lib/draw/index.ts | 5 ++ packages/core/src/lib/draw/svg.ts | 5 +- packages/core/src/lib/loader-event.ts | 2 +- packages/core/src/lib/loader.ts | 20 ++++- packages/core/src/util/filter.ts | 4 + packages/types/src/lib/element.ts | 5 ++ packages/util/src/index.ts | 3 +- packages/util/src/lib/loader.ts | 46 ++++++---- 13 files changed, 217 insertions(+), 27 deletions(-) create mode 100644 packages/core/examples/features/lib/data/html.js create mode 100644 packages/core/examples/test/html.html create mode 100644 packages/core/src/lib/draw/html.ts create mode 100644 packages/core/src/util/filter.ts diff --git a/packages/core/examples/features/lib/data/html.js b/packages/core/examples/features/lib/data/html.js new file mode 100644 index 0000000..2f18584 --- /dev/null +++ b/packages/core/examples/features/lib/data/html.js @@ -0,0 +1,89 @@ +const data = { + // bgColor: '#ffffff', + elements: [ + { + name: "html-001", + x: 40, + y: 40, + w: 200, + h: 70, + type: "html", + angle: 0, + desc: { + html: ` +
+ Hello World! +
+ +
+ Hello World! +
+ `, + }, + }, + + { + name: "html-001", + x: 200, + y: 120, + w: 150, + h: 100, + type: "html", + angle: 0, + desc: { + html: ` + +
+
+ +
+ +
+ +
+
+ `, + }, + }, + ], +}; + +export default data; diff --git a/packages/core/examples/test/html.html b/packages/core/examples/test/html.html new file mode 100644 index 0000000..fa30fbb --- /dev/null +++ b/packages/core/examples/test/html.html @@ -0,0 +1,38 @@ + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/packages/core/src/constant/element.ts b/packages/core/src/constant/element.ts index c83b0bc..d5d1229 100644 --- a/packages/core/src/constant/element.ts +++ b/packages/core/src/constant/element.ts @@ -5,6 +5,7 @@ const elementTypes = { 'image': {}, // TODO 'svg': {}, // TODO 'circle': {}, // TODO + 'html': {}, // TODO }; export const elementNames = Object.keys(elementTypes); diff --git a/packages/core/src/lib/draw/html.ts b/packages/core/src/lib/draw/html.ts new file mode 100644 index 0000000..4affa1d --- /dev/null +++ b/packages/core/src/lib/draw/html.ts @@ -0,0 +1,20 @@ +import { + TypeContext, + TypeElement, +} from '@idraw/types'; +import { rotateElement } from '../transform'; +import Loader from '../loader'; + + +export function drawHTML( + ctx: TypeContext, + elem: TypeElement<'html'>, + loader: Loader, +) { + const content = loader.getContent(elem.uuid); + rotateElement(ctx, elem, () => { + if (content) { + ctx.drawImage(content, elem.x, elem.y, elem.w, elem.h); + } + }); +} diff --git a/packages/core/src/lib/draw/image.ts b/packages/core/src/lib/draw/image.ts index 73d0b71..ff5549d 100644 --- a/packages/core/src/lib/draw/image.ts +++ b/packages/core/src/lib/draw/image.ts @@ -1,16 +1,14 @@ import { TypeContext, TypeElement, - TypeElemDesc, - // TypePoint, } from '@idraw/types'; import { rotateElement } from '../transform'; import Loader from '../loader'; -export function drawImage( +export function drawImage ( ctx: TypeContext, - elem: TypeElement, + elem: TypeElement<'image'>, loader: Loader, ) { // const desc = elem.desc as TypeElemDesc['rect']; diff --git a/packages/core/src/lib/draw/index.ts b/packages/core/src/lib/draw/index.ts index e54ba18..6cfb10a 100644 --- a/packages/core/src/lib/draw/index.ts +++ b/packages/core/src/lib/draw/index.ts @@ -11,6 +11,7 @@ import { clearContext, drawBgColor } from './base'; import { drawRect } from './rect'; import { drawImage } from './image'; import { drawSVG } from './svg'; +import { drawHTML } from './html'; import { drawText } from './text'; import { drawCircle } from './circle'; import { @@ -55,6 +56,10 @@ export function drawContext( drawSVG(ctx, elem as TypeElement<'svg'>, loader); break; } + case 'html': { + drawHTML(ctx, elem as TypeElement<'html'>, loader); + break; + } case 'circle': { drawCircle(ctx, elem as TypeElement<'circle'>); break; diff --git a/packages/core/src/lib/draw/svg.ts b/packages/core/src/lib/draw/svg.ts index 9555057..f3dbfa1 100644 --- a/packages/core/src/lib/draw/svg.ts +++ b/packages/core/src/lib/draw/svg.ts @@ -1,15 +1,14 @@ import { TypeContext, TypeElement, - TypeElemDesc, } from '@idraw/types'; import { rotateElement } from '../transform'; import Loader from '../loader'; -export function drawSVG( +export function drawSVG ( ctx: TypeContext, - elem: TypeElement, + elem: TypeElement<'svg'>, loader: Loader, ) { // const desc = elem.desc as TypeElemDesc['rect']; diff --git a/packages/core/src/lib/loader-event.ts b/packages/core/src/lib/loader-event.ts index d41fbc2..c472aa8 100644 --- a/packages/core/src/lib/loader-event.ts +++ b/packages/core/src/lib/loader-event.ts @@ -2,7 +2,7 @@ export type TypeLoadData = { [uuid: string]: { - type: 'image' | 'svg', + type: 'image' | 'svg' | 'html', status: 'null' | 'loaded' | 'fail', content: null | HTMLImageElement | HTMLCanvasElement, elemW: number; diff --git a/packages/core/src/lib/loader.ts b/packages/core/src/lib/loader.ts index fb99f1b..f97dc6e 100644 --- a/packages/core/src/lib/loader.ts +++ b/packages/core/src/lib/loader.ts @@ -2,8 +2,9 @@ import { TypeData, TypeElement, TypeElemDesc } from '@idraw/types'; import Board from '@idraw/board'; import util from '@idraw/util'; import { LoaderEvent, TypeLoadData, TypeLoaderEventArgMap } from './loader-event'; +import { filterScript } from './../util/filter'; -const { loadImage, loadSVG } = util.loader; +const { loadImage, loadSVG, loadHTML } = util.loader; type Options = { board: Board; @@ -113,7 +114,7 @@ export default class Loader { // add new load-data for (let i = data.elements.length - 1; i >= 0; i --) { const elem = data.elements[i]; - if (['image', 'svg',].includes(elem.type)) { + if (['image', 'svg', 'html', ].includes(elem.type)) { if (!storageLoadData[elem.uuid]) { loadData[elem.uuid] = this._createEmptyLoadItem(elem); uuidQueue.push(elem.uuid); @@ -130,6 +131,12 @@ export default class Loader { loadData[elem.uuid] = this._createEmptyLoadItem(elem); uuidQueue.push(elem.uuid); } + } else if (elem.type === 'html') { + const _ele = elem as TypeElement<'html'>; + if (filterScript(_ele.desc.html) !== storageLoadData[elem.uuid].source) { + loadData[elem.uuid] = this._createEmptyLoadItem(elem); + uuidQueue.push(elem.uuid); + } } } } @@ -148,6 +155,7 @@ export default class Loader { private _createEmptyLoadItem(elem: TypeElement): TypeLoadData[string] { let source = ''; + const type: TypeLoadData[string]['type'] = elem.type as TypeLoadData[string]['type']; if (elem.type === 'image') { const _elem = elem as TypeElement<'image'>; @@ -155,6 +163,9 @@ export default class Loader { } else if (elem.type === 'svg') { const _elem = elem as TypeElement<'svg'>; source = _elem.desc.svg || ''; + } else if (elem.type === 'html') { + const _elem = elem as TypeElement<'html'>; + source = filterScript(_elem.desc.html || ''); } return { type: type, @@ -278,6 +289,11 @@ export default class Loader { return image; } else if (params && params.type === 'svg') { 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 } diff --git a/packages/core/src/util/filter.ts b/packages/core/src/util/filter.ts new file mode 100644 index 0000000..df84741 --- /dev/null +++ b/packages/core/src/util/filter.ts @@ -0,0 +1,4 @@ + +export function filterScript(html: string) { + return html.replace(//ig, ''); +} \ No newline at end of file diff --git a/packages/types/src/lib/element.ts b/packages/types/src/lib/element.ts index 7cf9f52..eee617e 100644 --- a/packages/types/src/lib/element.ts +++ b/packages/types/src/lib/element.ts @@ -28,6 +28,7 @@ type TypeElemDesc = { circle: TypeElemDescCircle, image: TypeElemDescImage, svg: TypeElemDescSVG, + html: TypeElemDescHTML, // paint: TypeElemDescPaint, } @@ -58,6 +59,10 @@ type TypeElemDescSVG = { svg: string; } +type TypeElemDescHTML = { + html: string; +} + // type TypeElemDescPaint = TypePaintData export { diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index b585c03..9f3b022 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -4,7 +4,7 @@ import { toColorHexStr, toColorHexNum, isColorStr } from './lib/color'; import { createUUID } from './lib/uuid'; import { deepClone } from './lib/data'; import istype from './lib/istype'; -import { loadImage, loadSVG } from './lib/loader'; +import { loadImage, loadSVG, loadHTML } from './lib/loader'; export default { time: { @@ -15,6 +15,7 @@ export default { loader: { loadImage, loadSVG, + loadHTML, }, file: { downloadImageFromCanvas, diff --git a/packages/util/src/lib/loader.ts b/packages/util/src/lib/loader.ts index 13578ec..f8eda6a 100644 --- a/packages/util/src/lib/loader.ts +++ b/packages/util/src/lib/loader.ts @@ -13,25 +13,39 @@ export function loadImage(src: string): Promise { } -export function loadSVG(svg: string, opts?: { width: number, height: number }): Promise { +export function loadSVG(svg: string): Promise { return new Promise((resolve, reject) => { - // let style = ''; - // if (opts) { - // style = `min-height: ${opts.height}px; min-width: ${opts.width}px` - // } - // const _svg = ` - // - // - // ${svg} - // - // - // `; - const _svg = svg; + const image = new Image(); + const blob = new Blob([_svg], { type: 'image/svg+xml;charset=utf-8'}); + const reader = new FileReader(); + reader.readAsDataURL(blob); + reader.onload = function (event: ProgressEvent) { + const base64: string = event?.target?.result as string; + image.onload = function() { + resolve(image); + }; + image.src = base64; + }; + reader.onerror = function(err) { + reject(err); + }; + }); +} + +export function loadHTML(html: string, opts: { width: number, height: number }): Promise { + const { width, height } = opts; + return new Promise((resolve, reject) => { + const _svg = ` + + +
+ ${html} +
+
+
+ `;; const image = new Image(); const blob = new Blob([_svg], { type: 'image/svg+xml;charset=utf-8'}); const reader = new FileReader();