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>