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

Locating the nearest image within a specific div using jQuery's find method

Here is the HTML I used on my page: <div class="edge-img-sec"> <img src="img1" alt="edge-icon" class="edge-icon1"> <img src="img2" alt="right-icon" class="edge-icon1-right"> </div> <div class="edge-img-sec"> < ...

Generate HTML on the fly using Node variables

Utilizing Node.js with Express as the server, I have implemented a feature where users can upload .CSV files containing data. This data is then parsed and stored in a main array made up of arrays, with each line representing one array element. Currently, I ...

Convert and transfer CSS image data encoded in Base-64 to Asp.Net MVC/WebApi

Regarding the query. I successfully displayed a base-64 encoded image on the client side. .even { background: #fff url(data:image/gif;base64,R0lGODlhBgASALMAAOfn5+rq6uvr6+zs7O7u7vHx8fPz8/b29vj4+P39/f///wAAAAAAAAAAAAAAAAAAACwAAAAABgASAAAIMAAVCBxIsKDBgwgTDk ...

CSS - Ensuring that the height of one div extends all the way down to meet another div

I'm facing an issue with two divs in my design. One of them contains a checkbox and the other has text which can vary in size - sometimes it's small, sometimes it's large. When the text in the second div is big, the box on the left extends a ...

"Handling Errors in JavaScript when a Button Click Event Triggers a

There are 2 buttons intended for "Add to Favorites" and "Remove from Other Favorites". Here is the code snippet: <button type="submit" class="btn btn-warning btn-xs" id="FavoriButonex" data-id="<?php echo $sorid ?>"> <i class="fa fa-hea ...

Is it possible to execute a function within an HTML page simply by clicking on it?

As someone who is new to the world of HTML, CSS, and JS, I'm currently facing a challenge: On my website's page1.html, I have implemented a feature that allows users to sort different articles on the page by selecting a sub-menu from the navigat ...

Bringing in two JavaScript files with identical names

Recently, I encountered a challenging situation. I am working with material-ui and nextjs, both of which have a module called Link that is essential for my project. import { Link } from '@material-ui/core'; However, this setup is causing compil ...

Retrieve the keys stored within a complex array of objects

I am searching for a function that can transform my data, specifically an array of objects with nested objects. The function should only include keys with immediate string/number/boolean values and exclude keys with object/array values. For example: [ { ...

Is there a way to showcase a single HTML canvas across multiple div elements?

I'm currently developing a web application that includes a Google Maps feature in two different tabs using Twitter Bootstrap. I am exploring options to have one canvas element that can be displayed in both tabs with varying dimensions. One idea is to ...

Having trouble with installing create-react-app for my_app using npm because it seems to be stuck on f

I've hit a roadblock while trying to create a react app on my 2011 MacBook Pro. To start, I downloaded the latest version of Node from their official website. Following the instructions from React documentation, I ran the following commands: npm uni ...

Locate the index position of an element in one array based on a corresponding element in a

I am seeking a way to determine the index and group that an item belongs to within a parent json group. Is there a method for achieving this? I am willing to modify the json format if necessary. I made an attempt using JSON.stringify(), but it seems to be ...

Issue with the initial element in CSS and the cause remains a mystery

"first of type" dont like to work: .elements { width:70%; margin:0 auto; display:flex; flex-direction: column; .addElement { width:280px; text-align: center; border:1px solid black; font-family: ...

Design the button element with input text using CSS styling

https://i.sstatic.net/3vdRV.png Is there a way to stylize input text and buttons similar to this using CSS or Vuetify JS? ...

retrieving information from a secondary window

Currently, I am working on a project and have come across an issue. Please take a look at the following code snippet: <iframe name="stus" id="stus" style="display:none;"></iframe> <form name="water" id="water" method="post" autocomplete="of ...

What is the purpose of including window and undefined as parameters in this jQuery plugin?

Exploring the jquery resize plugin has left me puzzled about its inner workings: Typically, we only pass a jQuery object into jquery plugins like this: (function($){ ....plugin code.... })(jQuery); However, in the "resize" plugin, both window and un ...

Deleting specialized object using useEffect hook

There's a simple vanilla JS component that should be triggered when an element is added to the DOM (componentDidMount) and destroyed when removed. Here's an example of such a component: class TestComponent { interval?: number; constructor() ...

Ways to show buttons as inline or block elements when a single button is enclosed within a form

Need help figuring out how to make two buttons display as block elements on small screens and inline elements on larger screens, especially when one button is wrapped inside a form? I am struggling with displaying two buttons on my page. One of the button ...

Design a blurred glass effect background with a circular spotlight focusing on a specific section of an HTML webpage

I've been attempting to achieve a frosted glass effect on a background, with a circular section unblurred. Unfortunately, my current implementation in the sandbox editor is not working as expected - the area of the circle remains blurred. If you&apos ...

Enhancing the appearance of individual cells in jQuery DataTables

While working with jQuery DataTables, I have successfully managed to access row data and apply styling to the entire row. Below is the code snippet used to initialize the datatable: var $dataTable = $('#example1').DataTable({ "data": data, ...

How to make an entire video clickable on Android for seamless playback?

I have implemented an HTML5 video in my mobile web application. Currently, users need to click the small play icon at the bottom left of the video to start playing it. Is there a way to make the entire video clickable so it plays when clicked anywhere on t ...