Tips for effectively downsizing an HTML Canvas to achieve high-quality images

Introduction

I am currently facing issues with blurry visuals in my canvas animation, especially on devices with high pixel densities like mobile and retina screens.

In order to address this problem, I have researched canvas down-scaling techniques and followed a tutorial from this source.

Integrating Down-Scaling in the Project

The project where I intend to apply down-scaling features has some key components:

  • There is a main canvas for performance optimization.
  • Multiple smaller pre-rendered canvasses are used to load images efficiently.
  • The canvas includes animations (although not visible in the code snippet).

Question

My goal is simple: to eliminate blurriness in images displayed on high-density screens such as iPhones. Despite implementing downsampling techniques suggested by the tutorial, the images still appear enlarged and unclear. How can I correct this issue?

Explanation of the Code Snippet

The variable devicePixelRatio simulates high-dpi phone screens with a value of 2, while low-dpi screens have a ratio of 1. The function spawn generates multiple pre-rendered canvasses for the project.

If there are any missing details or questions related to this post, feel free to reach out. Thank you!

Code Snippet

var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d' );
var circles = [];

//Simulate Retina screen = 2, Normal screen = 1
let devicePixelRatio = 2

function mainCanvasPixelRatio() {
    // Resize canvas based on current size
    let rect = canvas.getBoundingClientRect();
    canvas.width = rect.width * devicePixelRatio;
    canvas.height = rect.height * devicePixelRatio;
    c.scale(devicePixelRatio, devicePixelRatio);
    canvas.style.width = rect.width + 'px';
    canvas.style.height = rect.height + 'px';
}

// Initial Spawn
function spawn() {
    for (let i = 0; i < 2; i++) {
        let radius = parseInt(i*30);
        let x = Math.round((canvas.width/devicePixelRatio) / 2);
        let y = Math.round((canvas.height /devicePixelRatio) / 2);

        let PreRenderCanvas = document.createElement('canvas');
        const tmp = PreRenderCanvas.getContext("2d");
        
        let PreRenderCanvasWidth = ((radius*2)*1.5)+1;
        let PreRenderCanvasHeight =  ((radius*2)*1.5)+1;

        PreRenderCanvas.width = PreRenderCanvasWidth * devicePixelRatio;
        PreRenderCanvas.height = PreRenderCanvasHeight * devicePixelRatio;
        
        PreRenderCanvas.style.width = PreRenderCanvasWidth + 'px';
        PreRenderCanvas.style.height = PreRenderCanvasHeight + 'px';

        tmp.scale(devicePixelRatio, devicePixelRatio);

        const image=  new Image();

        let m_canvasCenterX = (PreRenderCanvas.width/devicePixelRatio) * .5;
        let m_canvasCenterY =  (PreRenderCanvas.height/devicePixelRatio) * .5;

        tmp.strokeStyle = "red";
        tmp.beginPath();
        tmp.arc((m_canvasCenterX), (m_canvasCenterY), ((PreRenderCanvas.width/devicePixelRatio)/3) , 0, 2 * Math.PI);
        tmp.lineWidth = 2;
        tmp.stroke();
        tmp.restore();
        tmp.closePath()

        image.src=  "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3"

        let paddingX = (PreRenderCanvas.width/devicePixelRatio)/5;
        let paddingY = (PreRenderCanvas.height/devicePixelRatio)/5;

        image.onload = function () {
            tmp.beginPath()
            tmp.drawImage(image, paddingX,paddingY, (PreRenderCanvas.width/devicePixelRatio)-(paddingX*2),(PreRenderCanvas.height/devicePixelRatio)-(paddingY*2));
            tmp.closePath()
        }

        let circle = new Circle(x, y, c ,PreRenderCanvas);

        circles.push(circle)
    }
}


function Circle(x, y, c ,m_canvas) {
    this.x = x;
    this.y = y;
    this.c = c;
    this.m_canvas = m_canvas;
}

Circle.prototype = {
    draw: function () {
        this.c.drawImage( this.m_canvas, (this.x - (this.m_canvas.width)/2), (this.y - this.m_canvas.height/2));
    }
};

function animate() {
    c.clearRect(0, 0, (canvas.width /devicePixelRatio), (canvas.height /devicePixelRatio));

    circles.slice().reverse().forEach(function( circle ) {
        circle.draw();
    });

    requestAnimationFrame(animate);
}

mainCanvasPixelRatio()
spawn()
animate()
#mainCanvas {
     background:blue;
}
<canvas id="mainCanvas"></canvas>

<br>

Answer №1

<!DOCTYPE html>
<html>
<body>

<p>Selected Image:</p>
<img id="scream" width="220" height="277"

src="pic_the_scream.jpg" alt="The Scream">

<p>Canvas to Display:</p>

<canvas id="myCanvas" width="240" height="297"

style="border:1px solid #d3d3d3;">

</canvas>

<script>
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");

ctx.drawImage(img, 10, 10); };

</script>

</body>

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

"Troubleshooting issues with Material Design components in AngularJS: Why is <md-select> not functioning correctly

I'm attempting to implement the <md-select> tag, but I can't seem to achieve the same result as shown here. This is the code I've written: <div layout="column" layout-align="center center" style="margin: 0px 10px 0px 5px;"> & ...

Is it possible to merge a variable within single quotes in XPath?

Currently working with nodeJS and experimenting with the following code snippet: for (let i = 1; i <= elSize; i++) { try { let DeviceName = await driver .findElement(By.xpath("//span[@class='a-size-medium a-color-base a-text-normal ...

Vue parent component not receiving events properly

Referring to these sources: Forum Post Stack Overflow Question In my project, I am utilizing: CodeSandbox Example The setup involves the parent component listening for events emitted by a child component: mounted() { this.$on("edit-category& ...

What is the best way to vertically flip a background image that is repeating along the y axis in CSS?

I am in the process of developing a mobile web app and I need assistance with flipping the background image when it repeats along the y-axis. Can someone guide me on how to achieve this using CSS or JavaScript? https://i.stack.imgur.com/b107V.jpg var el ...

Is it possible to integrate external search functionality with Vuetify's data table component

I am currently working with the v-data-table component from Vuetify, which comes with a default filter bar in its properties. This table retrieves data from a local JSON file. The issue arises when I have another external component, a search bar created u ...

Failure to Present Outcome on Screen

Seeking assistance! I attempted to create a mini loan eligibility web app using JavaScript, but encountered an issue where the displayed result did not match the expected outcome upon clicking the eligibility button. Here is the HTML and JavaScript Code I ...

If the server goes offline, it will not produce an error message

Running a factory in AngularJS angular.module('app.services',[]) .factory('myFactory', function($http){ return { getData: function(){ return {animal: 'dog'} }, isUser: function() ...

Are there any pre-existing pop-up methods available for AngularJS?

Is there a way to create a simple pop-up window with static text and just one "Ok" button? In Java/Swing, it's possible to achieve this with just one line of code using pre-defined classes. However, when it comes to Angular pop-ups, it seems like the ...

Show or hide the expand/collapse button based on the height of the container

Looking for a way to hide content in a Div if it's taller than 68px and display an expand option? The challenge lies in detecting the height of the responsive Div, especially since character count varies. I attempted using PHP to count characters bu ...

What is the best way to generate an accordion populated with data from a MySQL query?

Everything is in order, I've got all the chapters and video series extracted without any issues: $id_course = 1; $stmt = $con->prepare("SELECT c.chapter, v.preview, v.title_video, ...

Using the filter function in JavaScript to retrieve specific data

When the user_id from two different data sources matches, I need to return the email from the first source. The first source is a table in PostgreSQL called "results" and the second source is JSON data. I attempted this code snippet: var table = db.query ...

Refresh choices for the user interface selection menu

I've successfully mastered the art of redefining options for Webix ui.richselect/ui.combo. The technique involves: richselect.getList().clearAll(); richselect.getList().parse(options_data) Now, I'm facing a challenge with changing options fo ...

Utilizing libraries in JavaScript

For a while now, I've been grappling with an issue related to importing modules into my main JavaScript script. I recently installed the lodash library via npm and I'm trying to import it into my main script so that I can utilize its functionali ...

Edit: "Submit a binary file with just JavaScript"

I am currently developing a Chrome application that utilizes the HTML5 Filesystem API to enable users to import and synchronize files. A challenge I'm facing is when attempting to sync image files, as they appear to become corrupted during the upload ...

Is there a way to activate a function in a sibling component without using Prop Drilling?

Looking at the image, you can see the structure of components I have. <Invoice> <Left></Left> <Right></Right> </Invoice> In the Left component, there is a form, and in the Right component, there is a Submit button l ...

Matter of Representing Nested For Loops in Javascript Arrays

When I have two arrays that intersect at certain elements, the resulting function should ideally output A, B, Y. However, in this case, it displays all possible combinations of lista.length * listb.length. <script> window.onload = function(){ ...

Using a function from one class within another class by passing it as a prop

Below are the methods found in my Search.tsx class. renderSuggestion(suggestion) { <div className="buttons"> <button className="button">View Location</button> <button className="button whitebutton" onClick={this.h ...

"Create a React button component that, when clicked, navig

I'm currently developing a web application using React and bootstrap. I'm facing difficulties in redirecting the page to another one when applying onClick to buttons. After adding a href, I'm unable to navigate to another page. Do you think ...

The Zurb Foundation grid system is causing issues when trying to implement vertical tabs

My vertical tabs are giving me trouble when I try to nest grid or block elements, causing everything to overflow outside the element. For example, in the image below, the numbers 1,2,3 should be in the right section. <div class="row"> <div class ...

Display a JavaScript dialogue box containing a PHP variable

Is there a way to display the correct JavaScript box when using a While loop in PHP? Here is the code snippet: while($result= mysql_fetch_array($data)){ <tr class="<?php echo $style;?>"> <td><?php echo $result['commis ...