diff --git a/__tests__/e2e.test.js b/__tests__/e2e.test.js new file mode 100644 index 0000000..075fc4d --- /dev/null +++ b/__tests__/e2e.test.js @@ -0,0 +1,69 @@ +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const jimp = require('jimp'); +const pixelmatch = require('pixelmatch'); +const pngjs = require('pngjs'); +const { delay } = require('./../scripts/util/time'); +const { pageList } = require('./../scripts/screen.config'); +const { createScreenshot } = require('../scripts/util/screen'); + +const snapshotDir = path.join(__dirname, 'snapshot'); +const diffDir = path.join(__dirname, 'diff'); +const { PNG } = pngjs; + +async function diff() { + const middlewares = []; + const diffRateList = []; + pageList.forEach((p) => { + middlewares.push(async (ctx = {}, next) => { + const { page, port } = ctx; + const width = p.w; + const height = p.h; + await page.setViewport( { width: p.w, height: p.h } ); + const pageUrl = `http://127.0.0.1:${port}/packages/${p.path || ''}`; + await page.goto(pageUrl); + await delay(p.delay || 100); + const buf = await page.screenshot(); + + const snapshotPicPath = parsePicPath(path.join(snapshotDir, p.path)); + const actual = (await jimp.read(buf)).scale(1).quality(100).bitmap; + const expected = (await jimp.read(fs.readFileSync(snapshotPicPath))).bitmap; + const diffBuf = new PNG({ width, height }); + const failedPixel = pixelmatch(expected.data, actual.data, diffBuf.data, actual.width, actual.height); + const failRate = failedPixel / (width * height); + if (failRate > 0) { + (await jimp.read(diffBuf)).scale(1).quality(100).write(parsePicPath(path.join(diffDir, p.path))); + } + diffRateList.push(failRate); + await next(); + }); + }); + await createScreenshot(middlewares, { baseDir: path.join(__dirname, '..') }); + return diffRateList; +} + +function parsePicPath(pagePath) { + // const picPath = pagePath.replace(/\.html$/, '.jpg'); + const picPath = pagePath + '.jpg'; + return picPath; +} + + + +describe('Screenshot testing', function() { + it('testing...', function(done){ + this.timeout(1000 * 60 * 2); + diff().then((rateList) => { + + assert.ok(Array.isArray(rateList)); + assert.ok(rateList.length > 0); + rateList.forEach((rate) => { + console.log('diff-rate =', rate); + assert.ok(rate < 0.05); + }); + + done(); + }).catch(done); + }); +}); \ No newline at end of file diff --git a/__tests__/snapshot/board/examples/features/test.html.jpg b/__tests__/snapshot/board/examples/features/test.html.jpg new file mode 100644 index 0000000..5c5b0e0 Binary files /dev/null and b/__tests__/snapshot/board/examples/features/test.html.jpg differ diff --git a/__tests__/snapshot/core/examples/features/image.html.jpg b/__tests__/snapshot/core/examples/features/image.html.jpg new file mode 100644 index 0000000..74be356 Binary files /dev/null and b/__tests__/snapshot/core/examples/features/image.html.jpg differ diff --git a/__tests__/snapshot/core/examples/features/rect.html.jpg b/__tests__/snapshot/core/examples/features/rect.html.jpg new file mode 100644 index 0000000..f927f89 Binary files /dev/null and b/__tests__/snapshot/core/examples/features/rect.html.jpg differ diff --git a/__tests__/snapshot/core/examples/features/svg.html.jpg b/__tests__/snapshot/core/examples/features/svg.html.jpg new file mode 100644 index 0000000..68a45cc Binary files /dev/null and b/__tests__/snapshot/core/examples/features/svg.html.jpg differ diff --git a/__tests__/snapshot/core/examples/features/text.html.jpg b/__tests__/snapshot/core/examples/features/text.html.jpg new file mode 100644 index 0000000..19e4adc Binary files /dev/null and b/__tests__/snapshot/core/examples/features/text.html.jpg differ diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..d37fa21 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,32 @@ +module.exports = { + // "collectCoverage": true, + "coverageDirectory": "reports", + "collectCoverageFrom": [ + "packages/**/src/**/*.ts", + "!packages/**/node_modules/**", + "!**/node_modules/**" + ], + "coverageReporters": [ + // "clover", + // "html", + "text-summary" + ], + "coverageThreshold": { + "global": { + "branches": 80, + "functions": 80, + "lines": 80, + "statements": 80 + } + }, + "moduleFileExtensions": [ + "js", "ts" + ], + "modulePaths": [ + "" + ], + "testRegex": "(/packages/([^\/]{1,})/__tests__/.*|\\.test)\\.ts$", + "setupFiles": [ + "jest-canvas-mock" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 312483f..b2b8278 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,11 @@ "@babel/preset-typescript": "^7.13.0", "@microsoft/api-extractor": "^7.13.2", "@rollup/plugin-node-resolve": "^11.2.1", + "@types/jest": "^26.0.23", "@typescript-eslint/eslint-plugin": "^4.25.0", "@typescript-eslint/parser": "^4.25.0", "babel-jest": "^26.6.3", + "canvas": "^2.8.0", "chalk": "^4.1.0", "eslint": "^7.27.0", "execa": "^5.0.0", @@ -31,6 +33,7 @@ "http-server": "^0.12.3", "husky": "^6.0.0", "jest": "^26.6.3", + "jest-canvas-mock": "^2.3.1", "jimp": "^0.16.1", "koa-compose": "^4.1.0", "lerna": "^3.22.1", diff --git a/packages/board/__tests__/__snapshots__/index.test.ts.snap b/packages/board/__tests__/__snapshots__/index.test.ts.snap new file mode 100644 index 0000000..1cfef36 --- /dev/null +++ b/packages/board/__tests__/__snapshots__/index.test.ts.snap @@ -0,0 +1,129 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`testing 1`] = ` +Array [ + Object { + "props": Object { + "height": 1600, + "width": 2400, + "x": 0, + "y": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "clearRect", + }, + Object { + "props": Object { + "height": 1600, + "width": 2400, + "x": 0, + "y": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "clearRect", + }, + Object { + "props": Object { + "dHeight": 1600, + "dWidth": 2400, + "dx": 0, + "dy": 0, + "img": , + "sHeight": 1600, + "sWidth": 2400, + "sx": 0, + "sy": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "drawImage", + }, +] +`; + +exports[`testing 2`] = ` +Array [ + Object { + "props": Object { + "height": 1600, + "width": 2400, + "x": 0, + "y": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "clearRect", + }, + Object { + "props": Object { + "height": 1600, + "width": 2400, + "x": 0, + "y": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "clearRect", + }, + Object { + "props": Object { + "dHeight": 1600, + "dWidth": 2400, + "dx": 0, + "dy": 0, + "img": , + "sHeight": 1600, + "sWidth": 2400, + "sx": 0, + "sy": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "drawImage", + }, +] +`; diff --git a/packages/board/__tests__/index.test.ts b/packages/board/__tests__/index.test.ts new file mode 100644 index 0000000..2485479 --- /dev/null +++ b/packages/board/__tests__/index.test.ts @@ -0,0 +1,81 @@ +import Board from './../src'; + +test('testing', async () => { + document.body.innerHTML = ` +
+ `; + const opts = { + width: 600, + height: 400, + devicePixelRatio: 4 + } + const mount = document.querySelector('#mount') as HTMLDivElement; + const board = new Board(mount, opts); + + const ctx = board.getContext(); + const data = { + elements: [ + { + x: 10, + y: 10, + w: 200, + h: 120, + type: 'rect', + desc: { + color: '#f0f0f0', + } + }, + { + x: 80, + y: 80, + w: 200, + h: 120, + type: 'rect', + desc: { + color: '#cccccc', + } + }, + { + x: 160, + y: 160, + w: 200, + h: 120, + type: 'rect', + desc: { + color: '#c0c0c0', + } + }, + { + x: 400 - 10, + y: 300 - 10, + w: 200, + h: 100, + type: 'rect', + desc: { + color: '#e0e0e0', + } + } + ] + }; + + board.clear(); + ctx.clearRect(0, 0, opts.width, opts.height); + ctx.setFillStyle('#ffffff'); + ctx.fillRect(0, 0, opts.width, opts.height); + data.elements.forEach(ele => { + ctx.setFillStyle(ele.desc.color); + ctx.fillRect(ele.x, ele.y, ele.w, ele.h); + }); + board.draw(); + + const originCtx = board.getOriginContext(); + // @ts-ignore; + const originCalls = originCtx.__getDrawCalls(); + expect(originCalls).toMatchSnapshot(); + + const displayCtx = board.getDisplayContext(); + // @ts-ignore; + const displayCalls = displayCtx.__getDrawCalls(); + expect(displayCalls).toMatchSnapshot(); + +}); \ No newline at end of file diff --git a/packages/board/example/basic/index.html b/packages/board/examples/basic/index.html similarity index 100% rename from packages/board/example/basic/index.html rename to packages/board/examples/basic/index.html diff --git a/packages/board/example/basic/main.js b/packages/board/examples/basic/main.js similarity index 100% rename from packages/board/example/basic/main.js rename to packages/board/examples/basic/main.js diff --git a/packages/board/example/features/index.html b/packages/board/examples/features/index.html similarity index 100% rename from packages/board/example/features/index.html rename to packages/board/examples/features/index.html diff --git a/packages/board/example/features/lib/action.js b/packages/board/examples/features/lib/action.js similarity index 96% rename from packages/board/example/features/lib/action.js rename to packages/board/examples/features/lib/action.js index 9353d65..8e8ba5d 100644 --- a/packages/board/example/features/lib/action.js +++ b/packages/board/examples/features/lib/action.js @@ -26,7 +26,7 @@ function isPointInElement(board, p = {x, y}) { function moveElement(board, idx, moveX, moveY) { const data = getData(); - const scale = getScale(); + const scale = getScale() || 1; if (data.elements[idx]) { // data.elements[idx].x += (moveX * scale * opts.devicePixelRatio); // data.elements[idx].y += (moveY * scale * opts.devicePixelRatio); diff --git a/packages/board/example/features/lib/data.js b/packages/board/examples/features/lib/data.js similarity index 100% rename from packages/board/example/features/lib/data.js rename to packages/board/examples/features/lib/data.js diff --git a/packages/board/example/features/lib/draw.js b/packages/board/examples/features/lib/draw.js similarity index 100% rename from packages/board/example/features/lib/draw.js rename to packages/board/examples/features/lib/draw.js diff --git a/packages/board/example/features/lib/event.js b/packages/board/examples/features/lib/event.js similarity index 100% rename from packages/board/example/features/lib/event.js rename to packages/board/examples/features/lib/event.js diff --git a/packages/board/example/features/lib/opts.js b/packages/board/examples/features/lib/opts.js similarity index 100% rename from packages/board/example/features/lib/opts.js rename to packages/board/examples/features/lib/opts.js diff --git a/packages/board/example/features/lib/scale.js b/packages/board/examples/features/lib/scale.js similarity index 89% rename from packages/board/example/features/lib/scale.js rename to packages/board/examples/features/lib/scale.js index d239789..e606c07 100644 --- a/packages/board/example/features/lib/scale.js +++ b/packages/board/examples/features/lib/scale.js @@ -3,6 +3,9 @@ let hasInited = false; export function doScale(board, scale) { if (hasInited === true) return; + if (!input) { + return; + } if (scale > 0) { input.value = scale; board.scale(scale); @@ -19,6 +22,9 @@ export function doScale(board, scale) { } export function getScale() { + if (!input) { + return; + } let val = 1; if (input.value * 1 > 0) { val = input.value * 1; diff --git a/packages/board/example/features/lib/scroll.js b/packages/board/examples/features/lib/scroll.js similarity index 94% rename from packages/board/example/features/lib/scroll.js rename to packages/board/examples/features/lib/scroll.js index 3791640..d9020f7 100644 --- a/packages/board/example/features/lib/scroll.js +++ b/packages/board/examples/features/lib/scroll.js @@ -4,6 +4,9 @@ let hasInited = false; export function doScroll(board, conf = {}) { if (hasInited === true) return; + if (!(inputX && inputY)) { + return; + } if (conf.scrollX >= 0) { inputX.value = conf.scrollX; diff --git a/packages/board/example/features/main.js b/packages/board/examples/features/main.js similarity index 100% rename from packages/board/example/features/main.js rename to packages/board/examples/features/main.js diff --git a/packages/board/examples/features/test.html b/packages/board/examples/features/test.html new file mode 100644 index 0000000..3efb8b5 --- /dev/null +++ b/packages/board/examples/features/test.html @@ -0,0 +1,29 @@ + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/packages/board/example/scale/index.html b/packages/board/examples/scale/index.html similarity index 100% rename from packages/board/example/scale/index.html rename to packages/board/examples/scale/index.html diff --git a/packages/board/example/scale/main.js b/packages/board/examples/scale/main.js similarity index 100% rename from packages/board/example/scale/main.js rename to packages/board/examples/scale/main.js diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index 8a00cb3..ca968d1 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -22,6 +22,7 @@ class Board { private _hasRendered = false; private _ctx: Context; private _displayCtx: CanvasRenderingContext2D; + private _originCtx: CanvasRenderingContext2D; private _scaleRatio = 1; private _scrollX = 0; private _scrollY = 0; @@ -33,15 +34,22 @@ class Board { this._displayCanvas = document.createElement('canvas'); this._mount.appendChild(this._displayCanvas); this._opts = this._parsePrivateOptions(opts); - const ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D; - const displayCtx = this._displayCanvas.getContext('2d') as CanvasRenderingContext2D; - this._ctx = new Context(ctx, this._opts); - this._displayCtx = displayCtx; + this._originCtx = this._canvas.getContext('2d') as CanvasRenderingContext2D; + this._displayCtx = this._displayCanvas.getContext('2d') as CanvasRenderingContext2D; + this._ctx = new Context(this._originCtx, this._opts); this._watcher = new Watcher(this._displayCanvas); this._render(); } + getDisplayContext(): CanvasRenderingContext2D { + return this._displayCtx; + } + + getOriginContext(): CanvasRenderingContext2D { + return this._displayCtx; + } + getContext(): Context { return this._ctx; } diff --git a/packages/board/src/util/context.ts b/packages/board/src/util/context.ts index b0fdf57..0db8ac5 100644 --- a/packages/board/src/util/context.ts +++ b/packages/board/src/util/context.ts @@ -38,7 +38,11 @@ class Context implements TypeContext { } calcDeviceNum(num: number): number { - return this._opts.devicePixelRatio * num; + return num * this._opts.devicePixelRatio; + } + + calcScreenNum(num: number): number { + return num / this._opts.devicePixelRatio; } getSize() { diff --git a/packages/core/example/lib/data/index.js b/packages/core/example/lib/data/index.js deleted file mode 100644 index 23d2f49..0000000 --- a/packages/core/example/lib/data/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import dataRect from './rect.js'; -import dataImage from './image.js'; -import dataSVG from './svg.js'; -import dataText from './text.js'; - -const url = new URLSearchParams(window.location.search); - -const dataMap = { - 'rect': dataRect, - 'image': dataImage, - 'svg': dataSVG, - 'text': dataText -} - -export function getData() { - return dataMap[url.get('data')] || dataMap['rect']; -} \ No newline at end of file diff --git a/packages/core/example/lib/data/text.js b/packages/core/example/lib/data/text.js deleted file mode 100644 index e2759b5..0000000 --- a/packages/core/example/lib/data/text.js +++ /dev/null @@ -1,73 +0,0 @@ - -const data = { - // bgColor: '#ffffff', - elements: [ - { - name: 'text-001', - x: 10, - y: 10, - w: 200, - h: 100, - type: 'text', - borderRadius: 20, - borderWidth: 2, - borderColor: '#bd0b64', - desc: { - fontSize: 20, - color: '#333333', - text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。', - fontFamily: '' - } - }, - { - name: 'text-002', - x: 80, - y: 80, - w: 200, - h: 120, - // angle: 30, - type: 'text', - borderRadius: 60, - borderWidth: 10, - borderColor: '#bd0b64', - desc: { - fontSize: 20, - text: 'Hello Text', - color: '#666666', - } - }, - // { - // name: 'text-003', - // x: 160, - // y: 160, - // w: 200, - // h: 20, - // type: 'text', - // angle: 80, - // borderRadius: 20, - // borderWidth: 10, - // borderColor: '#bd0b64', - // desc: { - // color: '#c0c0c0', - // } - // }, - // { - // name: 'text-004', - // x: 400 - 10, - // y: 300 - 10, - // w: 200, - // h: 100, - // type: 'text', - // borderRadius: 20, - // borderWidth: 10, - // borderColor: '#bd0b64', - // desc: { - // color: '#e0e0e0', - // } - // } - ] -} - - - -export default data; \ No newline at end of file diff --git a/packages/core/example/css/index.css b/packages/core/examples/features/css/index.css similarity index 100% rename from packages/core/example/css/index.css rename to packages/core/examples/features/css/index.css diff --git a/packages/core/examples/features/image.html b/packages/core/examples/features/image.html new file mode 100644 index 0000000..2490f0e --- /dev/null +++ b/packages/core/examples/features/image.html @@ -0,0 +1,25 @@ + + + + + + + + + +
+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/packages/core/example/index.html b/packages/core/examples/features/index.html similarity index 89% rename from packages/core/example/index.html rename to packages/core/examples/features/index.html index 2ef456c..dce8fb1 100644 --- a/packages/core/example/index.html +++ b/packages/core/examples/features/index.html @@ -31,7 +31,7 @@ - - + + \ No newline at end of file diff --git a/packages/core/example/lib/data/circle.js b/packages/core/examples/features/lib/data/circle.js similarity index 100% rename from packages/core/example/lib/data/circle.js rename to packages/core/examples/features/lib/data/circle.js diff --git a/packages/core/example/lib/data/image.js b/packages/core/examples/features/lib/data/image.js similarity index 82% rename from packages/core/example/lib/data/image.js rename to packages/core/examples/features/lib/data/image.js index e43f115..2cee87f 100644 --- a/packages/core/example/lib/data/image.js +++ b/packages/core/examples/features/lib/data/image.js @@ -15,7 +15,7 @@ const data = { // angle: 30, // angle: 0, desc: { - src: './images/computer.png', + src: './../images/computer.png', } }, { @@ -30,7 +30,7 @@ const data = { borderColor: '#bd0b64', type: 'image', desc: { - src: './images/chart.png', + src: './../images/chart.png', } }, { @@ -42,7 +42,7 @@ const data = { type: 'image', angle: 80, desc: { - src: './images/phone.png', + src: './../images/phone.png', } }, { @@ -53,7 +53,7 @@ const data = { h: 100, type: 'image', desc: { - src: './images/building-001.png', + src: './../images/building-001.png', } }, { @@ -64,7 +64,7 @@ const data = { h: 100, type: 'image', desc: { - src: './images/building-002.png', + src: './../images/building-002.png', } }, { @@ -75,7 +75,7 @@ const data = { h: 100, type: 'image', desc: { - src: './images/building-003.png', + src: './../images/building-003.png', } } ] diff --git a/packages/core/examples/features/lib/data/index.js b/packages/core/examples/features/lib/data/index.js new file mode 100644 index 0000000..b62a2de --- /dev/null +++ b/packages/core/examples/features/lib/data/index.js @@ -0,0 +1,42 @@ +import dataRect from './rect.js'; +import dataImage from './image.js'; +import dataSVG from './svg.js'; +import dataText from './text.js'; + +const url = new URLSearchParams(window.location.search); + +const dataMap = { + 'rect': dataRect, + 'image': dataImage, + 'svg': dataSVG, + 'text': dataText +} + +export function getData() { + return dataMap[getPageName()] || dataMap[url.get('data')] || dataMap['rect']; +} + +function getPageName() { + // const pathname = window.location.pathname || ''; + // const reg = /(?[\w+]{1,})\.html$/; + // const page = reg.exec(pathname)?.groups?.pageName || ''; + // return page; + + const pathname = window.location.pathname || ''; + const list = pathname.split('/'); + let pageName = list.pop() || ''; + pageName = pageName.replace(/\.html$/ig, ''); + return pageName; + + // return getQueryString('data') || 'rect'; +} + + +// function getQueryString(name) { +// let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); +// let r = window.location.search.substr(1).match(reg); +// if (r != null) { +// return decodeURIComponent(r[2]); +// }; +// return null; +// } \ No newline at end of file diff --git a/packages/core/example/lib/data/rect.js b/packages/core/examples/features/lib/data/rect.js similarity index 100% rename from packages/core/example/lib/data/rect.js rename to packages/core/examples/features/lib/data/rect.js diff --git a/packages/core/example/lib/data/svg.js b/packages/core/examples/features/lib/data/svg.js similarity index 100% rename from packages/core/example/lib/data/svg.js rename to packages/core/examples/features/lib/data/svg.js diff --git a/packages/core/examples/features/lib/data/text.js b/packages/core/examples/features/lib/data/text.js new file mode 100644 index 0000000..a740781 --- /dev/null +++ b/packages/core/examples/features/lib/data/text.js @@ -0,0 +1,80 @@ + +const data = { + // bgColor: '#ffffff', + elements: [ + { + name: 'text-001', + x: 10, + y: 10, + w: 200, + h: 100, + type: 'text', + borderRadius: 20, + borderWidth: 2, + borderColor: '#bd0b64', + desc: { + fontSize: 20, + color: '#333333', + text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。', + fontFamily: '' + } + }, + { + name: 'text-002', + x: 80, + y: 80, + w: 200, + h: 120, + // angle: 30, + type: 'text', + borderRadius: 60, + borderWidth: 10, + borderColor: '#bd0b64', + desc: { + fontSize: 20, + text: 'Hello Text', + color: '#666666', + } + }, + { + name: 'text-003', + x: 160, + y: 160, + w: 200, + h: 100, + type: 'text', + borderRadius: 20, + borderWidth: 2, + borderColor: '#bd0b64', + desc: { + fontSize: 20, + color: '#333333', + text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。', + fontFamily: '', + textAlign: 'right', + } + }, + { + name: 'text-004', + x: 400 - 10, + y: 300 - 10, + w: 200, + h: 100, + type: 'text', + borderRadius: 20, + borderWidth: 2, + borderColor: '#bd0b64', + desc: { + fontSize: 20, + color: '#333333', + text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。', + fontFamily: '', + textAlign: 'left', + } + } + ] +} + + + +export default data; \ No newline at end of file diff --git a/packages/core/example/lib/element.js b/packages/core/examples/features/lib/element.js similarity index 98% rename from packages/core/example/lib/element.js rename to packages/core/examples/features/lib/element.js index 43d1dc5..addf14e 100644 --- a/packages/core/example/lib/element.js +++ b/packages/core/examples/features/lib/element.js @@ -5,6 +5,7 @@ let hasInited = false; export function doElemens(core) { if (hasInited === true) return; + if (!dom) return; renderElemens(core); listenElements(core); } diff --git a/packages/core/example/main.js b/packages/core/examples/features/lib/main.js similarity index 54% rename from packages/core/example/main.js rename to packages/core/examples/features/lib/main.js index 49e0607..a3e5d38 100644 --- a/packages/core/example/main.js +++ b/packages/core/examples/features/lib/main.js @@ -1,16 +1,11 @@ -import { getData } from './lib/data/index.js'; -import { doScale } from './lib/scale.js'; -import { doScroll } from './lib/scroll.js'; -import { doElemens } from './lib/element.js'; +import { getData } from './data/index.js'; +import { doScale } from './scale.js'; +import { doScroll } from './scroll.js'; +import { doElemens } from './element.js'; const { Core } = window.iDraw; const data = getData(); const mount = document.querySelector('#mount'); -// const defaultConf = { -// scale: 0.8, -// scrollX: 100, -// scrollY: 50, -// } const defaultConf = { scale: 1, @@ -23,9 +18,11 @@ const core = new Core(mount, { devicePixelRatio: 4 }); -console.log('core ===', core); core.setData(data); +core.draw(); + doScale(core, defaultConf.scale); doScroll(core, defaultConf); doElemens(core); + diff --git a/packages/core/example/lib/scale.js b/packages/core/examples/features/lib/scale.js similarity index 95% rename from packages/core/example/lib/scale.js rename to packages/core/examples/features/lib/scale.js index a271ef6..e780af6 100644 --- a/packages/core/example/lib/scale.js +++ b/packages/core/examples/features/lib/scale.js @@ -3,6 +3,7 @@ let hasInited = false; export function doScale(core, scale) { if (hasInited === true) return; + if (!input) return; if (scale > 0) { input.value = scale; core.scale(scale); diff --git a/packages/core/example/lib/scroll.js b/packages/core/examples/features/lib/scroll.js similarity index 91% rename from packages/core/example/lib/scroll.js rename to packages/core/examples/features/lib/scroll.js index 3a1c1a7..7e240af 100644 --- a/packages/core/example/lib/scroll.js +++ b/packages/core/examples/features/lib/scroll.js @@ -3,7 +3,8 @@ const inputY = document.querySelector('#scrollY'); let hasInited = false; export function doScroll(core, conf = {}) { - if (hasInited === true) return; + if (hasInited === true) return; + if (!(inputY && inputX)) return; if (conf.scrollX >= 0) { inputX.value = conf.scrollX; diff --git a/packages/core/examples/features/rect.html b/packages/core/examples/features/rect.html new file mode 100644 index 0000000..2490f0e --- /dev/null +++ b/packages/core/examples/features/rect.html @@ -0,0 +1,25 @@ + + + + + + + + + +
+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/packages/core/examples/features/svg.html b/packages/core/examples/features/svg.html new file mode 100644 index 0000000..2490f0e --- /dev/null +++ b/packages/core/examples/features/svg.html @@ -0,0 +1,25 @@ + + + + + + + + + +
+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/packages/core/examples/features/text.html b/packages/core/examples/features/text.html new file mode 100644 index 0000000..2490f0e --- /dev/null +++ b/packages/core/examples/features/text.html @@ -0,0 +1,25 @@ + + + + + + + + + +
+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/packages/core/example/images/building-001.png b/packages/core/examples/images/building-001.png similarity index 100% rename from packages/core/example/images/building-001.png rename to packages/core/examples/images/building-001.png diff --git a/packages/core/example/images/building-002.png b/packages/core/examples/images/building-002.png similarity index 100% rename from packages/core/example/images/building-002.png rename to packages/core/examples/images/building-002.png diff --git a/packages/core/example/images/building-003.png b/packages/core/examples/images/building-003.png similarity index 100% rename from packages/core/example/images/building-003.png rename to packages/core/examples/images/building-003.png diff --git a/packages/core/example/images/chart.png b/packages/core/examples/images/chart.png similarity index 100% rename from packages/core/example/images/chart.png rename to packages/core/examples/images/chart.png diff --git a/packages/core/example/images/computer.png b/packages/core/examples/images/computer.png similarity index 100% rename from packages/core/example/images/computer.png rename to packages/core/examples/images/computer.png diff --git a/packages/core/example/images/phone.png b/packages/core/examples/images/phone.png similarity index 100% rename from packages/core/example/images/phone.png rename to packages/core/examples/images/phone.png diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f0e405d..04f3773 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,10 +1,13 @@ import { TypeData, TypePoint, TypeHelperWrapperDotDirection, TypeConfig, TypeConfigStrict } from '@idraw/types'; import Board from '@idraw/board'; +import util from '@idraw/util'; import { Renderer } from './lib/renderer'; import { Element } from './lib/element'; import { Helper } from './lib/helper'; import { mergeConfig } from './lib/config'; +const { time } = util; + type Options = { width: number; height: number; @@ -130,7 +133,7 @@ class Core { } this[_board].on('point', this._handlePoint.bind(this)); this[_board].on('moveStart', this._handleMoveStart.bind(this)); - this[_board].on('move', this._handleMove.bind(this)); + this[_board].on('move', time.throttle(this._handleMove.bind(this), 16)); this[_board].on('moveEnd', this._handleMoveEnd.bind(this)); } diff --git a/packages/core/src/lib/draw/index.ts b/packages/core/src/lib/draw/index.ts index 7e4cf1e..71cb631 100644 --- a/packages/core/src/lib/draw/index.ts +++ b/packages/core/src/lib/draw/index.ts @@ -34,18 +34,23 @@ export function drawContext( switch (elem.type) { case 'rect': { drawRect(ctx, elem as TypeElement<'rect'>); + break; } case 'text': { drawText(ctx, elem as TypeElement<'text'>, loader, helperConfig); + break; } case 'image': { drawImage(ctx, elem as TypeElement<'image'>, loader, helperConfig); + break; } case 'svg': { drawSVG(ctx, elem as TypeElement<'svg'>, loader, helperConfig); + break; } default: { // nothing + break; } } } diff --git a/packages/core/src/lib/draw/text.ts b/packages/core/src/lib/draw/text.ts index 0b8f57a..8b49168 100644 --- a/packages/core/src/lib/draw/text.ts +++ b/packages/core/src/lib/draw/text.ts @@ -22,6 +22,7 @@ export function drawText( ...{ fontSize: 12, fontFamily: 'sans-serif', + textAlign: 'center', }, ...elem.desc }; @@ -32,7 +33,7 @@ export function drawText( fontFamily: desc.fontFamily }); const fontHeight = desc.lineHeight || desc.fontSize; - + const lines: {text: string, width: number}[] = []; let lineText = ''; let lineNum = 0; for (let i = 0; i < desc.text.length; i++) { @@ -40,7 +41,10 @@ export function drawText( if (ctx.measureText(lineText + (desc.text[i] || '')).width < ctx.calcDeviceNum(elem.w)) { lineText += (desc.text[i] || ''); } else { - ctx.fillText(lineText, elem.x, elem.y + lineNum * fontHeight); + lines.push({ + text: lineText, + width: ctx.calcScreenNum(ctx.measureText(lineText).width), + }); lineText = (desc.text[i] || ''); lineNum ++; } @@ -49,15 +53,35 @@ export function drawText( } if (lineText && desc.text.length - 1 === i) { if ((lineNum + 1) * fontHeight < elem.h) { - ctx.fillText(lineText, elem.x, elem.y + lineNum * fontHeight); + lines.push({ + text: lineText, + width: ctx.calcScreenNum(ctx.measureText(lineText).width), + }); break; } } } + + // draw text lines + let _y = elem.y; + if (lines.length * fontHeight < elem.h) { + _y += ((elem.h - lines.length * fontHeight) / 2); + } + lines.forEach((line, i) => { + let _x = elem.x; + if (desc.textAlign === 'center') { + _x = elem.x + (elem.w - line.width) / 2 + } else if (desc.textAlign === 'right') { + _x = elem.x + (elem.w - line.width); + } + ctx.fillText(line.text, _x, _y + fontHeight * i); + }); + }); } + // export function createTextSVG(elem: TypeElement<'text'>): string { // const svg = ` // diff --git a/packages/types/src/lib/context.ts b/packages/types/src/lib/context.ts index dc4d59c..882db11 100644 --- a/packages/types/src/lib/context.ts +++ b/packages/types/src/lib/context.ts @@ -15,6 +15,7 @@ interface TypeContext { devicePixelRatio: number; }; calcDeviceNum(num: number): number; + calcScreenNum(num: number): number; setFillStyle(color: string | CanvasPattern): void; fill(fillRule?: CanvasFillRule | undefined): void; diff --git a/packages/types/src/lib/element.ts b/packages/types/src/lib/element.ts index 92c5468..cebb671 100644 --- a/packages/types/src/lib/element.ts +++ b/packages/types/src/lib/element.ts @@ -35,7 +35,8 @@ type TypeElemDescText = { lineHeight?: number; fontWeight?: string; fontFamily?: string; - backgroundColor?: string; + textAlign?: 'center' | 'left' | 'right' + // backgroundColor?: string; } type TypeElemDescCircle = { diff --git a/scripts/rollup.config.js b/scripts/rollup.config.js index e9141cf..778ccd5 100644 --- a/scripts/rollup.config.js +++ b/scripts/rollup.config.js @@ -51,7 +51,7 @@ function createConfigItem(params) { format, name: name, esModule: esModule === true, - sourcemap: true, + // sourcemap: true, exports }, plugins: [ diff --git a/scripts/screen.config.js b/scripts/screen.config.js index e6e7f6c..bed60ee 100644 --- a/scripts/screen.config.js +++ b/scripts/screen.config.js @@ -1,7 +1,10 @@ const pageList = [ - // { path: 'paint/examples/path/draw.html', w: 600, h: 600, delay: 200 }, - // { path: 'paint/examples/path/play.html', w: 600, h: 600, delay: 8000 }, - { path: 'drag-core/examples/demo.html', w: 500, h: 1000, delay: 2000 }, + { path: 'board/examples/features/test.html', w: 600, h: 400, delay: 1000 }, + { path: 'core/examples/features/rect.html', w: 600, h: 400, delay: 1000 }, + { path: 'core/examples/features/text.html', w: 600, h: 400, delay: 1000 }, + { path: 'core/examples/features/svg.html', w: 600, h: 400, delay: 1000 }, + { path: 'core/examples/features/image.html', w: 600, h: 400, delay: 1000 }, + // { path: 'core/examples/test.html', w: 600, h: 600, delay: 8000 }, ] module.exports = { diff --git a/scripts/snapshot.js b/scripts/snapshot.js index a9cc823..165f1cf 100644 --- a/scripts/snapshot.js +++ b/scripts/snapshot.js @@ -2,6 +2,7 @@ const jimp = require('jimp'); const path = require('path'); const { delay } = require('./util/time'); const { createScreenshot } = require('./util/screen'); +const { removeFullDir } = require('./util/file'); const { pageList } = require('./screen.config'); const snapshotDir = path.join(__dirname, '..', '__tests__', 'snapshot'); @@ -9,13 +10,18 @@ const snapshotDir = path.join(__dirname, '..', '__tests__', 'snapshot'); main(); async function main() { + removeFullDir(snapshotDir); const middlewares = []; pageList.forEach((p) => { middlewares.push(async (ctx = {}, next) => { const { page, port } = ctx; await page.setViewport( { width: p.w, height: p.h } ); const pageUrl = `http://127.0.0.1:${port}/packages/${p.path || ''}`; - await page.goto(pageUrl); + const result = await page.goto(pageUrl); + if (result.status() === 404) { + console.error(`404 Not Found: ${pageUrl}`) + throw Error('404 status code found in result') + } await delay(p.delay || 100); const buf = await page.screenshot(); (await jimp.read(buf)).scale(1).quality(100).write(createPicPath(p.path)); @@ -28,7 +34,7 @@ async function main() { function createPicPath(pagePath) { let picPath = path.join(snapshotDir, pagePath); - picPath = picPath.replace(/\.html$/, '.jpg'); + picPath = picPath + '.jpg'; // picPath.replace(/\.html$/, '.jpg'); return picPath; } diff --git a/scripts/util/file.js b/scripts/util/file.js new file mode 100644 index 0000000..7f9ef77 --- /dev/null +++ b/scripts/util/file.js @@ -0,0 +1,26 @@ +const fs = require('fs'); +const path = require('path'); + +function removeFullDir(dirPath) { + let files = []; + if (fs.existsSync(dirPath)) { + files = fs.readdirSync(dirPath); + files.forEach((filename) => { + let curPath = path.join(dirPath, filename); + const stat = fs.statSync(curPath); + if(stat.isDirectory()) { + removeFullDir(curPath); + } else if (stat.isFile()) { + // fs.unlinkSync(curPath); + fs.rmSync(curPath); + } else { + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(dirPath); + } +} + +module.exports = { + removeFullDir, +} \ No newline at end of file