Javascript canvas render centered text with an image - javascript

I am starting to work with the Javascript canvas (CanvasRenderingContext2D) and I am finding centering text really hard to do. I am using the following piece of code to center the text both horizontally and vertically:
ctx.textAlign = "center";
ctx.textBaseline = "middle";
I know I can use ctx.measureText() to get the width of a string but the height is not available in the returned object. I tried using advanced text metrics but they seem to be a new thing and my Firefox build (latest in the Ubuntu store) doesn't support it yet and I want my webpage to be at least compatible with 1 year-old browser versions.
I am being able to center the text right now but the problem is centering two objects (an image and a piece of text), like the second situation in the following image, where the objects must be centered together (like they were a single object that contained both the image and the text):
https://drive.google.com/file/d/1_P9826O1x0bFhRqPEKz_AB18iYNPa5CP/view?usp=sharing
The only way I can think of doing that is by knowing the height of the text, because I need to calculate both the position of the image and the text (pseudo-code):
//I can do this
image.x = (canvas.width / 2) - image.width;
text.x = (canvas.width / 2) - text.width;
//But not this
image.y = (canvas.height / 2) - (image.height / 2) - (text.height / 2);
//Do this with top-left text align
text.y = (canvas.height / 2) + (image.height / 2) - (text.height / 2)
Sadly, the only ways I found of getting the height of the text was through "it kinda works" approximations and rendering the text to a canvas and getting its pixel data to see the lowest pixel, which is bad for performance.
What can I do to fix this and get the text height in a better way? Is ti possible to center them without even having the height of the text? I would like to keep using the canvas element because this will be for a game, so I can't use HTML and CSS to center objects. Please, only use plain Javascript (no things like query).
Thanks in advance.

Related

HTML Canvas Not Displaying Correctly When Resized to Fit Parent Div with 'offsetWidth'

I'm working on a sketchpad application using html canvas and javascript (trying to stay away from jQuery). The canvas needs to be responsive and I've found several methods to do so, but each one stretches out the canvas and makes the sketchpad unusable. It's hard to explain without seeing the problem. Here's the CodePen. Try drawing inside the canvas and you'll see what I'm talking about. The current method I'm using to resize the canvas incorporates offsetWidth and offsetHeight like so:
var sketchpadContainer = [
document.getElementById('container').offsetWidth,
document.getElementById('container').offsetHeight]
var canvas = document.getElementById('sketchpad');
canvas.style.height = sketchpadContainer[1] + "px";
canvas.style.width = sketchpadContainer[0] + "px";
Is there a way to make the canvas responsive while at the same time keeping the dimensions of the sketch intact?
The CSS width and height properties are NOT the same as the width and height attributes on a Canvas element.
If you absolutely need to use css to set width/height, keep a scale factor of your default canvas size, then multiple the target x and y positions of your mouse position by the inverse of the x/y scale factors (or just divide the target position by them).
Using css to resize your canvas is a bit too hacky imo (and will leave your lines blurry), I highly recommend you instead simlpy change with width/height attributes of your canvas and use CanvasRenderingContext2D.scale() to change the size of your lines (A scale factor will still need to be used to calculate your true mouse pos, however)
Simply change
canvas.style.height = sketchpadContainer[1] + "px";
canvas.style.width = sketchpadContainer[0] + "px";
to
canvas.height = sketchpadContainer[1];
canvas.width = sketchpadContainer[0];
Apply CanvasRenderingContext2D.scale() when you first get your context, and then do as I mentioned above. (ctx.lineTo(x,y); -> ctx.lineTo(x/scaleFactorX,y/scaleFactorY); & lastX=x; -> lastX=x/scaleFactorX;)
I.E See HERE

Writing blank on canvas using fillText

I have a chart.js Donut chart in the center of which I have to show a value.There is a date field which refreshes the chart thereby affecting the central value.
var text = dataTxt,
textX = Math.round((width - ctx.measureText(text).width) / 2),
textY = height / 2;
ctx.fillText(" ", textX, textY);//attempting to write blank value.
ctx.fillText(text, textX, textY); //this writes the value.
ctx.save();
Problem is that on subsequent update of the chart the central area is not cleared & the value gets written over the old one.
For this I added the line ctx.fillText(" ", textX, textY); in above code but to no effect.
Any Ideas on how to clear up the area before the text is written there?
There is a HTML/CSS solution to write the data in the center but I cant use that since it doesnt export the central data when chart is downloaded as PNG.
In typical use cases people don't attempt to "undo" their changes on the Canvas - it's far easier to just clear the Canvas and start drawing all over again.
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Do my drawing again
myChart.update(); // With chart.js this triggers a redraw.
In your case I think you're making the mistake of thinking that the Canvas saves the text image and lets you alter it in subsequent drawings. Think of it like Microsoft Paint - once that text is on the image, you're not going to be able to get rid of it easily without undo'ing.

What is the best way to scale contents of a realtime whiteboard

I am making a whiteboard with angularjs, socketio an node.js.
As long as I use a fixed width/height for the canvas everywhere I can just broadcast the coordinates of the mouse/touch event and recreate the graphic in realtime. However, the problem I am facing is when trying to make the canvas have different sizes across different platforms (think desktop and a smartphone), the canvas has to be scaled and so does the graphic, but this makes things pretty slow.
The approach I am currently taking is to draw the graphic in a temporary hidden canvas of original size, then when there is a pause in the drawing stream (in other words the user has stopped doodling), I scale and copy it to the main canvas. The problem with this is, it doesn't feel very realtime at all, especially when a user keeps doodling without a pause for a while. Another approach I could try is to push all the coordinates in an array, apply 2d affine transformation on it, then redraw the entire thing. Though this too doesn't seem like a good solution for when the array size increases, repeatedly trying to apply transformations in realtime can easily eat up a lot of resources.
Is there any better way to achieve this?
do it with css scale transform. Scale the canvas to fit the size of the device be it a mobile, desktop, or tablet, or whatever.
Have your canvas be fixed width across all devices. Say it's 640px by 480px. Now we'll resize it to fit whatever window.
$(window).resize(function() {
var w = 640; // $('#mycanvas').width();
var h = 480; // $('#mycanvas').height();
var newWidth = $(window).width(); // this could be either smaller or bigger than the canvas
var newHeight = $(window).height(); // same here
var scaleX = newWidth / w;
var scaleY = newHeight / h;
$('#mycanvas').css('transform','scale(' + scaleX + ',' + scaleY +')');
$('#mycanvas').css('-webkit-transform','scale(' + scaleX + ',' + scaleY +')');
});
note: mycanvas could also be a container if any kind that holds the canvas and other divs or whatever. just make sure that w is the width of that container, etc.
note: if someone uses their fingers to draw across the screen, you may need to convert it from scaled coordinates to fixed width (640x480) coordinates.
Btw, I found a different way to do this that might be better. not sure:
Scaling canvas element with static resolution

Weird HTML 5 Canvas Antialiasing

I've been playing with canvas element and discovered that when I attempt to draw NxN uniform solid-colored cells next to each other, in some width/height configurations, there are blurry white-ish lines between them.
For instance, this canvas is supposed to look black but contains some sort of grid which I conjecture to be a result of faulty antialiasing in the browser.
Suffice to say, this bug appears only in some configurations but I would like to get rid of it for good. Is there any way to circumvent this? Have you ever had problems with antialiasing in canvas?
I have made this fiddle which demonstrates the issue and allows you to play with the dimensions of the canvas and number of cells. It also contains the code I use to draw the cells, so that you can inspect it and tell me if I'm doing anything wrong.
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
for (var i = 0; i < numberOfCells; ++i) {
for (var j = 0; j < numberOfCells; ++j) {
ctx.fillStyle = '#000';
ctx.fillRect(j * cellWidth, i * cellHeight, cellWidth, cellHeight);
}
}
Thanks in advance,
Petr.
jsFiddle : https://jsfiddle.net/ngxjnywz/2/
snippet of javascript
var cellWidth = Math.ceil(canvasWidth / numberOfCells);
var cellHeight = Math.ceil(canvasHeight / numberOfCells);
Depending on the width, height and your numberOfCells you are sometimes getting a... lets say 4.2 which is 4, however this would be displayed wrong and will allow a 1 pixel blank line to appear. So all you need to do is use the Math.ceil function and this will cause your cellWidth and cellHeight to always be the higher number and you won't get blank lines anymore
The best solution is to add a 0.5 pixel wide stroke around all the fills, using the same style as the fill and offsetting all drawing so that you render at the center of pixels rather than the top left.
If you add scaling or translation you will have to adjust the coordinates so that you still give the centers for your drawing coordinates.
In the end you can only reduce the artifacts but for many situations you will not be able to completely remove them.
This answer shows you how to remove the artifacts for an untransformed canvas.
How to fill the gaps
After reading through and trying several approaches, I've decided to come up with my own. I've created another (virtual) canvas which had integer dimensions corresponding to the number of cells in the grid.
After drawing all the cells in there, I call context.drawImage() on the main canvas and pass the virtual canvas as an argument along with offset and scale parameters to make it fit rest of my drawing. Assuming that the browser would scale the virtual canvas's image as a whole (and not as individual cells), I was hoping to get rid of the unwanted separator lines.
In spite of my efforts, the lines are still there. Any suggestions?
Here's the fiddle demonstrating my technique: https://jsfiddle.net/ngxjnywz/5/

How to centrally align text in HTML canvas?

I was making a small JavaScript game when I ran into this small issue: The text is not centrally aligned.
A simple example:
var txt="Lorem Ipsum";
context.fillText(txt,100,100);
Now the problem is that the beginning of the text is at the point 100,100. So later when I change the value of txt into a longer sentence, it is still drawn staring from 100,100 , reducing the aesthetic appeal of the program.
My question is, is there a way to draw text in such a way that the coordinates given mark the center of the text and not the beginning?
You can use textAlign:
context.textAlign = "center";
You can use the measureText() method of the context, to get the width of the text, and then make adjustments based on it. So let's suppose that you want to put it in the center of the canvas, then
(canvas.width / 2) - (ctx.measureText(txt).width / 2)
gives the position where the text should go. In the vertical case:
(canvas.height / 2) - (ctx.measureText(txt).height / 2)
Where canvas is the HTML5 Canvas itself, and ctx is the 2DContext object:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

Categories

Resources