Skip to content

Commit bc60a4b

Browse files
authored
fix: draw the canvas at the screen's pixel density (#263)
1 parent abb64b7 commit bc60a4b

2 files changed

Lines changed: 40 additions & 5 deletions

File tree

‎src/components/canvas/shotstack-canvas.ts‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ export class Canvas {
172172
this.currentZoom = Math.min(Math.max(newZoom, this.minZoom), this.maxZoom);
173173

174174
const stageCenter = {
175-
x: this.application.canvas.width / 2,
176-
y: this.application.canvas.height / 2
175+
x: this.viewportSize.width / 2,
176+
y: this.viewportSize.height / 2
177177
};
178178

179179
const distanceFromCenter = {
@@ -239,9 +239,10 @@ export class Canvas {
239239
return;
240240
}
241241

242+
// In CSS pixels: the canvas element's own width counts device pixels.
242243
this.viewportContainer.position = {
243-
x: this.application.canvas.width / 2 - (this.edit.size.width * this.currentZoom) / 2,
244-
y: this.application.canvas.height / 2 - (this.edit.size.height * this.currentZoom) / 2
244+
x: this.viewportSize.width / 2 - (this.edit.size.width * this.currentZoom) / 2,
245+
y: this.viewportSize.height / 2 - (this.edit.size.height * this.currentZoom) / 2
245246
};
246247

247248
this.syncContentTransforms();
@@ -380,7 +381,9 @@ export class Canvas {
380381
target,
381382
...(frame ? { frame } : {}),
382383
format: pixiFormat,
383-
quality: options.quality ?? 0.85
384+
quality: options.quality ?? 0.85,
385+
// Captures stay in output pixels whatever the screen's density.
386+
resolution: 1
384387
});
385388
}
386389

@@ -623,6 +626,9 @@ export class Canvas {
623626
width: this.viewportSize.width,
624627
height: this.viewportSize.height,
625628
antialias: true,
629+
// Draws at the screen's pixel density, so text and frames stay sharp on phones and retina screens. 2 caps the fill cost on 3x phones.
630+
resolution: Math.min(window.devicePixelRatio || 1, 2),
631+
autoDensity: true,
626632
powerPreference: "high-performance",
627633
eventFeatures: {
628634
globalMove: true, // Required for drag handling in SelectionHandles

‎tests/capture-frame.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,33 @@ describe("Canvas.captureFrame", () => {
8787
expect(ticker.start).not.toHaveBeenCalled();
8888
expect(ticker.started).toBe(false);
8989
});
90+
91+
it("captures output pixels whatever the screen's density", async () => {
92+
const { canvas } = setup();
93+
const { base64 } = (canvas as unknown as { application: { renderer: { extract: { base64: jest.Mock } } } }).application.renderer.extract;
94+
95+
await canvas.captureFrame();
96+
97+
expect(base64).toHaveBeenCalledWith(expect.objectContaining({ resolution: 1 }));
98+
});
99+
});
100+
101+
describe("Canvas.centerEdit", () => {
102+
it("centres in CSS pixels when the canvas element is drawn at twice that density", () => {
103+
const viewportContainer = { position: point(0, 0), scale: point(0.2, 0.2) };
104+
const canvas = Object.create(Canvas.prototype) as Canvas;
105+
Object.assign(canvas, {
106+
edit: { size: { width: 1920, height: 1080 } },
107+
application: { canvas: { width: 800, height: 600 } },
108+
viewportSize: { width: 400, height: 300 },
109+
currentZoom: 0.2,
110+
viewportContainer,
111+
overlayContainer: { position: point(0, 0), scale: point(1, 1) },
112+
uiController: null
113+
});
114+
115+
canvas.centerEdit();
116+
117+
expect(viewportContainer.position).toMatchObject({ x: 400 / 2 - (1920 * 0.2) / 2, y: 300 / 2 - (1080 * 0.2) / 2 });
118+
});
90119
});

0 commit comments

Comments
 (0)