How to draw text fitting in a given rectangle on a canvas? - javascript

I am creating some images using the canvas. I would like to be able to draw text in given rect (i.e. offset x, offset y, width and height) on a canvas and that text to be as large as possible without overflowing and if possible, with word wrapping. Is it possible?

It's possible.
no-wrapping:
You can use CanvasRenderingContext2D.measureText() method, which accepts a string, and returns a object { width: float }. You have to find out the height by doing some math using font's size. Then just enumerate the font sizes in a binary search way. soon you will find the best size for the canvas.
wrapping:
You have to find out the wrapping point in the string and calculate their width and height you self.
but there is a better easier way to do this.
create a hidden(visibility: hidden not display: none) div, defines its width, and put your text inside. enumerates font's size, and check if it overflows or grows too high.

Related

Raphael JS detect canvas size when set as %

I am using Raphael JS, and create a canvas with width set at 100% of the container like so...
// there is a `div` with id `paper`
paper = Raphael('paper', '100%', '100%')
paper.circle(50, 40, 100)
Now I want to know how big the canvas is. how can I reliably find out the canvas size on all platforms?
I am not using jQuery.
Update: Potential workaround
Bonus points will be awarded for a solution that makes getting pixel width unnecessary, by making cavas scale proportionally. I am fairly sure this is possible with canvas so assume it is possible with Raphael, so that if I create all elements to a set width (say 100 pixel wide canvas) then I should be able to scale the canvas to 100% of the screen, and the canvas should fill the screen, with all elements stretched appropriately, and keeping their proportions.
You can use the viewbox in order to scale to fit in full screen mode.
http://raphaeljs.com/reference.html#Paper.setViewBox
You define a physical region (x, y, width, height) that contains your svg data, and everything will be scaled up while maintaining the proportions. The viewbox region is upscaled proportionally (when you specify false for fit) to the maximum size it can be inside the container.
paper.setViewBox(0, 0, 100, 100, false);​
Unfortunately, Raphael doesn't seem to give you the option of specifying how the overflow is handled. For example, you might wish to centre the view box so that any excess is spread evenly. If you have a 100 x 200 container and you have a 100 x 100 viewbox, then you have 100 pixels in height below the upscaled viewport, when you may wish for the viewport to have 50 pixels above and 50 pixels below.
In SVG these options are defined on the SVG container's preserveAspectRatio property. If you are not supporting the VML (lte ie8) option then you could change this property to affect the alignment.
http://premsobel.info/notes/ml/svg/viewports.html
As for detecting width and height in general, you are better off detecting the width or height of the parent node using element.clientWidth and element.clientHeight. I tend to avoid using body as the parent node and add my own inner container for detecting the size. Your canvas is 100% width/height of some container, so I would go looking for that container to find out what size it is.

HTML5: inaccurate positioning of rectangles

I'm using canvas of HTML5 to create a "preview" image which mainly consists of some rectangles and simple lines. Works fine so far, but there's one problem I cannot fix somehow. Presume the following situation:
context.fillStyle = "rgba(0,0,0,0.75)";
context.fillRect(100.64646,100,50.94967,20);
context.fillRect(100.64646+50.94967,100,100,20);
So I'm drawing 2 rectangles with some opacity. The x-starting coordinate plus the x-length of the first rect is equal to the x-starting coordinate of the second rect, so in theory they should collide without any margin between. Sadly, the result is different:
(see http://files.clemensfreitag.de/thin_spacing.jpg)
There's a very tiny spacing between the boxes, and the background color is visible. But:
This problem doesn't occur if the coordinates and lengths are integer values.
Is there any way to get it done by using float values? Converting them to integers before drawing might be acceptable in my application, but I'm just wondering why this should not work with floats.
Best,
Clemens
What you're seeing is the result of overlaying two opaque colors. When the first rectangle ends at 151.59613, the rectangle is automatically antialiased, filling in the rightmost column with rgba(0,0,0,0.4470975). When the second rectangle starts at the same x coordinate, it is also antialiased, filling in the leftmost column (the same as the first rectangle's rightmost) with rgba(0,0,0,0.3029025). The two values do add up to rgba(0,0,0,0.75), but that's not how they are blended. Instead, the second color (rgba(0,0,0,.3029025)) is drawn on top of the first, resulting in rgba(0,0,0,0.4470975+(1-0.4470975)*0.3029025) = rgba(0,0,0,0.61457305). So there isn't actually a gap between the two rectangles, but rather a 1px column that is a slightly lighter shade of grey.
Similarly, if you were using solid colors then the second rectangle's antialiased column would overwrite the first's, resulting in an even lighter shade of grey in the "gap".
The issue does not show up with integer values because no antialiasing is required - each rectangle ends at the edge of a pixel.
It looks like none of the globalCompositeOperation settings fix this, and turning off antialiasing would sometimes result in a 1px gap, so I think your simplest solution is to force integer values (alternatively, you could clear that column then fill it in with the desired color).
This problem is related to the way objects are drawn on a float based grid (especially vertical and horizontal lines and thus rects).
See there for an explanation and a schema : http://canop.org/blog/?p=220
Depending on the size of your objects, you need to use integer or mid-integer coordinates and sizes for your shapes, the goal being to fill complete pixels in both dimensions.
For example :
use a mid-integer for a thin line (one pixel width)
use an integer coordinate for a 2 pixels wide line
(and extend the logic for rects)

How to get the maximum width of a single character in JavaScript?

How can I get the maximum width of a single character in JavaScript? I have a text painted in an unknown font, but I know what the text length and font size is.
Now I want the to get the largest character in that font, so I can make sure the parent element (a div for example) is not too narrow (it has an absolute width). The widest character is usually the M or W, but I'm not sure, perhaps there are fonts which paints wider characters.
Now how do I do that? Is there a fast method for?
Use the CSS width: 1em;. em is a relative measurement and is the width of the 'm' character

Stretching a Graph to Fill the Canvas Element in JavaScript

Quick question involving javascript canvas... I have a set points (connected with a line) I want to graph on a 400x300 canvas element. I will constantly be adding more points. I need the line to stretch to fill the entire canvas (leaving no unnecessary space).
Example:
into this:
Thanks! C.Ruhl
You want to find the step by doing canvasWidth / (number of points - 1)
and adding X += step each time.
Example here:
http://jsfiddle.net/pDDTQ/
Distinguish between internal canvas size and visible size. 400x300 is your visible size and set by style="width:400px; height:300px". Everytime there is new point (e.g. 400,500) you set canvas.width=400; canvas.height=500; and replot the whole graph. From a certain point you might want to adjust the width of the line.

Get svg graphics size when width/height attributes on <svg> are not set

So how to get the size of an SVG file with javascript when width and height are not set on the svg element? The svg is a string, but could be made a DOM if necessary.
You can use the getBBox() method of the SVGElement object. It tells you the width and height, x, and y offset in pixels without taking into account the scaling of the element.
document.getElementById('myelem').getBBox().width
document.getElementById('myelem').getBBox().height
are what you're looking for, I think.
EDIT:
I've recently also learned that the little known getBoundingClientRect() works as well! You can also do: var el = document.getElementById('myelem'); var mywidth = el.getBoundingClientRect().width;
Keep in mind that there is a difference between getBBox and getBoundingClientRect. getBBox gets the initial dimensions - getBoundingClientRect also respects the transformations done with scale etc.
SVGs are scalable vector graphics, and thus can have any arbitrary height and width. Only the ratio is fixed by the format.

Categories