diff --git a/packages/board/__tests__/lib/__snapshots__/context.test.ts.snap b/packages/board/__tests__/lib/__snapshots__/context.test.ts.snap index 4e880fc..63ac58a 100644 --- a/packages/board/__tests__/lib/__snapshots__/context.test.ts.snap +++ b/packages/board/__tests__/lib/__snapshots__/context.test.ts.snap @@ -310,6 +310,77 @@ Array [ ] `; +exports[`@idraw/board: src/lib/context Context.createPattern 1`] = ` +Array [ + Object { + "props": Object { + "height": 600, + "width": 600, + "x": 0, + "y": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "fillRect", + }, +] +`; + +exports[`@idraw/board: src/lib/context Context.drawImage 1`] = ` +Array [ + Object { + "props": Object { + "dHeight": 0, + "dWidth": 0, + "dx": 22, + "dy": 24, + "img": , + "sHeight": 0, + "sWidth": 0, + "sx": 0, + "sy": 0, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "drawImage", + }, + Object { + "props": Object { + "dHeight": 104, + "dWidth": 102, + "dx": 22, + "dy": 24, + "img": , + "sHeight": 204, + "sWidth": 202, + "sx": 122, + "sy": 124, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "drawImage", + }, +] +`; + exports[`@idraw/board: src/lib/context Context.fill 1`] = ` Array [ Object { @@ -365,6 +436,28 @@ Array [ ] `; +exports[`@idraw/board: src/lib/context Context.fillText 1`] = ` +Array [ + Object { + "props": Object { + "maxWidth": null, + "text": "Hello world", + "x": 100, + "y": 200, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "fillText", + }, +] +`; + exports[`@idraw/board: src/lib/context Context.lineTo 1`] = ` Array [ Object { @@ -510,3 +603,277 @@ Array [ }, ] `; + +exports[`@idraw/board: src/lib/context Context.restore 1`] = ` +Array [ + Object { + "props": Object { + "height": 200, + "width": 200, + "x": 20, + "y": 20, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "fillRect", + }, + Object { + "props": Object { + "height": 200, + "width": 200, + "x": 300, + "y": 150, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "fillRect", + }, +] +`; + +exports[`@idraw/board: src/lib/context Context.rotate 1`] = ` +Array [ + Object { + "props": Object { + "path": Array [ + Object { + "props": Object {}, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "beginPath", + }, + Object { + "props": Object { + "height": 400, + "width": 200, + "x": 20, + "y": 40, + }, + "transform": Array [ + 0.8660254037844387, + 0.49999999999999994, + -0.49999999999999994, + 0.8660254037844387, + 0, + 0, + ], + "type": "rect", + }, + ], + }, + "transform": Array [ + 0.8660254037844387, + 0.49999999999999994, + -0.49999999999999994, + 0.8660254037844387, + 0, + 0, + ], + "type": "stroke", + }, +] +`; + +exports[`@idraw/board: src/lib/context Context.save 1`] = ` +Array [ + Object { + "props": Object { + "height": 200, + "width": 200, + "x": 20, + "y": 20, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "fillRect", + }, + Object { + "props": Object { + "height": 200, + "width": 200, + "x": 300, + "y": 150, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "fillRect", + }, +] +`; + +exports[`@idraw/board: src/lib/context Context.scale 1`] = ` +Array [ + Object { + "props": Object { + "path": Array [ + Object { + "props": Object {}, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "beginPath", + }, + Object { + "props": Object { + "height": 400, + "width": 200, + "x": 20, + "y": 40, + }, + "transform": Array [ + 2, + 0, + 0, + 3, + 0, + 0, + ], + "type": "rect", + }, + ], + }, + "transform": Array [ + 2, + 0, + 0, + 3, + 0, + 0, + ], + "type": "stroke", + }, +] +`; + +exports[`@idraw/board: src/lib/context Context.stroke 1`] = ` +Array [ + Object { + "props": Object { + "path": Array [ + Object { + "props": Object {}, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "beginPath", + }, + Object { + "props": Object { + "height": 400, + "width": 200, + "x": 20, + "y": 40, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "rect", + }, + ], + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "stroke", + }, +] +`; + +exports[`@idraw/board: src/lib/context Context.translate 1`] = ` +Array [ + Object { + "props": Object { + "path": Array [ + Object { + "props": Object {}, + "transform": Array [ + 1, + 0, + 0, + 1, + 0, + 0, + ], + "type": "beginPath", + }, + Object { + "props": Object { + "height": 400, + "width": 200, + "x": 20, + "y": 40, + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 100, + 120, + ], + "type": "rect", + }, + ], + }, + "transform": Array [ + 1, + 0, + 0, + 1, + 100, + 120, + ], + "type": "stroke", + }, +] +`; diff --git a/packages/board/__tests__/lib/context.test.ts b/packages/board/__tests__/lib/context.test.ts index c6863f4..03004ab 100644 --- a/packages/board/__tests__/lib/context.test.ts +++ b/packages/board/__tests__/lib/context.test.ts @@ -115,7 +115,6 @@ describe('@idraw/board: src/lib/context', () => { }); }); - test('Context.setFillStyle', async () => { const opts = deepClone(options); const canvas = document.createElement('canvas'); @@ -129,7 +128,6 @@ describe('@idraw/board: src/lib/context', () => { expect(ctx2d.fillStyle).toStrictEqual(color); }); - test('Context.fill', async () => { const opts = deepClone(options); const canvas = document.createElement('canvas'); @@ -141,7 +139,7 @@ describe('@idraw/board: src/lib/context', () => { // @ts-ignore const calls = ctx2d.__getDrawCalls(); expect(calls).toMatchSnapshot(); - }) + }); test('Context.arc', async () => { const opts = deepClone(options); @@ -156,8 +154,7 @@ describe('@idraw/board: src/lib/context', () => { const calls = ctx2d.__getDrawCalls(); // console.log('calls =', JSON.stringify(calls, null, 2)); expect(calls).toMatchSnapshot(); - }) - + }); test('Context.rect', async () => { const opts = deepClone(options); @@ -172,7 +169,7 @@ describe('@idraw/board: src/lib/context', () => { const calls = ctx2d.__getDrawCalls(); // console.log('calls =', JSON.stringify(calls, null, 2)); expect(calls).toMatchSnapshot(); - }) + }); test('Context.fillRect', async () => { const opts = deepClone(options); @@ -193,7 +190,7 @@ describe('@idraw/board: src/lib/context', () => { // props: { x: 0, y: 0, width: 2000, height: 1800 } // } // ]); - }) + }); test('Context.clearRect', async () => { const opts = deepClone(options); @@ -207,7 +204,7 @@ describe('@idraw/board: src/lib/context', () => { const calls = ctx2d.__getDrawCalls(); // console.log('calls =', JSON.stringify(calls, null, 2)); expect(calls).toMatchSnapshot(); - }) + }); test('Context.beginPath', async () => { const opts = deepClone(options); @@ -222,8 +219,7 @@ describe('@idraw/board: src/lib/context', () => { const calls = ctx2d.__getDrawCalls(); // console.log('calls =', JSON.stringify(calls, null, 2)); expect(calls).toMatchSnapshot(); - }) - + }); test('Context.closePath', async () => { const opts = deepClone(options); @@ -238,7 +234,7 @@ describe('@idraw/board: src/lib/context', () => { const calls = ctx2d.__getDrawCalls(); // console.log('calls =', JSON.stringify(calls, null, 2)); expect(calls).toMatchSnapshot(); - }) + }); test('Context.lineTo', async () => { const opts = deepClone(options); @@ -253,7 +249,7 @@ describe('@idraw/board: src/lib/context', () => { const calls = ctx2d.__getDrawCalls(); // console.log('calls =', JSON.stringify(calls, null, 2)); expect(calls).toMatchSnapshot(); - }) + }); test('Context.moveTo', async () => { const opts = deepClone(options); @@ -313,7 +309,6 @@ describe('@idraw/board: src/lib/context', () => { expect(ctx2d.getLineDash()).toStrictEqual(lineDash.map(n => n * opts.devicePixelRatio)); }); - test('Context.setStrokeStyle', async () => { const opts = deepClone(options); const canvas = document.createElement('canvas'); @@ -327,67 +322,305 @@ describe('@idraw/board: src/lib/context', () => { expect(ctx2d.strokeStyle).toStrictEqual(color); }); - // 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 Context(ctx2d, opts); - // const x = 50; - // const y = 50; - // const w = 50; - // const h = 50; + // 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 Context(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(); + 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(); - // console.log('calls =', calls); - // // expect(calls).toMatchSnapshot(); - // console.log('ctx.isPointInPath(51, 51) =', ctx.isPointInPath(60, 60)) - // expect(ctx.isPointInPath(51, 51)) - // .toStrictEqual(ctx2d.isPointInPath(60 * opts.devicePixelRatio, 60 * opts.devicePixelRatio)); - // }); + // @ts-ignore + const calls = ctx2d.__getDrawCalls(); + expect(ctx.isPointInPath(51, 51)) + .toStrictEqual(ctx2d.isPointInPath(60 * opts.devicePixelRatio, 60 * opts.devicePixelRatio)); + }); - // test('Context.isPointInPathWithoutScroll', 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 Context(ctx2d, opts); + // TODO + test('Context.isPointInPathWithoutScroll', 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 Context(ctx2d, opts); - // ctx.setTransform({ - // scale: 2, - // scrollX: 10, - // scrollY: 10, - // }) - - // ctx.beginPath(); - // ctx.rect(50, 50, 100, 100); - // ctx.closePath(); - // // @ts-ignore - // const calls = ctx2d.__getDrawCalls(); - // // expect(calls).toMatchSnapshot(); - // console.log('ctx.isPointInPathWithoutScroll(51, 51) =', ctx.isPointInPathWithoutScroll(51, 51)) - // expect(ctx.isPointInPathWithoutScroll(51, 51)) - // .toStrictEqual( - // ctx2d.isPointInPath( - // 60 * opts.devicePixelRatio, - // 60 * opts.devicePixelRatio - // ) - // ); - // }); + ctx.setTransform({ + scale: 2, + scrollX: 10, + scrollY: 10, + }) + ctx.beginPath(); + ctx.rect(50, 50, 100, 100); + ctx.closePath(); + // @ts-ignore + const calls = ctx2d.__getDrawCalls(); + expect(ctx.isPointInPathWithoutScroll(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 Context(ctx2d, opts); + const color = '#f0f0f0'; + ctx.setStrokeStyle(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 Context(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 Context(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 Context(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 Context(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 Context(ctx2d, opts); + const img = new Image(); + const pattern = ctx.createPattern(img, 'repeat') as CanvasPattern; + ctx.setFillStyle(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 Context(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 Context(ctx2d, opts); + const textAlign = 'center'; + ctx.setTextAlign(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 Context(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 Context(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.setTextBaseline', 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 Context(ctx2d, opts); + const textBaseline = 'bottom'; + ctx.setTextBaseline(textBaseline); + expect(ctx2d.textBaseline).toStrictEqual(textBaseline) + }); + + test('Context.setGlobalAlpha', 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 Context(ctx2d, opts); + const globalAlpha = 0.45; + ctx.setGlobalAlpha(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 Context(ctx2d, opts); + ctx.save(); + ctx.setFillStyle('#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 Context(ctx2d, opts); + ctx.save(); + ctx.setFillStyle('#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 Context(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(); + }); + + }) diff --git a/packages/board/src/lib/context.ts b/packages/board/src/lib/context.ts index 22912da..6f85e34 100644 --- a/packages/board/src/lib/context.ts +++ b/packages/board/src/lib/context.ts @@ -73,7 +73,7 @@ class Context implements TypeContext { }; } - setFillStyle(color: string | CanvasPattern) { + setFillStyle(color: string | CanvasPattern | CanvasGradient) { this._ctx.fillStyle = color; }