What is the best way to resize an image within a canvas?

As I work on displaying an image using the cover simulation in canvas, I came across a helpful solution.

Currently, the image changes based on screen resolution, but only updates after a page refresh.

https://i.sstatic.net/qEL26.gif

Is there a way to achieve the desired scaling effect without needing to refresh the page? Feel free to test resizing the window.

HTML

<canvas id="canvas"></canvas> 

JS

var ctx = canvas.getContext('2d'),
    img = new Image;

canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);

img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/0/0f/2010-02-19_3000x2000_chicago_skyline.jpg';

function draw() {
    drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height);
}

/**
 * By Ken Fyrstenberg
 *
 * drawImageProp(context, image [, x, y, width, height [,offsetX, offsetY]])
 *
 * If image and context are only arguments rectangle will equal canvas
*/
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {

    if (arguments.length === 2) {
        x = y = 0;
        w = ctx.canvas.width;
        h = ctx.canvas.height;
    }

    /// default offset is center
    offsetX = offsetX ? offsetX : 0.5;
    offsetY = offsetY ? offsetY : 0.5;

    /// keep bounds [0.0, 1.0]
    if (offsetX < 0) offsetX = 0;
    if (offsetY < 0) offsetY = 0;
    if (offsetX > 1) offsetX = 1;
    if (offsetY > 1) offsetY = 1;

    var iw = img.width,
        ih = img.height,
        r = Math.min(w / iw, h / ih),
        nw = iw * r,
        nh = ih * r,
        cx, cy, cw, ch, ar = 1;

    if (nw < w) ar = w / nw;
    if (nh < h) ar = h / nh;
    console.log(ar)
    nw *= ar;
    nh *= ar;

    cw = iw / (nw / w);
    ch = ih / (nh / h);

    cx = (iw - cw) * offsetX;
    cy = (ih - ch) * offsetY;

    if (cx < 0) cx = 0;
    if (cy < 0) cy = 0;
    if (cw > iw) cw = iw;
    if (ch > ih) ch = ih;

    ctx.drawImage(img, cx, cy, cw, ch,  x, y, w, h);
}

Answer №1

Make sure to include a reference to draw when the window is resized:

window.onresize = draw;

By following this step, the functionality should be in place.

Rather than:

drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height);

Insert img within the draw function:

drawImageProp(ctx, img, 0, 0, canvas.width, canvas.height);

Next, move the width and height settings into the draw function, like so:

function draw() {
    canvas.setAttribute('width', window.innerWidth);
    canvas.setAttribute('height', window.innerHeight);
    drawImageProp(ctx, img, 0, 0, canvas.width, canvas.height);
}

You might also consider adding a debounce for re-drawing purposes:

var timeOut;
window.onresize = function(){
    if(timeOut)
        clearTimeout(timeOut);
    timeOut = setTimeout(draw, 10);
}

This method helps prevent multiple calls to the draw function when resizing the window frequently.

For a working demonstration, check out this example, modified from your codepen.

Answer №2

Success! Check it out!

http://codepen.io/anon/pen/KdRwVY

var context = canvas.getContext('2d'),
  image = new Image;

canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);

image.onload = draw;
image.src = 'https://upload.wikimedia.org/wikipedia/commons/0/0f/2010-02-19_3000x2000_chicago_skyline.jpg';

function draw() {
  drawImageProportionally(context, image, 0, 0, canvas.width, canvas.height);
}
window.onresize = resizeCanvas;

function resizeCanvas() {
  canvas.setAttribute('width', window.innerWidth);
  canvas.setAttribute('height', window.innerHeight);
  draw()
}

/**
 * By Ken Fyrstenberg
 *
 * drawImageProp(context, image [, x, y, width, height [,offsetX, offsetY]])
 *
 * If image and context are only arguments rectangle will equal canvas
 */
function drawImageProportionally(context, image, x, y, width, height, offsetX, offsetY) {

  if (arguments.length === 2) {
    x = y = 0;
    width = context.canvas.width;
    height = context.canvas.height;
  }

  /// default offset is center
  offsetX = offsetX ? offsetX : 0.5;
  offsetY = offsetY ? offsetY : 0.5;

  /// keep bounds [0.0, 1.0]
  if (offsetX < 0) offsetX = 0;
  if (offsetY < 0) offsetY = 0;
  if (offsetX > 1) offsetX = 1;
  if (offsetY > 1) offsetY = 1;

  var imgWidth = image.width,
    imgHeight = image.height,
    ratio = Math.min(width / imgWidth, height / imgHeight),
    newWidth = imgWidth * ratio, /// new prop. width
    newHeight = imgHeight * ratio, /// new prop. height
    centerX, centerY, cropWidth, cropHeight, aspectRatio = 1;

  /// decide which gap to fill    
  if (newWidth < width) aspectRatio = width / newWidth;
  if (newHeight < height) aspectRatio = height / newHeight;
  newWidth *= aspectRatio;
  newHeight *= aspectRatio;

  /// calculate source rectangle
  cropWidth = imgWidth / (newWidth / width);
  cropHeight = imgHeight / (newHeight / height);

  centerX = (imgWidth - cropWidth) * offsetX;
  centerY = (imgHeight - cropHeight) * offsetY;

  /// make sure source rectangle is valid
  if (centerX < 0) centerX = 0;
  if (centerY < 0) centerY = 0;
  if (cropWidth > imgWidth) cropWidth = imgWidth;
  if (cropHeight > imgHeight) cropHeight = imgHeight;

  /// fill image in dest. rectangle
  context.drawImage(image, centerX, centerY, cropWidth, cropHeight, x, y, width, height);
}
<canvas id="canvas"></canvas>

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

How can I get rid of the table borders and set colors for every other row in the

How can I remove the border lines from a table and assign colors to alternate rows in the table? Follow this link for the code: https://stackblitz.com/angular/kooxxyvddeqb?file=app%2Ftable-sticky-columns-example.css Thank you in advance ...

Trouble with displaying list bullets in jQTouch framework

I'm currently experimenting with jQTouch for a web app designed for iPhones. However, I am facing an issue where I want the content on the pages to display normal bullet lists instead of being styled as bars in the jqt theme. To achieve this, I am att ...

What language should be used for JSON data formats?

I am dealing with a JSON file named myjson.cfg that has the following structure: { "values": { "a": 1, "b": 2, "c": 3, "d": 4 }, "sales": [ { "a": 0, "b": 0, "c": 0, "d": 0, ...

Instructions for downloading a .zip file sent through a HTTP response (using an axios PUT request)

When the API responds, it should contain a data property with the .zip file I need. However, the format is unfamiliar to me. The format: https://i.sstatic.net/ruaVR.png I attempted to use .blob() as suggested in similar questions on Stackoverflow, but it ...

Obtaining only a portion of the text when copying and editing it

I have a React application where I am attempting to copy text from an HTML element, modify it, and then send it back to the user. I have been successful in achieving this, but I am facing an issue where even if I select only a portion of the text, I still ...

The canvas element's drawImage() method fails to render the specified video thumbnail

I'm having trouble creating a function that displays a specific image from a video at a certain time frame. The function takes two inputs: the path to the video and the specific second where the image should be displayed. <!DOCTYPE html> <htm ...

Retrieve error message from 400 error in AngularJS and WebAPI

Why am I having trouble reading the error message in AngularJS from this code snippet? ModelState.AddModelError("field", "error"); return BadRequest(ModelState); Alternatively, return BadRequest("error message"); return Content(System.Net.HttpStatusCod ...

Error in Angular form validation: Attempting to access property 'name' of an undefined value

Recently, I encountered an issue with my form while implementing Angular validation. The goal was to ensure that the input fields were not left blank by using an if statement. However, upon testing the form, I received the following error message: Cannot ...

Listening for events to wait for all XMLHttpRequest requests within an iframe

I have a basic HTML page that includes an iframe element <html> <head> $('#page-wrapper').ajaxStop(function() { console.log("Page Fully Loaded." ); }); </head> <body> <h3>Demo Page<h3> <iframe id= ...

Is there a way to instruct Google to include my site in its index using Angular.js?

I am currently working on an angular.js app and have been following Google's guide for ajax-based applications. Here are the steps I have taken: Added meta tags <base href="/"> <meta name="fragment" content="!"> Configured angular.js ...

Error in JQuery Document Ready AJAX function causing image to not load initially without a page refresh

Currently, I have a piece of customized code that extracts data from an XML file and uses this data to populate specific content on my webpage: $(document).ready(function() { $.ajax({ type: 'GET', url: 'config.xml?date=& ...

Converting a JSON PHP array into Javascript

How can I convert this PHP array named $data into JSON using json_encode? Whenever I try to do so in JavaScript by writing... var myJson = <?php echo json_encode($data) ?>; console.log(myJson); I encounter errors. I am curious about any restrictio ...

Struggling to make Datatables function properly with basic JSON data

As a newcomer to frontend development, I'm attempting to convert a JSON call into a table format. After researching, it seems like datatables is the best tool for this task. However, I'm struggling to make it work. Here is my JSON GET call: GET ...

Show the details of a location directly on Google Maps API marker

I am currently developing a project using AngularJs, where I am displaying information on a Google Maps interface. You can check out my example on this plunker link. My goal is to retrieve the 'city' and 'desc' fields by clicking on th ...

Ways to retrieve specific information using an ID from a JSON file with Angular.js

Can someone assist me with fetching data from my data.json file using the unique ID? I have provided an overview of my code below. $scope.editProfileData=function(){ var editid={'id':2}; $http({ method: 'POST&ap ...

Troubles encountered when creating select/drop-down optgroup using jquery

Looking to incorporate the <optgroup> tag into a dropdown with JQuery. The option works as expected, but struggling with getting the option group to function correctly. Here's the HTML setup: <select name="DropDownID" id="DropDownID"> ...

Retrieving Results from Sequenced Promises

I am facing an issue with returning data from a chained promise. In the following scenario, how can this be achieved? Here is some pseudocode: mark = function(){ return promiseA .then(function(data){ .....}) .then(function(data){return ne ...

Disabling the onClick event on HTML tags with personalized WooCommerce messages

I have customized the notices/success.php template file by modifying the content as shown below: <?php foreach ( $messages as $message ) : ?> <div class="woocommerce-message" role="alert"> <span> <?php ...

Tips for optimizing search functionality in Angular to prevent loading all data at once

An exploration for information within vast datasets is triggered by AngularJS when the input contains more than 3 characters. var app = angular.module('test_table', []); app.controller('main_control',function($scope, $http){ $scope ...

In Node.js, I encountered an issue where req.body was returning as undefined, despite the fact that when I logged req, I could

I am having trouble logging the req.body to the console in my Twilio text app. When I try console.log(req), the body and its contents show up, but it says that the body is undefined when I try to log it on its own. Any help would be greatly appreciated. ...