idraw/packages/util/__tests__/lib/context.test.ts
2025-05-10 15:20:55 +08:00

511 lines
18 KiB
TypeScript

/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Context2D, deepClone } from '@idraw/util';
import { getData } from './data';
describe('@idraw/board: src/lib/context', () => {
const options = {
width: 600,
height: 400,
contextWidth: 1000,
contextHeight: 900,
devicePixelRatio: 2
};
test('Context', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const data = getData();
ctx.clearRect(0, 0, opts.contextWidth, opts.contextHeight);
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
data.elements.forEach((ele) => {
ctx.fillStyle = ele.detail.color;
ctx.fillRect(ele.x, ele.y, ele.w, ele.h);
});
// @ts-ignore;
const calls = ctx2d.__getDrawCalls();
expect(calls).toMatchSnapshot();
});
test('Context.setFillStyle', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const color = '#f0f0f0';
ctx.fillStyle = color;
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
expect(ctx2d.fillStyle).toStrictEqual(color);
});
test('Context.fill', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
expect(calls).toMatchSnapshot();
});
test('Context.arc', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.arc(70, 80, 50, 0, Math.PI * 2, true);
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.rect', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.rect(10, 20, 100, 200);
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.fillRect', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.fillRect(10, 20, 80, 100);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
// expect(calls).toStrictEqual([
// {
// type: 'fillRect',
// transform: [ 1, 0, 0, 1, 0, 0 ],
// props: { x: 0, y: 0, width: 2000, height: 1800 }
// }
// ]);
});
test('Context.clearRect', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.clearRect(0, 0, opts.contextWidth, opts.contextHeight);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.beginPath', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.beginPath();
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.closePath', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.closePath();
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.lineTo', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.lineTo(10, 20);
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.moveTo', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.moveTo(10, 20);
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.arcTo', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.arcTo(50, 50, 100, 100, Math.PI * 2);
ctx.fill();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.setLineWidth', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const lineWidth = 12;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.lineWidth = lineWidth;
expect(ctx2d.lineWidth).toStrictEqual(lineWidth * opts.devicePixelRatio);
});
test('Context.setLineDash', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const lineDash = [10, 20];
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.setLineDash(lineDash);
// @ts-ignore
// const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
// expect(calls).toMatchSnapshot();
expect(ctx2d.getLineDash()).toStrictEqual(lineDash.map((n) => n * opts.devicePixelRatio));
});
test('Context.setStrokeStyle', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const color = '#f0f0f0';
ctx.strokeStyle = color;
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
expect(ctx2d.strokeStyle).toStrictEqual(color);
});
// TODO
test('Context.isPointInPath', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
// const lineDash = [10, 20];
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const x = 50;
const y = 50;
const w = 50;
const h = 50;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + w, y);
ctx.lineTo(x + w, y + h);
ctx.lineTo(x, y + h);
ctx.lineTo(x, y);
ctx.closePath();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
expect(ctx.isPointInPath(51, 51)).toStrictEqual(ctx2d.isPointInPath(60 * opts.devicePixelRatio, 60 * opts.devicePixelRatio));
});
test('Context.setStrokeStyle', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const color = '#f0f0f0';
ctx.strokeStyle = color;
expect(ctx2d.strokeStyle).toStrictEqual(color);
});
test('Context.stroke', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.rect(10, 20, 100, 200);
ctx.stroke();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.translate', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const x = 50;
const y = 60;
ctx.translate(x, y);
ctx.rect(10, 20, 100, 200);
ctx.stroke();
ctx.translate(-x, -y);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
expect(calls).toMatchSnapshot();
});
test('Context.rotate', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const radian = Math.PI / 6;
ctx.rotate(radian);
ctx.rect(10, 20, 100, 200);
ctx.stroke();
ctx.rotate(-radian);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.drawImage', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const img = new Image();
const dx = 11;
const dy = 12;
const dw = 51;
const dh = 52;
const sx = 61;
const sy = 62;
const sw = 101;
const sh = 102;
ctx.drawImage(img, dx, dy);
ctx.drawImage(img, dx, dy, dw, dh);
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.createPattern', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const img = new Image();
const pattern = ctx.createPattern(img, 'repeat') as CanvasPattern;
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 300, 300);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(ctx2d.fillStyle).toStrictEqual(pattern);
expect(calls).toMatchSnapshot();
});
test('Context.measureText', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.$setFont({ fontSize: 20 });
const size = ctx.measureText('Hello World!');
expect(size.width).toStrictEqual(12);
});
test('Context.setTextAlign', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const textAlign = 'center';
ctx.textAlign = textAlign;
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
expect(ctx2d.textAlign).toStrictEqual(textAlign);
});
test('Context.fillText', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.fillText('Hello world', 50, 100);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.$setFont', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const fontSize = 22;
const fontFamily = 'Hello';
const fontWeight = 'bold';
ctx.$setFont({
fontSize,
fontFamily,
fontWeight
});
// console.log('ctx2d.font =', ctx2d.font);
expect(ctx2d.font).toStrictEqual([fontWeight, fontSize * opts.devicePixelRatio + 'px', fontFamily].join(' '));
});
test('Context.textBaseline', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const textBaseline = 'bottom';
ctx.textBaseline = textBaseline;
expect(ctx2d.textBaseline).toStrictEqual(textBaseline);
});
test('Context.globalAlpha', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const globalAlpha = 0.45;
ctx.globalAlpha = globalAlpha;
expect(ctx2d.globalAlpha).toStrictEqual(globalAlpha);
});
test('Context.save', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.save();
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(10, 10, 100, 100);
ctx.restore();
ctx.fillRect(150, 75, 100, 100);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.restore', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
ctx.save();
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(10, 10, 100, 100);
ctx.restore();
ctx.fillRect(150, 75, 100, 100);
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
test('Context.scale', async () => {
const opts = deepClone(options);
const canvas = document.createElement('canvas');
canvas.width = opts.contextWidth;
canvas.height = opts.contextHeight;
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
const ctx = new Context2D(ctx2d, opts);
const scaleX = 2;
const scaleY = 3;
ctx.scale(scaleX, scaleY);
ctx.rect(10, 20, 100, 200);
ctx.stroke();
// @ts-ignore
const calls = ctx2d.__getDrawCalls();
// console.log('calls =', JSON.stringify(calls, null, 2));
expect(calls).toMatchSnapshot();
});
});