I'm looking for a way to render an existing 2D HTML Canvas in such a way that its contents are not blurry. However, as an additional constraint, I want to maintain the physical size of the canvas on the screen and (preferably) also maintain the draw coordinates on the screen. The canvas is a fixed size.
For example, if I have a 400x400 canvas with a circle with radius 4 drawn at the center, I want to upscale the drawn components without the positions shifting (IE the circle will still render in the same place it would have before, but won't be as blurry).
I am currently defining the dimensions of the canvas in the HTML using width
and height
(not CSS), which is something I would also like to keep. Setting the CSS to the HTML value via JS seems to not make a difference.
Note that I am not simply drawing lines - I have text (which looks horrible), as well as moving shapes. I also have no actual images - I simply want the canvas elements to look like they were proper vector graphics instead of the blurry mess they currently are.
Things I have tried (and why they don't work for me):
- LittleJoe's answer on Canvas drawings, like lines, are blurry - Final canvas size is changed, cutting off 3/4 of the canvas. Note that none of the answers on this SO post actually solve my problem.
- context.translate(0.5, 0.5);
- Does not solve problem at all as I am not trying to draw only lines
I am currently using Safari as my browser. However, the problem exists across browsers.
Reprex Below (only shows blurry text, which is the most obviously blurry component since it is directly adjacent to regular non-blurry non-Canvas text). Please excuse the act of shoving the JS into the HTML file - this is only for demonstration.
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Example - Graphics</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<script>
function createNew(canvasid, title) {
let newgame = new NewGame(canvasid, title);
newgame.startGame(canvasid);
}
function NewGame(canvasid, title) {
this.text = []; //array containing all text objects
this.canvas = document.getElementById(canvasid);
this.context = this.canvas.getContext("2d");
this.startGame = function (canvasid) {
this.setupInterval(20);
CreateText(4, 12, "#FFFFFF", 12, "Arial", "left", "Text (Blurry)", this);
CreateText(4, 24, "#FFFFFF", 12, "Arial", "left", title, this);
};
this.setupInterval = function(updatefreq) {
setInterval(this.update_main, updatefreq, this);
};
this.clearCanvas = function () {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
};
this.update_main = function (currgame) {
currgame.clearCanvas();
currgame.draw_main(canvasid);
};
//Main draw loop. Handles render order.
this.draw_main = function (canvasid) {
let i;
for (i = 0; i < this.text.length; i += 1) {
this.text[i].draw();
}
};
}
function DMKText(x, y, fillStyle, fontsize, font, textAlign, content, currgame) {
this.x = x;
this.y = y;
this.fillStyle = fillStyle;
this.fontsize = fontsize;
this.font = font;
this.textAlign = textAlign;
this.content = content;
this.createtime = currgame.frameNo;
this.existtime = 0;
this.update = function () {
};
this.draw = function () {
let ctx = currgame.context; //game window
ctx.fillStyle = this.fillStyle;
ctx.font = "" + this.fontsize + "px " + this.font;
ctx.textAlign = this.textAlign;
ctx.fillText(this.content, this.x, this.y);
};
return this;
}
function CreateText(x, y, fillStyle, fontsize, font, textAlign, content, currgame) {
let newtext = new DMKText(x, y, fillStyle, fontsize, font, textAlign, content, currgame);
currgame.text.push(newtext);
return newtext;
}
</script>
<body>
<style>
canvas { background-color: black; }
</style>
<div id="canvasdiv">
<canvas id="canvas_1" width="320" height="160" style="border:1px solid #DDDDDD;"></canvas>
</div>
<br>
<button type="button" onclick="createNew('canvas_1', 'Example')">Run Simulation</button>
</body>
</html>
Full source code for the project I'm working on located at GitHub. Bottom of README has links to live examples.