From 096a0ebe642e3861e3c2688cd55d74cd13573cff Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Wed, 22 Feb 2023 12:50:39 +0100 Subject: [PATCH] :sparkles: Add downscale option --- src/index.js | 29 +++++++++++++++-------------- src/solid/PixelPerfectCanvas.tsx | 17 ++++++++--------- test/solid/App.tsx | 1 + vite.config.ts | 4 ++++ 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/index.js b/src/index.js index 0972616..34b4f00 100644 --- a/src/index.js +++ b/src/index.js @@ -14,29 +14,32 @@ export const MAX_CANVAS_SIZE = 10000; * the dimensions of the canvas. * @prop {AttachCanvasUpdate=} onResize - A callback function which will be called whenever the * dimensions of the canvas change. + * @prop {number=} downscale - An integer to downscale the canvas by, + * can be used to render the canvas at a lower resolution. **/ /** + * A function to call to unbind the event listeners attached to a canvas through `attachCanvas`. * @callback AttachCanvasUnbind * @returns {void} **/ /** - * @typedef {object} AttachCanvas - * @prop {AttachCanvasUnbind} unbind - Call this function to remove the event listeners attached - * to the canvas - **/ - -/** + * Makes the given canvas pixel-perfect, by dynamically resizing it + * in order to have the canvas pixel resolution be always equal to the device resolution. + * + * Returns a function to "unbind" the bound listeners. + * * @param {HTMLCanvasElement} canvas * @param {AttachCanvasOptions} options - * @returns {HTMLCanvasElement & AttachCanvas} + * @returns {AttachCanvasUnbind} **/ export function attachCanvas(canvas, options = {}) { const EPSILON = 0.001; canvas.style.imageRendering = "pixelated"; const container = options.container ?? canvas.parentNode; const onResize = options.onResize ?? (() => {}); + const downscale = options.downscale ?? 1; let old_width = null; let old_height = null; @@ -50,8 +53,8 @@ export function attachCanvas(canvas, options = {}) { }; // Compute native dimensions of canvas - width = width * dpr; - height = height * dpr; + width = width * dpr / downscale; + height = height * dpr / downscale; if (isInteger(width, EPSILON) && isInteger(height, EPSILON)) { width = Math.round(width); height = Math.round(height); @@ -82,8 +85,8 @@ export function attachCanvas(canvas, options = {}) { canvas.height = height; // Set style width to guarantee perfect scaling - canvas.style.width = `${width / dpr}px`; - canvas.style.height = `${height / dpr}px`; + canvas.style.width = `${width / dpr * downscale}px`; + canvas.style.height = `${height / dpr * downscale}px`; onResize(canvas, width, height); } @@ -106,7 +109,7 @@ export function attachCanvas(canvas, options = {}) { resize(window.devicePixelRatio ?? 1); }, 0); - canvas.unbind = function unbind() { + return function unbind() { if (resize_observer) { resize_observer.disconnect(); // Drop resize_observer @@ -119,8 +122,6 @@ export function attachCanvas(canvas, options = {}) { unbind_dpr = null; } }; - - return canvas; } export function listenPixelRatio(element, callback, fire_first = false) { diff --git a/src/solid/PixelPerfectCanvas.tsx b/src/solid/PixelPerfectCanvas.tsx index aa130c8..7ce654b 100644 --- a/src/solid/PixelPerfectCanvas.tsx +++ b/src/solid/PixelPerfectCanvas.tsx @@ -1,5 +1,5 @@ import { JSX, Component, createEffect, onCleanup } from "solid-js"; -import { attachCanvas } from '../index'; +import { attachCanvas, AttachCanvasOptions } from '../index'; import styles from './PixelPerfectCanvas.module.css'; export type PixelPerfectCanvasProps = { @@ -28,7 +28,7 @@ export type PixelPerfectCanvasProps = { * Callback called after the event listeners for dimension and DPR changes are detached. **/ onDetach?: (canvas: HTMLCanvasElement) => void, -} +} & Omit /** * A canvas that is wrapped in a container (which will be styled with `style` or `class`), @@ -43,20 +43,19 @@ export const PixelPerfectCanvas: Component = (props) => let canvasRef: HTMLCanvasElement; let containerRef: HTMLDivElement; - createEffect>((old) => { - const result = attachCanvas(canvasRef, { + createEffect(() => { + const unbind = attachCanvas(canvasRef, { container: containerRef, onResize: props.onResize, + downscale: props.downscale }); - props.onAttach?.(result); + props.onAttach?.(canvasRef); onCleanup(() => { - result.unbind(); - props.onDetach?.(result); + unbind(); + props.onDetach?.(canvasRef); }); - - return result; }); return (
{ }} onResize={update} onAttach={setCanvasRef} + downscale={8} />