Adjust the size of the canvas element based on changes to its parent's dimensions

I am working with an application that includes a div containing a canvas element to display an image. Additionally, there is a sidebar that can be hidden with the click of a button, causing all other elements to resize and adjust to the remaining space.

While this resizing behavior works well for elements with width/height set as percentages, it does not seem to apply to the canvas element itself.

The div structure I currently have is as follows:

<div id="background-img" class="image-div"></div>

.image-div {
  height: inherit;
  width: 100%;
  margin-top: 25px;
  position: absolute;
}

The initial height of the div is 305px but changes to 375px when the sidebar is hidden.

Below is the code used to create the canvas:

const imgDiv = document.getElementById('background-img');
if (imgDiv) {
  const blob = new Blob([svg], { type: 'image/svg+xml' });
  const url = URL.createObjectURL(blob);

  const canvas = document.createElement('canvas');
  canvas.className = 'canvas-background';
  canvas.id = 'canvas-id';
  const ctx = canvas.getContext('2d');

  canvas.width = imgDiv.clientWidth;
  canvas.height = imgDiv.clientHeight;

  const img = new Image();
  img.onload = function() {
    ctx.drawImage(img, 0, 0);
  };
  img.src = url;
  imgDiv.appendChild(canvas);
}

No additional CSS styles are applied to the canvas elsewhere in the code.

I'm seeking a way to enable the canvas to automatically resize itself whenever its parent's width and height change. This should include functionality for zooming in/out on the page, similar to how it behaves on Chrome with divs or img elements. Any guidance on achieving this would be greatly appreciated.

Answer №1

Currently, there isn't a straightforward solution to achieve this task. One might instinctively seek a resize event on an element, but such events are typically applicable only to the window, as elaborated in this MDN article.

After conducting thorough research and exploring various discussions, I have identified a few options for your consideration.

Continuous Loop Approach

You can render the image within a recursive function that continuously monitors the dimensions of the imgDiv as it draws itself.

CSS:

canvas {
    width: 100%;
    height: 100%;
}

JS:

function draw(ctx, imgDiv, img) {
    ctx.canvas.width = imgDiv.offsetWidth;
    ctx.canvas.height = imgDiv.offsetheight;
    ctx.drawImage(img, 0, 0);
    requestAnimationFrame(() => {
        draw(ctx, imgDiv, img);
    });
}

img.onload = function() {
    draw(ctx, imgDiv, img);
};

While this approach constantly processes updates, it may impact performance due to its resource-intensive nature. The use of requestAnimationFrame optimizes performance by syncing with the display's refresh rate, typically around 60 times per second.

ResizeObserver (Experimental)

This method introduces the novel concept of ResizeObserver, similar to other observer APIs like IntersectionObserver, PerformanceObserver, and MutationObserver. It tracks resizing changes in elements and triggers callbacks accordingly.

Implementation example:

const observer = new ResizeObserver(entries => {
    entries.forEach(entry => {
        // Update the canvas dimensions.
    });
});

// Observe the resizing of the image-div.
observer.observe(imgDiv);

Due to its experimental status, caution is advised when considering this approach, despite its applicability to your scenario. For details on browser support and usage guidelines, refer to resources such as Caniuse and Alligator.io.

Utilize the resize Event on window

This option primarily focuses on window or document width adjustments, requiring additional modifications for scenarios like sidebar expansions or contractions.

window.addEventListener('resize', (event) => {
    requestAnimationFrame(() => {
        // Update the canvas dimensions.
    });
});

For optimal performance, incorporating requestAnimationFrame here is crucial, considering that the resize event occurs for every pixel adjustment and may hinder efficiency.

Edit Note:

Although Artyomska has apparently resolved the issue, I am sharing my findings in case this discussion proves beneficial for future inquiries.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Accessing the URL causes malfunctioning of the dynamic routing in Angular 2

I am currently working on implementing dynamic routing functionality in my Angular application. So far, I have successfully achieved the following functionalities: Addition of routing to an existing angular component based on user input Removal of routin ...

Unable to generate a fresh database entry through form submission

I have designed User and Pairings models as shown below: class User < ActiveRecord::Base enum role: [:student, :supervisor, :admin] has_many :students, class_name: "User", foreign_key: "supervisor_id" belongs_to :supervisor, ...

Is there a way for me to gain entry to this array in vuejs?

Can anyone help me with accessing the objects in this array? I am using laravel, inertiajs, and vuejs. I am passing a variable from a laravel controller to a vuejs component with inertia.js. https://i.stack.imgur.com/p7yjL.png https://i.stack.imgur.com/y ...

Tips for modifying VueJS's <slot> content prior to component instantiation

I am working with a VueJS component called comp.vue: <template> <div> <slot></slot> </div> </template> <script> export default { data () { return { } }, } </script> When I ...

Unable to create the complete PDF file using html-pdf in a NodeJS environment

When trying to create a PDF from an HTML template, I am encountering some difficulties. While it works with static entries, I want to generate the PDF for multiple items that can vary each time. I feel like I might be overlooking something, so any suggesti ...

Is there a problem with the way divs are being displayed when using jQuery to show the first 12 elements with the class "hide-show"?

I am encountering a problem with displaying data using the jQuery slice() and show() methods to reveal dynamically generated divs from a PHP function. The code is as follows: <style> .hide-show { display :none; } </style> <div class="c ...

Is it possible to customize the background color of select2 boxes separately for each option?

I recently implemented the select2 plugin and now I'm looking to add a new class within the .select2-results .select2-highlighted class in order to change the background color. Does anyone have any suggestions on how to achieve this? ...

Array of Ascending Progress Indicators

I currently have a progress bar that I'm happy with, but I want to enhance it by incorporating stacked progress bars. My aim is to have the progress bar behave in the following way when an option is selected: https://i.stack.imgur.com/Pw9AH.png & ...

Encountering the error 'Cannot read property 'length' of undefined' while attempting to fetch data from a URL using node.js

While attempting to create a Discord-Bot capable of looking up definitions on urbandictionary, I encountered an error after trying to fetch the json from their api. const args = Array.prototype.slice.call(commandArgs); if (!args.length) { return m ...

We could not locate the requested resource with a DELETE request using the fetch JSON method

Currently, I am in the process of developing a webpage that utilizes JSON API REST alongside XAMPP with an Apache server. Up until now, everything has been working smoothly as I have been utilizing the DELETE method successfully. However, I seem to have hi ...

Discover the secrets of extracting the ID from a MenuItem within a Menu

Table Ui with menu Struggling with fetching the correct user ID in the edit button The edit button for all users is only displaying the last user's ID If the edit button is not nested inside the Menu, it works fine. However, within the Menu, it onl ...

Do we need to use the render method in ReactJs?

I'm curious about how ReactJs uses its Render() functionality. Let's say we have some HTML code within index.html: <section id="Hello"> <h1>Hello world</h1> <p>Something something Darkside</p> </section&g ...

The switch statement remains unchanged for varying variables

Here is some code that I am working with: updateTable(selectedIndex) { console.log("running updateTable function"); let level = ''; // if (selectedIndex == 1){ // this.setState({level: 'day'}) // th ...

Using pre-built modules in an environment that does not support Node.js

Despite the limitations on node API usage (such as fs, http, net...), vanilla JS can still be used on any engine. While simple functionalities can be easily extracted from packaged modules if licensing terms are met, things get complicated when dealing wit ...

Setting default parameters for TypeScript generics

Let's say I define a function like this: const myFunc = <T, > (data: T) => { return data?.map((d) => ({name: d.name}) } The TypeScript compiler throws an error saying: Property 'name' does not exist on type 'T', whic ...

What is the process for enabling the experimental-modules option when running an npm package's bin command?

Beginning with Node v8.5.0, the support for ES6 style modules has been introduced. import x from 'x' You can access this feature by running node using the --experimental-modules option, like so: node --experimental-modules test.mjs By utilizi ...

Sending a file to the jqGrid handler

Currently, I am using Grails in combination with jqGrid and attempting to implement a rather unique feature. My goal is to allow users to upload a file which will then be sent to the jqGrid controller and used as a filter for the data displayed on the grid ...

vue implementing autoscroll for long lists

I am looking to implement an autoscrolling log feature on my webpage that is dynamically fetched from a REST endpoint. To handle the potentially large size of this log, I decided to use vue-virtual-scroll-list. Additionally, I wanted the log to automatical ...

Organize all ngDialog instances within a factory and access them through a directive

In order to keep the ngDialogs centralized in one place instead of dispersed, I had the idea of creating a factory named modal.js. This factory would contain a list of all the modals with a switch statement. Here is an example: .factory(&ap ...