I draw the text on canvas and then apply requestAnimationFrame to change its color:
var text = 'Sample text';
ctx.fillText(text,canvas_width/2,100);
requestAnimationFrame(animate);
function animate(time){
ctx.fillText(text,-offset,100);
}
See the demo with full code used.
Initially text looks OK (jf you comment requestAnimationFrame() line):
but after animation used it looks like below -
You may notice white pixels there, which looks awful (please ignore the colors used - they are applied to demonstrate the problem).
What could be wrong there?
I was thinking that it is probably caused by my offset/position calculations - textWidth/2, cw/2, but could it return different result from time to time?
I've tested the code with Google Chrome 39.0.2171.95 (64-bit) under OSX 10.10.1.
You're not clearing the canvas, so the old text will stay there. That is the problem that is giving you your white pixels. This problem is (presumably) because of anti-aliasing.
To fix this problem, like I have mentioned, you're not clearing the canvas on every frame. Make sure you add this before you draw anything on the canvas:
ctx.clearRect(0, 0, canvas.width, canvas.height)
Related
The idea is simple, create a star with text and rotate it.
But its not smooth after making a quick script
here is my fiddle
The star is moving oke, but the text is shaking like a snake :)
Cause
The reason why this happening is due to the browser's text rendering engine. They are translating each point in the text path using the same rotation matrix but due to the engine you will get rounding errors in there causing the text to "jibber".
This is not only happening for canvas but for CSS transformations as well, with text.
Solution
Simply render the text to a canvas "layer" and use that as an image which you rotate instead of the text itself. This rasterizes the text in its normal position and the transformation happens on the bitmap instead which most browsers handle pretty well.
Here is one example integrating the answer I linked to in the comments. I'm showing only the main text as it works as a comparer as well, before and after:
// canvas layer
var tcanvas = document.createElement('canvas'); //tcanvas must be in global scope
var tctx = tcanvas.getContext('2d');
tcanvas.width = canvas.width;
tcanvas.height = canvas.height;
tctx.translate(250, 250);
tctx.fillStyle = "black";
tctx.font = "bold 60px Arial";
tctx.textAlign = 'center';
tctx.fillText('€ 1215,34', 0, 0);
Now the layer is ready and we can replace the text drawing methods with a single drawImage instead:
c.drawImage(tcanvas, -x, -y);
Result of this modification
To draw the "extra" just move those lines down to the layer creation as well. Note that tcanvas in the example must be accessible globally.
If the rotation speed of the text is not intentional just remove the second call to rotate you have there before rendering the text.
Tip: instead of redrawing gradients and the star just render it once to another layer and rotate that as an image as well. This will be much more efficient. You could also avoid save/restore (which are relative costly) by just accumulating the step on rotate() itself and use setTransform to transform the matrix using absolute values.
Ways to optimize memory usage: for text layer use a canvas size where the text fits in exact (don't use fixed values as text size may vary in size and position from browser to browser depending on the text rendering engine), same for star if you go with a layer for that as well.
Hope this helps!
The shake in your text comes from the fact that the context is not in a proper state when you draw the text : you just did quite some operations on it before.
I just added a
c.restore();
c.save();
c.translate(x,y);
before the text part of your code, and the text is solidly hung to the star now :
http://jsfiddle.net/gamealchemist/xUr4f/1/
Edit : There are in fact 2 issues at stake here : 1) the text rotation is not quite on track with the star and 2) the rounding of the text coordinates makes the text shake.
Both Chrome and FF exhibit 1) of course, and with a clean context 1) disappear on both.
For 2) : Chrome is ok with non-integer coordinates text, but FF does round, which creates a shake on a rotating text.
Only solution i see it to 'print' the text on a canvas, then have the canvas rotate. I did it for the 'extra' in this fiddle :
http://jsfiddle.net/xUr4f/4/
There's a loss of quality compared to the fillText, but unless the coordinates rounding can be avoided, it seems as the best solution.
Let's say that I use some HTML5 markup:
<canvas id="e" width="400" height="200"></canvas>
<script>
var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
context.fillStyle = "red";
context.font = "bold 72px JerseyLetters";
context.fillText("Bulls", 50, 100);
</script>
To make some cool text like this:
Then I decide I want these letters to fit into an envelope that looks like this:
Hoping to get something like this:
How would I go about (1) defining an envelope like the one above and then (2) putting the text in the envelope using HTML5 Canvas to get the result?
I am open to either something that directly places text in the envelope or a solution that first creates an image and then fits the image in an envelope.
Thanks!
EDIT
I added the tags "webgl" and "three.js" to this question on the advice of #markE. I will research those two packages in the mean time as well. I'm very new to .
webGL way:
Do it as a image-processing with pixel-shader.
Render text with 2d canvas, bind webGL texture with buffer and fill texture with canvas image (rendered text). Have prepared envelope that actually maps the area that envelope holds and also every pixel play role of the UV coordinate from the first image. Running that as pixel shader, you have image-to-be-squeezed and envelope (uvs) you'll output final image. That way, it's completely font and text independent. You could even probably make one image-processing step more so you could load any envelope shape and process it on spot, so it becomes font, text and envelope-shape independent.
I'm not sure how well did I explain this.
Hope this helps, though.
SVG provides these sort of text transforms. See http://tavmjong.free.fr/SVG/TEXT_PATH/TextPath.html
EDIT: This link appears to be converting the text to actual SVG. Probably not going to be helpful for you, sorry.
I'm trying to draw a shape with quite precise rounded corners, I'd settle for anything around 3px. Unfortunately Flash has other ideas, and is creating a rounded rectangle with four seemingly different radii. My code is below:
var sq:Shape = new Shape();
sq.graphics.beginFill(0x000000,1);
sq.graphics.drawRoundRect(20,20,20,20,4,4);
sq.graphics.endFill();
addChild(drop);
I removed the line as apparently fills render better, and changed to an even number radius as apparently that helps, but it's still the same. The code above gives me a square like so:
The corners are noticeably different. If I were using a bigger radius it might not be so much of a problem, but because of the small radius of the corners the square just looks odd.
Have I missed the drawRoundRectEvenly function or am I asking too much here? Any help is appreciated! Thanks!
Darren
Try setting pixelHinting to true.
sq.graphics.lineStyle(1, 0x000000, 1.0, true);
Even if you're not using strokes, try it with an alpha 0 and see if it helps smooth things a bit. It will most likely still not be perfect, but it should snap to pixels a bit better and clean up dramatically.
I have a canvas with the following size: 500x200. Inside this canvas i'm drawing some number of blocks (actually - table cells). Information about how much blocks i should draw i'm getting via AJAX, but size for every cell is fixed - 100x50. So, i can display inside my canvas only 5 blocks horizontally and 4 vertically. But what about other blocks? What if script return a table 30x30 cells. How can i side scroll (mouse preferred) my canvas so user can the rest of the cells (no zoom out, only scrolling).
If you need any more information, please, tell me and i will provide it.
Thank you.
The easiest way to accomplish this is to implement mouse-panning.
On the mouse down event, begin panning and save the mouse position
On the mouse move event, translate the context (ctx.translate(x,y)) by the difference between the current mouse position and the original position, then redraw the scene.
On the mouse up event, stop panning.
There are harder ways. You could implement scrollbars inside the canvas, as Mozilla Bespin has done (...which became Mozilla Skywriter which then merged with Ace and dropped all Canvas use). The code that they used was pretty good.
Or you could implement DOM scrollbars for use with your canvas, which isn't exactly easy to get right in all cases. This involves adding several dummy divs in order to give the appearance and function of real scrollbars. I have done this but the code remains unreleased for now. But thats no reason you can't give it a try if thats what you really want.
Check out a great tutorial at: http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx
It will give you an answer to your question and much much more...
I'm with Simon Sarris on this, but as an alternative, you could clone the canvas, and replace it with a blank canvas, and then render the original canvas as an image. I've some MooTools js that goes like this, which is fine for my use, by ymmv:
var destinationCanvas = this.canvas.clone()l
destinationCanvas.cloneEvents( this.canvas, 'mousemove');
var destCtx = destinationCanvas.getContext('2d');
destCtx.drawImage(
this.canvas,
(this.options.scrollPx)*-1,
0
);
destinationCanvas.replaces( this.canvas );
this.canvas.destroy();
this.canvas = destinationCanvas;
this.ctx = destCtx; // this.canvas.getContext('2d');
I'm a bit confused with the way the canvas element anti-aliases text and am hoping you all can help.
In the following screenshot the top "Quick Brown Fox" is an H1 element and the bottom one is a canvas element with text rendered on it. On the bottom you can see both "F"s placed side by side and zoomed in. Notice how the H1 element blends better with the background:
Here's the code I'm using to render the canvas text:
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.font = '26px Arial';
ctx.fillText('Quick Brown Fox', 0, 26);
}
Is it possible to render the text on the canvas in a way so that it looks identical to the H1 element? And why are they different?
It's now possible to get sub-pixel font rendering by creating an opaque canvas context. In Safari and Chrome you can get this using this snippet:
var ctx = canvas.getContext("2d", {alpha: false})
I found this from this blog post.
Answering my own question:
It is possible using the technique demonstrated on this site:
https://bel.fi/alankila/lcd/
The only problem is that its too slow to implement in a production app. If anyone runs across a faster way please let me know.
Matt, I sat with the (same/similar) problem last week, which, in my case, turned out to be because of differences in pixel densities on the devices I was testing; I wrote about it tonight - http://joubert.posterous.com/crisp-html-5-canvas-text-on-mobile-phones-and
The link at posterous is dead, so here is the gist with the source code:
https://gist.github.com/joubertnel/870190
And the snippet itself:
// Output to Canvas without consideration of device pixel ratio
var naiveContext = $('#naive')[0].getContext('2d');
naiveContext.font = '16px Palatino';
naiveContext.fillText('Rothko is classified as an abstract expressionist.', 10, 20);
// Output to Canvas, taking into account devices such as iPhone 4 with Retina Display
var hidefCanvas = $('#hidef')[0];
var hidefContext = hidefCanvas.getContext('2d');
if (window.devicePixelRatio) {
var hidefCanvasWidth = $(hidefCanvas).attr('width');
var hidefCanvasHeight = $(hidefCanvas).attr('height');
var hidefCanvasCssWidth = hidefCanvasWidth;
var hidefCanvasCssHeight = hidefCanvasHeight;
$(hidefCanvas).attr('width', hidefCanvasWidth * window.devicePixelRatio);
$(hidefCanvas).attr('height', hidefCanvasHeight * window.devicePixelRatio);
$(hidefCanvas).css('width', hidefCanvasCssWidth);
$(hidefCanvas).css('height', hidefCanvasCssHeight);
hidefContext.scale(window.devicePixelRatio, window.devicePixelRatio);
}
hidefContext.font = "16px Palantino";
hidefContext.fillText("Rothko is classified as an abstract expressionist.", 10, 20);
Here's a way of doing sub-pixel rendering for any canvas content (text, images, vectors, etc). http://johnvalentine.co.uk/archive.php?art=tft.
Outline of the method
It draws onto a canvas, which is then drawn to the screen to take advantage of RGB-striped subpixels. It works with alpha channels too. Note that this might not work if you are using a portrait display, non-striped pixels, or if your browser displays canvases at a lower resolution than your display.
There's scope for fine-tuning, but it's a big gain for a simple method.
This is generically called subpixel anti-aliasing, or ClearType on Windows. I'm not aware of any OS/browser combinations that currently support this for Canvas.
I'd be interested to see some tests using sub-pixel offsets for the text to see if any browsers even use pixel-based hinting of the font rendering (aligning ascenders on pixel boundaries, for example). My assumption would be no.
Edit: My assumption was wrong; it would appear that Safari, Chrome, and Firefox all utilize some pixel font hinting. Safari and Chrome appear the same, snapping to whole pixel boundaries, but are different from Firefox (snapping to half-pixel boundaries?). See the visual results of testing (on OS X) here: http://phrogz.net/tmp/canvas_text_subpixel.html
You could make the fonts a lot clearer with fairly easy technique.
You can scale the canvas in CSS twice as small:
canvas {
transform-origin: left top;
transform: scale(0.5);
}
In the HTML double the dimensions of the canvas:
<canvas width="(width*2)" height="(height*2)">
Finally draw everything on the canvas in double size.
You will notice that the fonts are a lot clearer.
This is not completely the same as H1 in the HTML but a looks lot better than normal font rendering.