Introduction
I'm trying to deal with blurry visuals on my canvas animation. The blurriness is especially prevalent on mobile-devices, retina and high-dpi (dots-per-inch) screens.
I'm looking for a way to ensure the pixels that are drawn using the canvas look their best on low-dpi screens and high-dpi screens. As a solution to this problem I red multiple articles about canvas-down-scaling and followed this tutorial:
https://www.kirupa.com/canvas/canvas_high_dpi_retina.htm
Integrating down-scaling in the project
The project in which I want to implement down-scaling can be found below and consists of a few important features:
- There is a (big) main canvas. (Performance optimization)
- There are multiple (pre-rendered) smaller canvasses that are used to draw and load a image into. (Performance optimization)
- The canvas is animated. (In the code snippet, there is no visible animation but the animation function is intergrated.)
Question
I tried to implement this method on both the mainCanvas and the smaller pre-renderd canvasses. As shown in the result of the snippet it is not implemented correctly, the output is still blurry. What did I do wrong and how am I able to fix this issue?
Explanation of the code snippet
The variable devicePixelRatio is set to 2 to simulate a high-dpi phone screen, low-dpi screens have a devicePixelRatio of 1. Multiple pre-rendered canvasses generated is the function spawn is the snippet there are 5 different canvasses, but on the production environment there are 10's.
If there are any pieces of information missing or questions about this post, please let me know. Thanks a lot!
Edit: Added a reference canvas so the blur is more visible, the blur is slightly there but still visible
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() {
// get current size of the canvas
let rect = canvas.getBoundingClientRect();
// increase the actual size of our canvas
canvas.width = rect.width * devicePixelRatio;
canvas.height = rect.height * devicePixelRatio;
// ensure all drawing operations are scaled
c.scale(devicePixelRatio, devicePixelRatio);
// scale everything down using CSS
canvas.style.width = rect.width + 'px';
canvas.style.height = rect.height + 'px';
}
// Initial Spawn
function spawn() {
for (let i = 0; i < 2; i++) {
//Set Radius
let radius = parseInt(i*30);
//Give position
let x = Math.round((canvas.width/devicePixelRatio) / 2);
let y = Math.round((canvas.height /devicePixelRatio) / 2);
//Begin Prerender canvas
let PreRenderCanvas = document.createElement('canvas');
const tmp = PreRenderCanvas.getContext("2d");
//Set PreRenderCanvas width and height
let PreRenderCanvasWidth = ((radius*2)*1.5)+1;
let PreRenderCanvasHeight = ((radius*2)*1.5)+1;
//Increase the actual size of PreRenderCanvas
PreRenderCanvas.width = PreRenderCanvasWidth * devicePixelRatio;
PreRenderCanvas.height = PreRenderCanvasHeight * devicePixelRatio;
//Scale PreRenderCanvas down using CSS
PreRenderCanvas.style.width = PreRenderCanvasWidth + 'px';
PreRenderCanvas.style.height = PreRenderCanvasHeight + 'px';
//Ensure PreRenderCanvas drawing operations are scaled
tmp.scale(devicePixelRatio, devicePixelRatio);
//Init image
const image= new Image();
//Get center of PreRenderCanvas
let m_canvasCenterX = (PreRenderCanvas.width/devicePixelRatio) * .5;
let m_canvasCenterY = (PreRenderCanvas.height/devicePixelRatio) * .5;
//Draw red circle on PreRenderCanvas
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()
//Set Image
image .src= "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3"
//Get padding
let paddingX = (PreRenderCanvas.width/devicePixelRatio)/5;
let paddingY = (PreRenderCanvas.height/devicePixelRatio)/5;
//Load image
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)
}
}
// Circle parameters
function Circle(x, y, c ,m_canvas) {
this.x = x;
this.y = y;
this.c = c;
this.m_canvas = m_canvas;
}
//Draw circle on canvas
Circle.prototype = {
//Draw circle on canvas
draw: function () {
this.c.drawImage( this.m_canvas, (this.x - (this.m_canvas.width)/2), (this.y - this.m_canvas.height/2));
}
};
// Animate
function animate() {
//Clear canvas each time
c.clearRect(0, 0, (canvas.width /devicePixelRatio), (canvas.height /devicePixelRatio));
//Draw in reverse for info overlap
circles.slice().reverse().forEach(function( circle ) {
circle.draw();
});
requestAnimationFrame(animate);
}
mainCanvasPixelRatio()
spawn()
animate()
#mainCanvas {
background:blue;
}
#ReferenceCanvas {
background:blue;
}
Down-Sampling/Blurry:
<canvas id="mainCanvas"></canvas>
<br>
Reference Not-Blurry:
<canvas id="ReferenceCanvas"></canvas>
<script>
window.onload = function() {
var myCanvas = document.getElementById('ReferenceCanvas');
var ctx = myCanvas.getContext('2d');
var img = new Image;
img.onload = function(){
ctx.drawImage(img, 95,20, 110,110);
};
img.src = "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3";
};
</script>from How to properly Down-scale HTML Canvas for good looking images?
No comments:
Post a Comment