Why doesn't createRadialGradient work on Firefox? - javascript

I'm working with the canvas tag of HTML5 and JavaScript to access the canvas methods and properties.
This code works on Chrome but it doesn't work on Firefox: http://jsfiddle.net/thirtydot/BD3xA/.
Does anyone know why?

createRadialGradient works on Firefox, but addColorStop does not work completely - and will throw an exception if you pass in a transparency along with your color.
For example, after you create a radial gradient:
var grad = ctx.createRadialGradient(centerX,centerY,outRadius,centerX,centerY,outRadius+pad);
var colorOut="rgba(100,200,100,0.7)";
grad.addColorStop(0,'rgba(0,0,0,0)');
grad.addColorStop(0.01,colorOut);
The above works great on chrome, but will not work on FF because of the 0.7 in the rgba color.
So, I use something like:
colorOut= ($.browser.mozilla)?'#D88':'rgba(200,100,100,0.7);';
This doesn't make gradients look quite so nice on FF, but functions.
Of course, you should cache that $.browser.mozilla earlier - make a var IS_MOZILLA = $.browser.mozilla; and then just use that (so that you minimize class calls... as saving every computation in complex drawing calls is important).

Related

Raphael.js function getBBox give back NAN/NAN/NAN in IE8

using Raphaƫl 2.1.4 - JavaScript Vector Library
do something like that:
var textDummy = paper.text(50,500, 'hello world').attr({fill: 'transparent', 'font-size': 14});
var textBox = textDummy.getBBox();
with chrome and firefox everything is fine,
but in IE8 it give back NaN/NaN/NaN,
par exemple textBox.height is NaN.
how i can fix this?
i found a workaround solution from this answer to the question
"Raphael JS and Text positioning"
If i use _getBBox() instead of getBBox() everything is working in ie 8 also.
_getBBox() is undocumented but used internally by Raphael itself, and it works!
I had the same problem in Rapahel 2.2.0 and 2.2.1, and using ._getBBox() didn't fix it for me.
What did fix it for me is falling back to .auxGetBBox() if it's defined and regular .getBBox() doesn't work, like this:
var bbox = path.getBBox( );
// Workaround for apparent bug in Raphael's VML module's getBBox() override
if( isNaN( bbox.x ) && path.auxGetBBox ){
bbox = path.auxGetBBox();
}
I don't have a fix for the underlying bug, but I have found the source of it.
In VML mode, Raphael takes the initial getBBox() function, saves it as auxGetBBox() on the element prototype, then replaces it with a function that appears to be broken.
It has calculations based on a variable defined as var z = 1/this.paper._viewBoxShift.scale;, which clearly expects _viewBoxShift.scale to be some factor of the scale of the current viewbox compared to the initial viewbox , but actually _viewBoxShift.scale is an object like this which appears to come from paperproto.getSize():
{ height: someNumber, width: someNumber }
This is where all the NaNs are coming from. Cannae divide by an object.
So this workaround works fine if no zoom is applied using a viewbox, but may give incorrect results if a zoom has been applied (something I can't get to work at all in recent versions of raphael in VML mode, but that's a seperate question). Fixing that will involve digging deep into Raphael's VML module to pipe a proper zoom factor into this z variable.

Is context.clearRect() really THAT expensive?

I have this piece of canvas animation that is exhibiting some weird characteristics:
http://jsbin.com/olasol/2/edit
I'm on the latest version of Chrome. I'm using Chrome's inbuilt FPS monitor (you can activate it by going to about:flags).
I have marked the line in the JavaScript section which I think is the potential culprit:
fallingctx.clear();
This line does nothing special. It calls a function which in-turn calls clearRect().
The "weird" things I notice are:
The clear(); function causes very noticeable FPS drop on my laptop (Core 2 Duo), but not on my desktop (i5 2500k).
Removing that line alone is sufficient to produce 60fps on my laptop as well. As expected, the canvas doesn't clear after each frame, but still, it produces stable 60fps.
The FPS drop happens only when my Chrome window is on the larger side! When I shrink the window and reload, it doesn't happen! (Is it more expensive to clear a larger rectangle?).
I tried replacing the clear() with a drawImage() of a full white JPEG to cover the canvas. The JavaScript is able to do 200 drawImage() executions each cycle for the smaller image particles (evident from the second point). However, when I add one single drawImage for the overall canvas, it lags again! (Make sure the output occupies the entire screen in order to reproduce the result.)
Why is all this happening? How do I fix it?
It really depends on the hardware, but think about what the invocation of clearRect has to do! Something essentially must zero-out a piece of memory large enough to handle the canvas contents. That can be costly. Think about how much memory has to hold RGBA at HD resolutions... That's over two million pixels of data, around 8 MB in bytes Admittedly, it's not all that much these days in general, but if there's any bandwidth or caching issues related to pushing memory to the video card or something you are doing 60 times a second... well, expect problems.
What I've heard often works is just to clear around where the image is formerly drawn. See http://jsbin.com/olasol/6/edit
I made the following changes for you.
for (var i=0; i< noOfDrops; ++i)
{
fallingctx.clearRect(
fallingDrops[i].x-1,
fallingDrops[i].y-1,
fallingDrops[i].image.width+2,
fallingDrops[i].image.height+2);
}
for (var i=0; i< noOfDrops; ++i)
{
fallingDrops[i].y += fallingDrops[i].speed; //Set the falling speed
fallingctx.drawImage (fallingDrops[i].image, fallingDrops[i].x, fallingDrops[i].y);
}
There's probably a good reason that I need to clearRect around where the image was rendered but a simple reason escapes me. (It is something to do with things being rendered not quite at the pixel specified... I forget exactly).
You also need to do something about the fact you are starting the render loop before the image is loaded (also in the jsbin) so I added
var imgSource = "http://lorempixel.com/20/20/sports/";
var imgObj = new Image();
and replaced superinit
function superinit()
{
imgObj.onload = function(){
flowerfallsetup();
requestAnimFrame(flowerfall);
}
imgObj.onerror = function (){
alert("could not load image");
}
imgObj.src = imgSource;
}
Edit: I forgot to mention because of the prior image setup, I did change the line in your flowerfallsetup :
fallingDr["image"] = imgObj;
There are many ways to handle the asynchronous loading of images, I just chose one that was easy for this example.
Edit: I have to confess, there might be a bit more to this. It works fine on desktop browsers, but on the iPhone, there are clipping issues. If I can figure out what's causing the problem I'll try to post an update.

Canvas draw calls are rendering out of sequence

I have the following code for writing draw calls to a "back buffer" canvas, then placing those in a main canvas using drawImage. This is for optimization purposes and to ensure all images get placed in sequence.
Before placing the buffer canvas on top of the main one, I'm using fillRect to create a dark-blue background on the main canvas.
However, the blue background is rendering after the sprites. This is unexpected, as I am making its fillRect call first.
Here is my code:
render: function() {
this.buffer.clearRect(0,0,this.w,this.h);
this.context.fillStyle = "#000044";
this.context.fillRect(0,0,this.w,this.h);
for (var i in this.renderQueue) {
for (var ii = 0; ii < this.renderQueue[i].length; ii++) {
sprite = this.renderQueue[i][ii];
// Draw it!
this.buffer.fillStyle = "green";
this.buffer.fillRect(sprite.x, sprite.y, sprite.w, sprite.h);
}
}
this.context.drawImage(this.bufferCanvas,0,0);
}
This also happens when I use fillRect on the buffer canvas, instead of the main one.
Changing the globalCompositeOperation between 'source-over' and 'destination-over' (for both contexts) does nothing to change this.
Paradoxically, if I instead place the blue fillRect inside the nested for loops with the other draw calls, it works as expected...
Thanks in advance!
Addenum: Changing the composite operation does behave as expected, but not for remedying this specific issue. Sorry for the ambiguity.
There's a lot that's suspect here.
First off double buffering a canvas does nothing but hurt performance by adding complication, all browsers do double buffering automatically, so if that's your goal here you shouldn't be drawing to a buffer.
Here's an example of why you don't need double buffering: http://jsfiddle.net/simonsarris/XzAjv/
So getting to the meat of the matter, lines of javascript inside a discrete function don't simply run out of order. Something else is wrong here.
Setting a breakpoint on the drawImage would solve this pretty much instantly, so if you aren't familiar with firebug or chrome developer tools I'd highly recommend giving them a look.
I'm guessing that the "blue" you're seeing is actually the only thing drawn to your "buffer" canvas and perhaps this.buffer is not actually the buffer context.
Another possibility is that this.w and this.h are accidentally very small, so that your initial clearRect and fillRect at the start of the method are doing nothing.
In any case speculation is nowhere near as good as opening up developer tools and actually looking at what's happening.
Generally speaking if you need things to be in order use an array not an object. Iterating over an object is not guaranteed to be in any particular order.
Use an array and for (var i=0; i

Optimal pixel drawing speed?

I'm using the Canvas object with javascript. Just doing some tests to see how fast I can set pixels in a draw loop.
On mac, it works great in FF, safari, chrome. On windows, I get a flickering effect on FF and chrome. It looks like somehow the canvas implementation on windows is different than on mac for the different browsers? (not sure if that's true).
This is the basic code I'm using to do the drawing (taken from the article below - I've optimized the below to tighten the draw loop, it runs pretty smooth now):
var canvas = document.getElementById('myCanvasElt');
var ctx = canvas.getContext('2d');
var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var x = 0; x < canvasData.width; x++) {
for (var y = 0; y < canvasData.height; y++) {
// Index of the pixel in the array
var idx = (x + y * canvas.width) * 4;
canvasData.data[idx + 0] = 0;
canvasData.data[idx + 1] = 255;
canvasData.data[idx + 2] = 0;
canvasData.data[idx + 3] = 255;
}
}
ctx.putImageData(canvasData, 0, 0);
again, browers on windows will flicker a bit. It looks like the canvas implementation is trying to clear the canvas to white before the next drawing operation takes place (this does not happen on mac). I'm wondering if there is a setting I can change in the Canvas object to modify that value (double-buffering, clear before draw, etc)?
This is the article I am using as reference:
http://hacks.mozilla.org/2009/06/pushing-pixels-with-canvas/
Thanks
I think it's fairly clear that browsers who implement the Canvas object use DIBS (device independent bitmaps). The fact that you have access to the pixelbuffer without having to lock the handle first is proof of this. And Direct2D has nothing to do with JS in a browser thats for sure. GDI is different since it uses DDBs (device dependent bitmaps, i.e allocated from video memory rather than conventional ram). All of this however has nothing to do with optimal JS rendering speed. I think writing the RGBA values as you do is probably the best way.
The crucial factor in the code above is the call to putImageData(). This is where browsers can differ in their implementation. Are you in fact writing directly to the DIB, and putImageData is simply a wrapper around InvalidateRect? Or are you in fact writing to a duplicate in memory, which in turn is copied into the canvas device context? If you use linux or mac then this is still a valid question. Although device contexts etc. are typically "windows" terms, most OS'es deal with handles or structures in pretty much the same way. But once again, we are at the mercy of the browser vendor.
I think the following can be said:
If you are drawing many pixels in one go, then writing directly to the pixelbuffer as you do is probably the best. It is faster to "bitblt" (copy) the pixelbuffer in one go after X number of operations. The reason for this is that the native graphics functions like FillRect also calls "invalidate rectangle" which tells the system that a portion if the screen needs a re-draw (refresh). So if you call 100 line commands, then 100 update's will be issued - slowing down the process. Unless (and this is the catch) you use the beginPath/EndPath methods as they should be used. Then it's a whole different ballgame.
It's here that the Begin/End path "system" comes into play, and also the Stroke/Outline commands. They allow you to execute X number of drawing operations within a single update. But a lot of people get this wrong and issue a redraw for each call to line/fillrect etc.
Also, have you tried creating an invisible canvas object, drawing to that, and then copying to a visible canvas? This could be faster (proper double-buffering).
The problem is with the way the browsers use the native graphics APIs on the different OSes. And even on the same OS, using different APIs (for example GDI vs. Direct2D in Windows) would also produce different results.

Problems with animateAlong in IE7

I'm having trouble making a simple shape move along a path in IE7 (the only version of IE I tried, actually). The following code works fine in chrome and firefox, but not IE. I couldn't find an obvious reason, has anybody seen something similar?
canvas.path(rPath.path).attr("stroke", "blue");
var circle = canvas.circle(rPath.startX, rPath.startY, 5);
circle.animateAlong(rPath.path, 3000, true);
My rPath variable has the path and the starting point coordinates.
Microsoft script debugger points to this line as the one where the code breaks:
os.left != (t = x - left + "px") && (os.left = t); (line 2131 inside the uncompressed raphael.js script file, inside Element[proto].setBox = function (params, cx, cy) {...})
Any ideas? Any experience (good or bad) with raphael's animateAlong in IE7?
TIA,
Andrei
Create a circle using a real path..
Take this code...
paper.path('M325 35a200 200 0 1 0 1 0' );
and play with it here...
http://www.irunmywebsite.com/raphael/additionalhelp.html?q=animateAlong
It turned out to be the original coordinates of the moving circle, rPath.startX in my example. It was obtained by splitting a string, therefore a string value. While the positioning of the circle worked fine, the animateAlong was not as forgiving in IE.
Parsing it to an int before using it fixed the issue.

Categories

Resources