Responsive Canvas Stage Demo
Do you need responsive/adaptive canvas for your desktop and mobile applications?
There are many ways to make your canvas stage "responsive", and you may need different behavior for different applications.
This demo shows the simplest solution: fitting a canvas stage into the user's window with scaling. In this example, we'll focus on adjusting the stage WIDTH. You can add extra logic if you need to fit height too.
Instructions: Try resizing your browser window and see how the canvas adapts.
- Vanilla
- React
- Vue
import Konva from 'konva'; // Define virtual size for our scene // The real size will be different to fit user's page const sceneWidth = 1000; const sceneHeight = 1000; // Create stage with initial size const stage = new Konva.Stage({ container: 'container', width: sceneWidth, height: sceneHeight, }); const layer = new Konva.Layer(); stage.add(layer); // Add circle in the center const circle = new Konva.Circle({ radius: 50, fill: 'red', x: stage.width() / 2, y: stage.height() / 2, }); layer.add(circle); // Add rectangle in bottom right of the stage const rect = new Konva.Rect({ fill: 'green', x: stage.width() - 100, y: stage.height() - 100, width: 100, height: 100, }); layer.add(rect); // Add some text const text = new Konva.Text({ x: 20, y: 20, text: 'Try resizing your browser window', fontSize: 20, fontFamily: 'Arial', fill: 'black', }); layer.add(text); // Function to make the stage responsive function fitStageIntoParentContainer() { // Get the container element const container = document.getElementById('container'); // Make the container take up the full width container.style.width = '100%'; // Get current container width const containerWidth = container.offsetWidth; // Calculate scale based on virtual width vs actual width const scale = containerWidth / sceneWidth; // Set stage dimensions and scale stage.width(sceneWidth * scale); stage.height(sceneHeight * scale); stage.scale({ x: scale, y: scale }); } // Initial fit fitStageIntoParentContainer(); // Adapt the stage on window resize window.addEventListener('resize', fitStageIntoParentContainer);
import { useState, useEffect, useRef } from 'react'; import { Stage, Layer, Circle, Rect, Text } from 'react-konva'; const App = () => { // Define virtual size for our scene const sceneWidth = 1000; const sceneHeight = 1000; // State to track current scale and dimensions const [stageSize, setStageSize] = useState({ width: sceneWidth, height: sceneHeight, scale: 1 }); // Reference to parent container const containerRef = useRef(null); // Function to handle resize const updateSize = () => { if (!containerRef.current) return; // Get container width const containerWidth = containerRef.current.offsetWidth; // Calculate scale const scale = containerWidth / sceneWidth; // Update state with new dimensions setStageSize({ width: sceneWidth * scale, height: sceneHeight * scale, scale: scale }); }; // Update on mount and when window resizes useEffect(() => { updateSize(); window.addEventListener('resize', updateSize); return () => { window.removeEventListener('resize', updateSize); }; }, []); return ( <div ref={containerRef} style={{ width: '100%', height: '100%' }} > <Stage width={stageSize.width} height={stageSize.height} scaleX={stageSize.scale} scaleY={stageSize.scale} > <Layer> <Circle radius={50} fill="red" x={sceneWidth / 2} y={sceneHeight / 2} /> <Rect fill="green" x={sceneWidth - 100} y={sceneHeight - 100} width={100} height={100} /> <Text x={20} y={20} text="Try resizing your browser window" fontSize={20} fontFamily="Arial" fill="black" /> </Layer> </Stage> </div> ); }; export default App;
<template> <div ref="containerRef" style="width: 100%; height: 100%;"> <v-stage :config="{ width: stageWidth, height: stageHeight, scaleX: scale, scaleY: scale }" > <v-layer> <v-circle :config="{ radius: 50, fill: 'red', x: sceneWidth / 2, y: sceneHeight / 2 }" /> <v-rect :config="{ fill: 'green', x: sceneWidth - 100, y: sceneHeight - 100, width: 100, height: 100 }" /> <v-text :config="{ x: 20, y: 20, text: 'Try resizing your browser window', fontSize: 20, fontFamily: 'Arial', fill: 'black' }" /> </v-layer> </v-stage> </div> </template> <script setup> import { ref, onMounted, onUnmounted, computed } from 'vue'; // Define virtual size for our scene const sceneWidth = 1000; const sceneHeight = 1000; // Reactive references const containerRef = ref(null); const scale = ref(1); // Computed properties for stage dimensions const stageWidth = computed(() => sceneWidth * scale.value); const stageHeight = computed(() => sceneHeight * scale.value); // Function to handle resize const updateSize = () => { if (!containerRef.value) return; // Get container width const containerWidth = containerRef.value.offsetWidth; // Calculate scale scale.value = containerWidth / sceneWidth; }; // Add event listeners onMounted(() => { updateSize(); window.addEventListener('resize', updateSize); }); // Clean up onUnmounted(() => { window.removeEventListener('resize', updateSize); }); </script>