69
__tests__/e2e.test.js
Normal file
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
BIN
__tests__/snapshot/board/examples/features/test.html.jpg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
__tests__/snapshot/core/examples/features/image.html.jpg
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
__tests__/snapshot/core/examples/features/rect.html.jpg
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
__tests__/snapshot/core/examples/features/svg.html.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
__tests__/snapshot/core/examples/features/text.html.jpg
Normal file
|
After Width: | Height: | Size: 177 KiB |
32
jest.config.js
Normal file
|
|
@ -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": [
|
||||
"<rootDir>"
|
||||
],
|
||||
"testRegex": "(/packages/([^\/]{1,})/__tests__/.*|\\.test)\\.ts$",
|
||||
"setupFiles": [
|
||||
"jest-canvas-mock"
|
||||
]
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
129
packages/board/__tests__/__snapshots__/index.test.ts.snap
Normal file
|
|
@ -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": <canvas
|
||||
height="1600"
|
||||
width="2400"
|
||||
/>,
|
||||
"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": <canvas
|
||||
height="1600"
|
||||
width="2400"
|
||||
/>,
|
||||
"sHeight": 1600,
|
||||
"sWidth": 2400,
|
||||
"sx": 0,
|
||||
"sy": 0,
|
||||
},
|
||||
"transform": Array [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "drawImage",
|
||||
},
|
||||
]
|
||||
`;
|
||||
81
packages/board/__tests__/index.test.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import Board from './../src';
|
||||
|
||||
test('testing', async () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="mount"></div>
|
||||
`;
|
||||
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();
|
||||
|
||||
});
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
29
packages/board/examples/features/test.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<style>
|
||||
html,body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#mount canvas {
|
||||
border-right: 1px solid #aaaaaa40;
|
||||
border-bottom: 1px solid #aaaaaa40;
|
||||
background-image:
|
||||
linear-gradient(#aaaaaa40 1px, transparent 0),
|
||||
linear-gradient(90deg, #aaaaaa40 1px, transparent 0),
|
||||
linear-gradient(#aaa 1px, transparent 0),
|
||||
linear-gradient(90deg, #aaa 1px, transparent 0);
|
||||
background-size: 10px 10px, 10px 10px, 50px 50px, 50px 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="mount"></div>
|
||||
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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'];
|
||||
}
|
||||
|
|
@ -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;
|
||||
25
packages/core/examples/features/image.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<link rel="stylesheet" href="./css/index.css" />
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; }
|
||||
.dashboard .row { margin: 0 }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="dashboard">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div id="mount"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script type="module" src="./lib/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<script src="./../dist/index.global.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script type="module" src="./lib/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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',
|
||||
}
|
||||
}
|
||||
]
|
||||
42
packages/core/examples/features/lib/data/index.js
Normal file
|
|
@ -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 = /(?<pageName>[\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;
|
||||
// }
|
||||
80
packages/core/examples/features/lib/data/text.js
Normal file
|
|
@ -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;
|
||||
|
|
@ -5,6 +5,7 @@ let hasInited = false;
|
|||
|
||||
export function doElemens(core) {
|
||||
if (hasInited === true) return;
|
||||
if (!dom) return;
|
||||
renderElemens(core);
|
||||
listenElements(core);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
25
packages/core/examples/features/rect.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<link rel="stylesheet" href="./css/index.css" />
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; }
|
||||
.dashboard .row { margin: 0 }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="dashboard">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div id="mount"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script type="module" src="./lib/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
25
packages/core/examples/features/svg.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<link rel="stylesheet" href="./css/index.css" />
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; }
|
||||
.dashboard .row { margin: 0 }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="dashboard">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div id="mount"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script type="module" src="./lib/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
25
packages/core/examples/features/text.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<link rel="stylesheet" href="./css/index.css" />
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; }
|
||||
.dashboard .row { margin: 0 }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="dashboard">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div id="mount"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script type="module" src="./lib/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = `
|
||||
// <svg xmlns="http://www.w3.org/2000/svg" width="${elem.w}" height = "${elem.h}">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ type TypeElemDescText = {
|
|||
lineHeight?: number;
|
||||
fontWeight?: string;
|
||||
fontFamily?: string;
|
||||
backgroundColor?: string;
|
||||
textAlign?: 'center' | 'left' | 'right'
|
||||
// backgroundColor?: string;
|
||||
}
|
||||
|
||||
type TypeElemDescCircle = {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ function createConfigItem(params) {
|
|||
format,
|
||||
name: name,
|
||||
esModule: esModule === true,
|
||||
sourcemap: true,
|
||||
// sourcemap: true,
|
||||
exports
|
||||
},
|
||||
plugins: [
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
26
scripts/util/file.js
Normal file
|
|
@ -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,
|
||||
}
|
||||