fabric.js: Responsive canvas

The fabric canvas is resized automatically within the available width of the parent element.

by Arjan Haverkamp

HTML

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/fabric.min.js"></script>
<!-- Works with any percentage width and any canvas dimensions -->
<div style="width:75%">
   <canvas id="canvas" width="800" height="600" style="border:1px solid blue"></canvas>
</div>

CSS

/* This is crucial! */
.canvas-container { width:100% !important; }

JavaScript

/*
 * IMPORTANT: You must declare the following rule in your stylesheet:
 * .canvas-container { width:100% !important; }
 */

const canvas = document.getElementById('canvas');
const fabricCanvas = new fabric.Canvas(canvas);
const canvasParent = canvas.parentElement;

// Add some shapes to the canvas
const circle = new fabric.Circle({
   radius: 120, fill: 'green', left: 100, top: 100
});

const triangle = new fabric.Triangle({
	width: 250, height: 200, fill: 'blue', left: 350, top: 250
});

fabricCanvas.add(circle, triangle);


// Resize pixelsize of canvas, set viewport transform
// Benefit: resize handles keep original size
const resizeCanvas1 = () => {
  const containerWidth = canvasParent.clientWidth;
  const fabricWidth = fabricCanvas.getWidth();
  const fabricHeight = fabricCanvas.getHeight();
  const ratio = fabricWidth / fabricHeight;
  const scale = containerWidth / fabricWidth;

  const zoom = fabricCanvas.getZoom() * scale;
  fabricCanvas.setDimensions({width: containerWidth, height: containerWidth / ratio});
  fabricCanvas.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);
}

// Resize CSS size of canvas, keep pixelsize as is
// Drawback: resize handles get very small when canvas is large
const resizeCanvas2 = () => {
  const fabricWidth = fabricCanvas.getWidth();
  const fabricHeight = fabricCanvas.getHeight();
  const cssWidth = Math.min(canvasParent.clientWidth, fabricWidth);
  const ratio = fabricWidth / canvasParent.clientWidth;

  fabricCanvas.setDimensions(
		{
			width: cssWidth + 'px', 
			height: cssWidth / (fabricWidth/fabricHeight) + 'px'
		},
		{ cssOnly: true }
  )
  .requestRenderAll();
}

// Choose either one:
window.addEventListener('resize', resizeCanvas1);
//window.addEventListener('resize', resizeCanvas2);

window.dispatchEvent(new Event('resize'));