blurred text when a canvas is placed on top of another canvas - javascript

I want to display the mouse's x position on canvas as mouse moves. Can someone please tell why the text is so big and blurry and also is there any way to achieve the transparency between these two canvases
<body style="margin: 0; padding: 0; height: 100%; width: 100%; overflow: hidden;">
<canvas id="myCanvas" style="display: block; height: 100%; width: 100%;">
Your browser does not support the HTML5 canvas tag.</canvas> <canvas id="temp">
Your browser does not support the HTML5 canvas tag.</canvas>
<script type="text/javascript">
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var hgap = 0;
var vgap = 0;
var rows, cols;
var annotation_x = 1;
var row = 0; var col = 0;
//ctx.font = "14px Arial";
var t = document.getElementById("temp");
tctx = t.getContext("2d");
// tctx.lineWidth = 10;
tctx.lineJoin = 'round';
tctx.lineCap = 'round';
tctx.strokStyle = 'red';
var mouse = { x: 0, y: 0 };
c.addEventListener('mousemove', function (evt) {
// tctx.clearRect(0, 0, c.width, c.height);
ctx.clearRect(0, 0, c.width, c.height);
mouse.x = evt.pageX;
mouse.y = evt.pageY;
ctx.fillText(mouse.x, 10, 10);
}, false);
</script>
</body>

The problem isn't due to having two canvas.
The problem is that you scale your canvas using CSS, but you don't modify its number of pixels (height and width attributes). Then, the result is blurry.
If you don't scale it with CSS, the result is fine: Demo
Alternatively, you can try using image-rendering CSS property (e.g. crisp-edges, demo)
You can also modify height and width attributes when the browser is resized (resize event), and redraw the canvas.

<canvas id="myCanvas" style="display: block; height: 100%; width: 100%;">
^ You shouldn't scale a canvas element with CSS unless you want it's contents to be interpolated. Consider a 300x125px image, which you've scaled to the width and height of the browser window - the image will become blurry.
If you wish to have a full-screen canvas you need to scale it with JS:
var c = document.getElementById("myCanvas");
c.width = window.innerWidth;
c.height = window.innerHeight;
..or with HTML:
<canvas width="1920" height="1080"></canvas>

Related

Make image horizontally and vertically centered over canvas drawing with HTML, CSS, and JS

Sorry if the title is vague, it was hard to figure out what to call this problem. I have a canvas and an image overlaying each other like this:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function newLine(){
let value = (Math.floor(Math.random() * 100) + 1) * 0.06283185307179587;
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = 10;
ctx.strokeStyle = '#00FF00';
ctx.beginPath();
ctx.arc(100, 75, 55, 0, value);
ctx.stroke();
}
setInterval(()=>{
newLine()
}, 100)
img{
width: 100px;
border-radius: 50px;
position: absolute;
left: 58px;
top: 33px;
}
<img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>
<canvas id="myCanvas"></canvas>
This works, but when I go to add multiple, or add a div above them, because of the image's absolute position, the image ends up over the div, not the canvas. Here's an example illustrating my issues:
var can = document.getElementsByClassName("canvas");
for (var i = 0; i < can.length; i++) {
let c = can.item(i)
var ctx = c.getContext("2d");
function newLine(){
let value = (Math.floor(Math.random() * 100) + 1) * 0.06283185307179587;
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = 10;
ctx.strokeStyle = '#00FF00';
ctx.beginPath();
ctx.arc(100, 75, 55, 0, value);
ctx.stroke();
requestAnimationFrame(newLine)
}
newLine()
}
img{
width: 100px;
border-radius: 50px;
position: absolute;
left: 58px;
top: 33px;
}
div{
background-color: red;
}
canvas{
display: inline-block;
}
<div class="top">
<h1>Some text</h1>
</div>
<img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>
<canvas class="canvas"></canvas>
<img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>
<canvas class="canvas"></canvas>
(If you could figure out why the first one doesn't play, that would be amazing also)
The issues are:
The first canvas doesn't play
The images (because it's position is absolute) are stacked up and not over the second canvas
Is it possible to do this but with relative positioning? Please let me know if this was a confusing question. Thanks
Avoid mixing images <img> and canvas, instead just draw the image inside the canvas with drawImage nothing special just a few more lines on JavaScript
Here is an example:
(And I also fixed your issue where the first one does not play)
class Shape {
constructor(ctx, width, height, image) {
this.ctx = ctx;
this.width = width;
this.height = height;
this.image = image;
this.value = Math.random() * 5
this.inc = 0.03
}
draw() {
this.value += this.inc;
if (this.value > 2 * Math.PI || this.value < 0.05) this.inc *= -1;
this.ctx.clearRect(0, 0, this.width, this.height);
// Draw centered round image
this.ctx.beginPath();
this.ctx.drawImage(this.image , 50, 24, 100, 100);
this.ctx.lineWidth = 50;
this.ctx.strokeStyle = 'white';
this.ctx.arc(100, 75, 75, 0, 2 * Math.PI);
this.ctx.stroke();
// Draw green progress bar
this.ctx.beginPath();
this.ctx.lineWidth = 12;
this.ctx.strokeStyle = '#00FF00';
this.ctx.arc(100, 75, 55, 0, this.value);
this.ctx.stroke();
}
}
var can = document.getElementsByClassName("canvas");
var animations = []
function loop() {
animations.forEach(anim => anim.draw());
requestAnimationFrame(loop)
}
var image = new Image();
image.src = "https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s";
image.onload = imageLoaded;
function imageLoaded() {
for (var i = 0; i < can.length; i++) {
let c = can.item(i)
animations.push(new Shape(c.getContext("2d"), c.width, c.height, image));
}
loop()
}
div {
background-color: red;
}
canvas {
display: inline-block;
border: 1px solid gray;
}
<div class="top">
<h2>Some text</h2>
</div>
<canvas class="canvas" width=200></canvas>
<canvas class="canvas" width=200></canvas>
There are two problems: positioning of the images above the canvases and only one canvas playing.
First positioning. The img is given position absolute. To find out where to position it the system goes back up the tree to find the first element it comes across that has a position set. It then positions the img relative to that.
The canvas is at the same level as the img so that doesn't influence the img's position, going back up the first thing it hits with a position is the body element (which is positioned by default) and so it puts the img slightly set off from the top of the page.
To remedy this we can put each pair of img/canvas into their own div and position that inline-block. The img will then be positioned relative to that div and so will be placed over the canvas as required.
Second only one canvas playing. The code within the newline function uses a c and ctx. These are not set within the function so it takes the last one that was set. In addition, requestAnimationFrame with its callback is called for each can.length times each time so you end up with more than one of these. They stack up. The browser gets 'confused' and (at least on my laptop) it ended up not responding.
To cure this, call requestAnimationFrame just once per 'cycle' and repaint all the canvases at the same call to the callback - so put the definition of c and ctx inside the for loop so every canvas is set up on each frame.
var can = document.getElementsByClassName("canvas");
function newLine(){
for (var i = 0; i < can.length; i++) {
let c = can.item(i)
var ctx = c.getContext("2d");
let value = (Math.floor(Math.random() * 100) + 1) * 0.06283185307179587;
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = 10;
ctx.strokeStyle = '#00FF00';
ctx.beginPath();
ctx.arc(100, 75, 55, 0, value);
ctx.stroke();
}
requestAnimationFrame(newLine)
}
newLine()
img{
width: 100px;
border-radius: 50px;
position: absolute;
left: 58px;
top: 33px;
}
div{
background-color: red;
}
canvas{
display: inline-block;
}
.wrapper {
display: inline-block;
position: relative;
}
<div class="top">
<h1>Some text</h1>
</div>
<div class="wrapper">
<!-- <img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>-->
<img src="https://i.stack.imgur.com/ZBclx.jpg" />
<canvas class="canvas"></canvas>
</div>
<div class="wrapper">
<!-- <img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>-->
<img src="https://i.stack.imgur.com/ZBclx.jpg" />
<canvas class="canvas"></canvas>
</div>
Note, the actual logo image didn't show on your snippet - I guess it cannot be downloaded by just anybody - so I put it on imgur. The positioning is slightly off because the top and left positions had been previously set to compensate for its being positioned relative to the body. These need a bit of adjustment.
There is quite a lot of GPU being used (around 20% on my laptop, obviously that will differ depending on the user's equipment). It might be worth investigating whether a CSS animation could be used instead of having to redraw several canvases on each frame.
The animated canvases are very flickery. I don't know whether this is intended or whether it was just a test, but they may be upsetting for those with conditions such as epilepsy.

How to position a button at the bottom of a bounding rectangle in canvas

I'm working on positioning a button on top of my canvas but a little lower than my bounding rectangle that I've drawn on my canvas, I have the variable bboxX, bboxY, bboxWidth, bboxHeight which is the attributes of the bounding rectangle in my canvas. I'm using getBoundingClientRect() to get the offset of the canvas and adding it to the coordinate of the bounding box in the canvas but the button position isn't right. How can I position the button the way I want?
This is what I'm trying to achieve:
const video = <HTMLVideoElement> document.getElementById("vid");
const canvas = <HTMLCanvasElement> document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.drawImage(this.video, 0, 0, canvas.width,canvas.height);
ctx.strokeStyle = "#00FFFF";
ctx.lineWidth = 2;
ctx.strokeRect(bboxX, bboxY, bboxWidth, bboxHeight);
let rect = canvas.getBoundingClientRect();
let positionX = (bboxX + rect.left).toString() + "px";
let positionY = (bboxY + rect.top).toString() + "px";
document.getElementById("button").style.visibility = "visible";
document.getElementById("button").style.top = positionY;
document.getElementById("button").style.left = positionX;
canvas{
position: absolute;
z-index: 10;
width: 100%;
height: 100%;
}
#button{
position: absolute;
z-index: 15;
visibility: hidden;
}
<div>
<button id="button">Show</button>
<video hidden id="vid" width="300" height="300"></video>
<canvas id="canvas"></canvas>
</div>
I would use a relative position...
Not clear why you are using the canvas.getBoundingClientRect in your placement of the button you need to align with the rectangle you draw not the entire canvas.
See code below:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let bboxX = 20
let bboxY = 10
let bboxWidth = 100
let bboxHeight = 60
ctx.lineWidth = 2;
ctx.strokeRect(bboxX, bboxY, bboxWidth, bboxHeight);
let rect = canvas.getBoundingClientRect();
let positionX = (bboxX + bboxWidth/2 -25).toString() + "px";
const button = document.getElementById("button")
button.style.visibility = "visible";
button.style.left = positionX;
#button{
visibility: hidden;
position: relative;
width:50px;
}
<div>
<canvas id="canvas" width="300" height="80"></canvas>
<br>
<button id="button" >Show</button>
</div>
The key in that code is:
let positionX = (bboxX + bboxWidth/2 -25)
that center aligns the button with the rectange.
bboxX + bboxWidth/2 is the center of the rectange,
then we subtract -25 half of the button width.

How to properly choose canvas width?

I am using the following code (in accordance with this answer) to have a responsive canvas:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.font = '52px verdana';
var text = 'Sample text';
var textWidth = ctx.measureText(text).width;
ctx.fillText(text, 50, 50);
canvas {
width: 100%;
height: auto;
}
<div class="container">
<div class="row">
<div class="col-md-12">
<canvas id="canvas" width="800">
Sorry, your browser doesn't support the <canvas> element.
</canvas>
</div>
</div>
</div>
It doesn't look well because the actual canvas element size is not 800, but 1140 (in my browser). Looks like I should detect the window size somehow and assign correct canvas size value. How should I do it? I am using the latest Twitter Bootstrap library.
Dont use css style
ctx.canvas.width = window.innerWidth || document.body.clientWidth;

Bad quality for 100% both width and height of canvas

I have done a very tiny example with canvas, it's available on JsFiddle:
http://jsfiddle.net/yPtr5/
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
#myCanvas {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<canvas id="myCanvas">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var canvas = document.getElementById( "myCanvas" );
var context = canvas.getContext( "2d" );
context.id = "myContext";
context.beginPath();
context.arc( 95, 50, 40, 0, 2 * Math.PI );
context.stroke();
setTimeout( function() {
var rectWidth = 150;
var rectHeight = 75;
context.fillStyle = "blue";
context.fillRect( rectWidth / -2, rectHeight / -2, rectWidth, rectHeight );
}, 2000 );
</script>
</body>
</html>
As you are able to see, the rendering result has a very low quality:
So, I'm wondering, how can I draw various figures using Canvas in a good quality, I don't want to draw in small size, I want to draw in 100% size of page.
So, maybe I didn't define some anti aliasing filter or something else?
Thanks!
Problem
In most general cases we should avoid using CSS to set the canvas size.
The default size of canvas is 300 x 150 pixels (bitmap). If you set the size using CSS we'll just end up scaling those 300 x 150 pixels meaning the browser will start interpolating and smoothing the image, which is why you end up with a blurry result.
Solution
Remove these from the CSS-rule:
#myCanvas {
/*width: 100%;
height: 100%;*/
display: block;
}
and set the size in JavaScript like this:
var canvas = document.getElementById( "myCanvas" );
canvas.width = window.innerWidth; // equals window dimension
canvas.height = window.innerHeight;
You can of course set any other size you need (in pixels). You probably want to define position (i.e. fixed or absolute) for the canvas' CSS as well if your goal is full window size.
Hope this helps.
The height and width need to be set on the height and width attributes of the canvas tag and not in CSS. Any CSS sizing of the canvas element merely stretches the canvas and does not size it properly.
<canvas id="canvas" width="500px" height="500px">
Have a look at this project that I posted on my site: Creating an HTML5 Paint App
It includes a functionto resize the canvas when the browser window size changes (which you would have to modify):
this.onScreenSizeChanged = function (forceResize) {
if (forceResize || (this.canvas.width != window.innerWidth /*||
this.canvas.height != window.innerHeight*/)) {
var image = this.context.getImageData(0, 0,
this.canvas.width, this.canvas.height);
this.canvas.width = (window.innerWidth);
this.canvas.height = (window.innerHeight);
this.context.putImageData(image, 0, 0);
}
}
this.onScreenSizeChanged(true);
In my case, it was a problem with the screen pixel ratio.
To solve it, I created a canvas with a higher pixel ratio as follows:
function createHiPPICanvas(w, h) {
let ratio = window.devicePixelRatio;
let cv = document.createElement("canvas");
cv.width = w * ratio;
cv.height = h * ratio;
cv.style.width = w + "px";
cv.style.height = h + "px";
cv.getContext("2d").scale(ratio, ratio);
return cv;
}
Then I also increased accordingly the pixel ratio of the images used inside of the canvas.

How to calculate shape height and width including its border width in canvas?

I am working on a project using HTML5 and javascript.I have a canvas on which document will be loaded.
canvas = document.getElementById('pdf');
ctx = canvas.getContext('2d');
In above canvas, document will be loaded.
I have to draw different shapes on it as an individual canvas at runtime.So that on drawing i have to create a new canvas for each and every Shape (required).
MouseDown
function mouse_Pressed(e) {
getMouse(e);//getting current X and Y position
x1=mx; y1=my;
var cords=getMousePos(canvas,e);
annCanvas=document.createElement('canvas'); //Creating
annCanvasContext=annCanvas.getContext('2d');
isDraw=true;isDrawAnn=true;
}
MouseMove
function mouse_Dragged(e)
{
if(isDraw)
{
getMouse(e);
x2=mx; y2=my;
calculatePoints(x1,y1,x2,y2);
var width=endX-startX;
var height=endY-startY;
annCanvas.style.position = "absolute";
annCanvas.width=4; //Width of square
annCanvas.style.left=""+startX+"px";
annCanvas.style.top=""+startY+"px";
annCanvas.height=height;
annCanvas.style.zIndex="100000";
document.getElementById("canvas").appendChild(annCanvas);
annCanvasContext.fillStyle='rgb(255,255,0)';
annCanvasContext.lineWidth=borderWidth;
annCanvasContext.strokeRect(0,0,width,height);
annCanvasContext.stroke();
}
}
MouseReleased
function mouse_Released(e)
{
isDrag = false;
isDraw=false;
isDrawAnn=false;
}
Main problem is that if i am drawing rectangle with border width 50 then its Canvas-outside part is going to cut because of less area of canvas(i.e. rect shape). So i want to calculate canvas height, width with its borderWidth.
This should take into account the border width as well as width etc.
function init() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var s = size(canvas);
alert(s[0]);
}
function size(el) {
var clientHeight = (el.offsetHeight > el.clientHeight) ? el.offsetHeight : el.clientHeight;
var clientWidth = (el.offsetWidth > el.clientWidth) ? el.offsetWidth : el.clientWidth;
return [clientWidth, clientHeight];
};
HTML:
<body onload="init();">
<canvas id="canvas" width="200" height="300" style="width: 500px; height: 400px; border: 10px solid brown;"></canvas>
</body>

Categories

Resources