0

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.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
Andrew Fan
  • 1,313
  • 5
  • 17
  • 29
  • Hy, Do you know about fabric js? It is javascript library to deal with the canvas, There are some examples, you can check here: http://fabricjs.com/fabric-intro-part-5 – HarisH Sharma Nov 12 '19 at 06:13
  • Please post a [mcve] of what tou are currently doing. Your explanations are too broad for us to give you proper advice. – Kaiido Nov 12 '19 at 09:28
  • My minimal reproducible example for the 'problem' is the default behavior of a HTML canvas in this case. I can provide more in-depth code for some of the attempts to rectify the issue but that code is based on the linked SO answer. – Andrew Fan Nov 12 '19 at 14:10
  • I mean a reproducible example of what you are doing. You say you are drawing "an existing canvas", does this mean you use ctx2.drawImage(canvas1,...)? What is it you call "the physical size"? The real size on whatever support your user may render the page? (think beam projector). What are you really doing? We can read your explanations in many different ways. So please post a minimal example where we will be able to experience the same issue. You could even post a screenshot **along with the code** which could help identify some known issues (high def monitor etc.) – Kaiido Nov 12 '19 at 14:22
  • You say you are drawing animated shapes, maybe you simply forgot to clear the canvas at every frames? Many possibilities really. We need to see a [mcve] to be able to help you. – Kaiido Nov 12 '19 at 14:26
  • I've added the reprex code as well as a link to GitHub. Bottom of readme has links to live examples. I clear the window every frame by default. – Andrew Fan Nov 13 '19 at 00:38

0 Answers0