How to centrally align text in HTML canvas? - javascript

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");

Related

Is there a bug in HTML Canvas' fillText function?

I just came across some unexpected behaviour on the HTML Canvas element; I tried to strip the problem down as much as I could. In short, it appears the ctx.fillText fails to render the text in specific regions of the canvas.
This is the smallest script I could write to consistently reproduce the bug (I tested it on different machines, OSs and browsers). It creates a black canvas (1.25×1.25 in drawing space units, 1000×1000 in pixels, the drawing space origin is in the middle) and draws red dots as the mouse passes over it, but there are several horizontal stripes in which it fails to do so.
// define boundaries of drawing space
const left = -.625;
const tops = -.625;
const scale = 800;
const width = 1.25;
const height = 1.25;
// create canvas
let canvas = document.createElement("canvas");
canvas.width = scale * width;
canvas.height = scale * height;
canvas.style.backgroundColor = "black";
document.body.appendChild(canvas);
// create context
let ctx = canvas.getContext("2d");
ctx.scale(scale, scale);
ctx.translate(-left, -tops);
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.font = `.03px arial`;
ctx.fillStyle = "rgba(208, 64, 64, 1)";
// coordinates follow mouse
addEventListener("mousemove", event => {
let mouseX = left + event.layerX / scale;
let mouseY = tops + event.layerY / scale;
ctx.fillText(".", mouseX, mouseY);
});
You can paste it in your dev-tools in about:blank and see for yourself. In any case here's a gif too:
As you can see, there are several horizontal stripes left untouched, even though I pass over them with the mouse. Also, the entirety of the bottom 80% of the canvas is unaffected.
A few important notes:
This occurs with any text, but I used the dot because it takes up the least amount of space, while bigger symbols easily bridged the gaps in the upper portion and made it more difficult to see.
The mousemove event is not the culprit, mouseX and mouseY update properly and smoothly.
It is not due to my mouse being dragged too quickly, it leaves the gaps no matter how slow I move.
It is not due to how small the scale is, as the x dimension has the same scaling as the y dimension, but the former doesn't present this issue.
This does not occur with the ctx.strokeText method, which works fine.
It also does not occur with ctx.fillPath.
Am I doing something wrong? Or is this actually a bug?
The problem is the extremely small font size you're trying to render. You shouldn't use values like .03px - it makes sense for renderers to not be able to render something like that correctly, considering the typical smallest paintable size on a display is 1 pixel (a little smaller than that on high DPI displays, but probably not much less than .25px). It may work for painting simple lines, but rendering is more complicated than that (e.g. hinting).
Try the following values:
scale = 80
width = 12.5
height = 12.5
ctx.font = `.3px arial`;
Alternatively, try to paint a dot or a square rather than a "." string.
As a side note, I was able to reproduce the problem on Chrome, but on Firefox it actually renders fine.

Javascript canvas render centered text with an image

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.

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.

How do I make text gradient in canvas?

I want to fill my text with a top from bottom gradient. I've followed four or five different tutorials on how to achieve this but it doesn't work.
I have two different dynamic text sources, something like this:
<div id="first-name" contentEditable="true">Olaf</div>
<div id="last-name" contentEditable="true">Smith</div>
I want to draw whatever the user writes on the canvas (after a button press). I know how to fetch the values, so let's ignore that part and focus on two things:
1) How to know how wide the content is to fill with a gradient.
2) How to fill anything with a gradient to begin with...
They say this is how you should do it:
var d_canvas = document.getElementById('canvas')
var context = d_canvas.getContext('2d')
var firstNameGradient = context.createLinearGradient(6,38,6,70) //no idea what values I should use
firstNameGradient.colorStop(0, '#eede85')
firstNameGradient.colorStop(1, '#fea700')
context.fillStyle = firstNameGradient
context.font = "bold 26px Tahoma"
context.fillText(firstName, 6, 38)
context.font = "bold 36px Tahoma"
context.fillText(lastName, whereFirstNameEnds, 38)
However, whatever values I try to fill with it doesn't work. In, fact nothing gets drawn in at all.
So I have no idea how to solve either 1) or 2). Does anyone know?
Edit: Just noticed an error:
TypeError: firstNameGradient.colorStop is not a function
context.createLinearGradient(6,38,6,70) //no idea what values I should use
This is defining a line from (x1, y1) to (x2, y2) which in turn describes the angle of the gradient. Seems OK here, might need some fine tuning.
Change these:
firstNameGradient.colorStop(0, '#eede85')
firstNameGradient.colorStop(1, '#fea700')
to use addColorStop() instead
firstNameGradient.addColorStop(0, '#eede85')
firstNameGradient.addColorStop(1, '#fea700')
To find the width (in pixels) you can use measureText() and the property width of the returned object:
var tw = firstNameGradient.measureText(firstName).width;
Text is by default drawn with y representing the baseline. You can change this by setting text-baseline to top - this makes it easier to provide the gradient line:
firstNameGradient.textBaseline = "top";
The specs do provide a way to get the height of a font as well, but no browers has yet implemented this part so you need to either guess or use a DOM element to measure height.
Sorry for being so naive as to use w3Schools, but If you reverse engineer this snippet you should get the job done:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.font="20px Georgia";
ctx.fillText("Hello World!",10,50);
ctx.font="30px Verdana";
// Create gradient
var gradient=ctx.createLinearGradient(0,0,c.width,0);
gradient.addColorStop("0","magenta");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","red");
// Fill with gradient
ctx.fillStyle=gradient;
ctx.fillText("Big smile!",10,90);
Should you need any more advice check it out here
NOTE: Please, for the love of all things holy, don't use w3Schools, please try to use MDN
NOTE 2: I have no experience with canvas, so if I am making myself out to be an idiot, I am very sorry.
You can use ctx.measureText(txt) to get the right width for the canvas. Here's an example that works as required:
var context = document
.querySelector('canvas')
.getContext('2d');
var pos = {x: 50, y: 50};
var txt = 'Hello!';
var txtHeight = 50;
context.font = txtHeight + 'px Verdana';
var txtWidth = context.measureText(txt).width;
var gradient = context.createLinearGradient(
pos.x, pos.y, txtWidth, txtHeight);
gradient.addColorStop(0,"red");
gradient.addColorStop(1,"blue");
context.fillStyle = gradient;
context.fillText(txt,pos.x,pos.y);
And here's the demo on JSFiddle

draw rectangle over html with javascript

I know there's a similar question here, but neither the question nor the answer have any code.
What I want to do, is to port this functionality to a 100% Javascript solution. Right now I'm able to draw a rectangle on top of HTML content using PHP.
I scrape a website with CasperJS, take a snapshot, send the snapshot path back to PHP along with a json object that contains all the information necessary to draw a rectangle with GD libraries. That works, but now I want to port that functionality into Javascript.
The way I'm getting the rectangle information is using getBoundingClientRect() that returns an object with top,bottom,height, width, left,and right.
Is there any way to "draw" the HTML of the website to a canvas element, and then draw a Rectangle on that canvas element?
Or is there any way to draw a rectangle on top of the HTML using Javascript?
Hope my question is clear enough.
This is the function that I'm using to get the coordinates of the elements that I want to draw a Rectangle around.
function getListingRectangles() {
return Array.prototype.map.call(document.querySelectorAll('.box'), function(e) {
return e.getBoundingClientRect();
});
You can create a canvas element and place it on top of the HTML page:
//Position parameters used for drawing the rectangle
var x = 100;
var y = 150;
var width = 200;
var height = 150;
var canvas = document.createElement('canvas'); //Create a canvas element
//Set canvas width/height
canvas.style.width='100%';
canvas.style.height='100%';
//Set canvas drawing area width/height
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//Position canvas
canvas.style.position='absolute';
canvas.style.left=0;
canvas.style.top=0;
canvas.style.zIndex=100000;
canvas.style.pointerEvents='none'; //Make sure you can click 'through' the canvas
document.body.appendChild(canvas); //Append canvas to body element
var context = canvas.getContext('2d');
//Draw rectangle
context.rect(x, y, width, height);
context.fillStyle = 'yellow';
context.fill();
This code should add a yellow rectangle at [100, 150] pixels from the top left corner of the page.

Categories

Resources