How to use a Matrix with RaphaelJS - javascript

I would like to move a RaphaelElement and created a simple test case:
var paper = Raphael(0, 0, 500, 500);
var rect = paper.rect(50, 50, 100, 100);
rect.attr("fill", "blue");
rect.matrix.translate(300, 300);
But the rectangle isn't moved. At first I thought that for the matrix was probably not updated correctly and tried this:
//...
alert("" + rect.matrix.x(0,0)); // prints 0
rect.matrix.translate(300, 300);
alert("" + rect.matrix.x(0,0)); //prints 300
Obviously the matrix is changed but the rectangle does not care about that. Therefore I changed my code to:
rect.matrix=rect.matrix.translate(300,300);
But that either crashes the program or has no effect at all.
It seems like I'm missing some sort of update method, to apply a matrix to a RaphaelElement. Something that looks like this:
rect.updateMatrix();
I've searched in the documentation but didn't find such a method. What is the canonical usage of matrices in RaphaelJs?

rect.translate(300, 300) will apply a translation or alternatively rect.transform("t300,300")
Also
rect.transform(['m',mat.a, mat.b, mat.c, mat.d, mat.e, mat.f]);
can be used to apply an existing matrix (mat) to the rect.

I found another reference on Stackoverflow that does an awesome job explaining the matrix. It solved my problem.
StackOverflow Matrix clarification

Related

EaselJS: Update shape color on property change

I am writing a simple game in JS using EaselJS. I try to keep everything object-oriented to keep in sync state of game, state of EaselJS objects and what is displayed on my canvas. I would like to have possibility to change stroke color of shapes that are displayed on canvas by changing their attributes. I've found in docs append() here, but I can't get it working.
Here's what I've achieved so far:
Shape definition:
var bLetter = new createjs.Shape();
bLetter.graphics.append({exec: setPoweredState});
bLetter.graphics.moveTo(0, 0)
.lineTo(0, segSize * 2)
.arc(circSize, segSize * 2, circSize, Math.PI, Math.PI + 0.0001, true);
setPoweredState function - When i set bLetter.powered = true, the line color should change:
setPoweredState = function(ctx, shape) {
if(shape.powered) {
shape.color = consts.COLOR_LINE_ACTIVE;
} else {
shape.color = consts.COLOR_LINE_INACTIVE;
}
shape.graphics.beginStroke(shape.color).setStrokeStyle(consts.STROKE_SIZE, 'round', 'round');
}
When I set bLetter.powered = true and I check bLetter.color it seems that the function is executed - the bLetter.color property changes. However, the bLetter object on canvas is not updated. What's more, it's not drawn at all - probably I am using append() in incorrect way. What am I missing?
BTW: I omit the code with initializing createjs.Stage on canvas and adding bLetter as it's child, I don't think it's an issue, the shape is drawn correctly, only color won't change.
The issue is that you are continually appending new stroke / strokestyle commands to the end of the graphics queue, so they have no effect on the preceding paths.
The easiest approach in my opinion would be to save off the command as Lanny suggested, and modify its style in your setPoweredState method.
Here's an example of the above, implement with minimal modifications to your approach:
https://jsfiddle.net/u4o4hahw/
Instead of using append, consider just storing off commands to modify.
Here is another question that I answered outlining how it works: Injecting a new stroke color into a shape, and here is a blog post: http://blog.createjs.com/new-command-approach-to-easeljs-graphics/
Basically, you can store any command, then change its properties later:
var cmd = shape.graphics.beginFill("red").command;
shape.graphics.otherInstructionsHere();
// Later
cmd.style = "blue";
Here is a link to the docs on the Fill command used in the sample code: http://createjs.com/docs/easeljs/classes/Graphics.Fill.html

RectangularRangeIndicator format like triangular using dojo

I'm trying to add an indicator to my vertical gauge but the format of the indicator is like a triangle, but I want it a large line.
This is JSFIDDLE example.
The code :
require(['dojo/parser', 'dojox/dgauges/components/default/VerticalLinearGauge','dojox/dgauges/RectangularRangeIndicator', 'dojo/domReady!'], function (parser, VerticalLinearGauge) {
parser.parse();
var ri = new dojox.dgauges.RectangularRangeIndicator();
ri.set("start",0 );
ri.set("value", 30);
ri.set("startThickness",100);
ri.set("endThickness",100);
gauge.getElement("scale").addIndicator("ri", ri, false);
});
This is a bug, it will be fixed in next releases (1.8.x and 1.9).
See https://github.com/dmandrioli/dgauges/issues/15
As a workaround, you can redefine the bad method like this:
myRangeIndicator._defaultVerticalShapeFunc =
function(indicator, group, scale, startX,
startY, endPosition, startThickness, endThickness,
fill, stroke){ ... }
See this workaround in action at http://jsfiddle.net/BFPuL/5/
I think that the properties are startThickness and endThickness (no "stroke" at the end). However, I still don't get one solid line when setting these properties to equal values like one would expect. It seems as though something odd (perhaps a bug) is happening with the way that the startThickness property is handled.
This problem has me interested, so I'll try to set aside some time later to dig into the source to see what the real issue is, but for now I can offer you a dirty workaround. The RectangularRangeIndicator is drawn using the dojox/gfx module, and the outline of the indicator drawn is controlled by the stroke property. So, if you want, you can do something like:
var ri = new dojox.dgauges.RectangularRangeIndicator();
ri.set("start",0 );
ri.set("value", 30);
ri.set("startThickness", 0);
ri.set("endThickness", 0);
ri.set("stroke", {color: "red", width: 2.5});
ri.set("paddingLeft", 7); // Default is 10, set this to whatever works for you.
This will appear to draw straight line (which is really the border of an extremely thin shape). Check out how it looks in a real example. Again, I understand that this is not the best solution since it is more of a dirty trick, but it is a way around what appears to be a bug in the code that renders a range indicator.

How can I make Raphael.js elements "wiggle" on the canvas?

I'm working on a project that uses SVG with Raphael.js. One component is a group of circles, each of which "wiggles" around randomly - that is, slowly moves along the x and y axes a small amount, and in random directions. Think of it like putting a marble on your palm and shaking your palm around slowly.
Is anyone aware of a Raphael.js plugin or code example that already accomplishes something like this? I'm not terribly particular about the effect - it just needs to be subtle/smooth and continuous.
If I need to create something on my own, do you have any suggestions for how I might go about it? My initial idea is along these lines:
Draw a circle on the canvas.
Start a loop that:
Randomly finds x and y coordinates within some circular boundary anchored on the circle's center point.
Animates the circle from its current location to those coordinates over a random time interval, using in/out easing to smooth the effect.
My concern is that this might look too mechanical - i.e., I assume it will look more like the circle is tracing a star pattern, or having a a seizure, or something like that. Ideally it would curve smoothly through the random points that it generates, but that seems far more complex.
If you can recommend any other code (preferably JavaScript) that I could adapt, that would be great too - e.g., a jQuery plugin or the like. I found one named jquery-wiggle, but that seems to only work along one axis.
Thanks in advance for any advice!
Something like the following could do it:
var paper = Raphael('canvas', 300, 300);
var circle_count = 40;
var wbound = 10; // how far an element can wiggle.
var circleholder = paper.set();
function rdm(from, to){
return Math.floor(Math.random() * (to - from + 1) + from);
}
// add a wiggle method to elements
Raphael.el.wiggle = function() {
var newcx = this.attrs.origCx + rdm(-wbound, wbound);
var newcy = this.attrs.origCy + rdm(-wbound, wbound);
this.animate({cx: newcx, cy: newcy}, 500, '<');
}
// draw our circles
// hackish: setting circle.attrs.origCx
for (var i=0;i<circle_count;i++) {
var cx = rdm(0, 280);
var cy = rdm(0, 280);
var rad = rdm(0, 15);
var circle = paper.circle(cx, cy, rad);
circle.attrs.origCx = cx;
circle.attrs.origCy = cy;
circleholder.push(circle);
}
// loop over all circles and wiggle
function wiggleall() {
for (var i=0;i<circleholder.length;i++) {
circleholder[i].wiggle();
}
}
// call wiggleAll every second
setInterval(function() {wiggleall()}, 1000);
http://jsfiddle.net/UDWW6/1/
Changing the easing, and delays between certain things happening should at least help in making things look a little more natural. Hope that helps.
You can accomplish a similar effect by extending Raphael's default easing formulas:
Raphael.easing_formulas["wiggle"] = function(n) { return Math.random() * 5 };
[shape].animate({transform:"T1,1"}, 500, "wiggle", function(e) {
this.transform("T0,0");
});
Easing functions take a ratio of time elapsed to total time and manipulate it. The returned value is applied to the properties being animated.
This easing function ignores n and returns a random value. You can create any wiggle you like by playing with the return formula.
A callback function is necessary if you want the shape to end up back where it began, since applying a transformation that does not move the shape does not produce an animation. You'll probably have to alter the transformation values.
Hope this is useful!
There is a very good set of easing effects available in Raphael.
Here's a random set of circles that are "given" bounce easing.
Dynamically add animation to objects
The full range of easing effects can be found here. You can play around with them and reference the latest documentation at the same time.
Putting calls in a loop is not the thing to do, though. Use callbacks, which are readily available.

How to draw a vector path progressively? (Raphael.js)

How to animate a vector path like it's being drawn, progressively? In other words, slowly show the path pixel by pixel.
I'm using Raphaël.js, but if your answer is not library specific—like maybe there's some general programming pattern for doing that kind of thing (I'm fairly new to vector animation)—it's welcome!
It's easy to do with straight paths, as easy as an example on that page::
path("M114 253").animate({path: "M114 253 L 234 253"});
But try to change code on that page, say, this way::
path("M114 26").animate({path: "M114 26 C 24 23 234 253 234 253"});
And you'll see what I mean. Path is certainly animated from it initial state (point "M114 26") to the end state (curve "C 24 23 234 253 234 253" starting on point "M114 26"), but not in a way specified in question, not like it's being drawn.
I don't see how animateAlong can do that. It can animate an object along a path, but how can I make this path to gradually show itself while object is being animated along it?
The solution?
(Via peteorpeter's answer.)
Seems like currently the best way to do it is via 'fake' dashes using raw SVG. For the explanation see this demo or this document, page 4.
How produce progressive drawing?
We have to use stroke-dasharray and stroke-dashoffset and know length of curve to draw.
This code draw nothing on screen for circle, ellipse, polyline, polygone or path:
<[element] style="stroke-dasharray:[curve_length],[curve_length]; stroke-dashoffset:[curve_length]"/>
If in animate element stroke-dashoffset decrease to 0, we get progressive drawing of curve.
<circle cx="200" cy="200" r="115"
style="fill:none; stroke:blue; stroke-dasharray:723,723; stroke-dashoffset:723">
<animate begin="0" attributeName="stroke-dashoffset"
from="723" to="0" dur="5s" fill="freeze"/>
</circle>
If you know a better way, please leave an answer.
Update (26 Apr. 2012): Found an example that illustrates the idea well, see Animated Bézier Curves.
Maybe someone is searching for an answer, like me for two days now:
// Draw a path and hide it:
var root = paper.path('M0 50L30 50Q100 100 50 50').hide();
var length = root.getTotalLength();
// Setup your animation (in my case jQuery):
element.animate({ 'to': 1 }, {
duration: 500,
step: function(pos, fx) {
var offset = length * fx.pos;
var subpath = root.getSubpath(0, offset);
paper.clear();
paper.path(subpath);
}
});
That did the trick for me, only by using RaphaelJS methods.
Here is a jsFiddle example as requested in the comments, http://jsfiddle.net/eA8bj/
Eureka! (Maybe - assuming you're comfortable stepping outside the friendly realm of Raphael into pure SVG land...)
You can use SVG keyTimes and keySplines.
Here's a working example:
http://www.carto.net/svg/samples/animated_bustrack.shtml
...and here's some potentially useful explanation:
http://msdn.microsoft.com/en-us/library/ms533119(v=vs.85).aspx
I'd like to offer an alternative, Raphael+JS-only solution that I have made substantial use of in my own work. It has several advantages over davidenke's solution:
Doesn't clear the paper with each cycle, allowing the animated path to coexist nicely with other elements;
Reuses a single path with Raphael's own progressive animation, making for smoother animations;
Substantially less resource intensive.
Here's the method (which could quite easily be retooled into an extension):
function drawpath( canvas, pathstr, duration, attr, callback )
{
var guide_path = canvas.path( pathstr ).attr( { stroke: "none", fill: "none" } );
var path = canvas.path( guide_path.getSubpath( 0, 1 ) ).attr( attr );
var total_length = guide_path.getTotalLength( guide_path );
var last_point = guide_path.getPointAtLength( 0 );
var start_time = new Date().getTime();
var interval_length = 50;
var result = path;
var interval_id = setInterval( function()
{
var elapsed_time = new Date().getTime() - start_time;
var this_length = elapsed_time / duration * total_length;
var subpathstr = guide_path.getSubpath( 0, this_length );
attr.path = subpathstr;
path.animate( attr, interval_length );
if ( elapsed_time >= duration )
{
clearInterval( interval_id );
if ( callback != undefined ) callback();
guide_path.remove();
}
}, interval_length );
return result;
}
And here are two samples of its usage on my site: one for Path Transformation, and the other for Progressive Lettering.
I've created a script for this: Scribble.js, based on this great dasharray/dashoffset technique.
Just instantiate it overs a bunch of SVG <path>s:
var scribble = new Scribble(paths, {duration: 3000});
scribble.erase();
scribble.draw(function () {
// done
});
--
NB: Full USAGE code here: https://gist.github.com/abernier/e082a201b0865de1a41f#file-index-html-L31
Enjoy ;)
Using "pathLength" attribute we can set virtual length to the path. From then we can use this virtual length in "stroke-dasharray".
So if we set "pathLength" to 100 units we then can set "stroke-dasharray" to "50,50" wich wuld be exactly 50%, 50% of the path!
There is one problem with this approach: the only browser that supports this attribute is Opera 11.
Here is example of smooth curve drawind animation without javascript or hardcoded length.(Works properly only in Opera 11)
Anton & Peteorpeter's solution sadly breaks down in Chrome when paths get complicated. It's fine for the bus map in that linked demo. Check out this animated "flower petals" jsfiddle I created, which draws correctly in FF10 and Safari5, but flickers uncontrollably in Chrome:
http://jsfiddle.net/VjMvz/
(This is all HTML and inline SVG, no javascript.)
I'm still looking for a non-Flash solution for this. AnimateAlong obviously won't cut it for what I'm doing. Raphael.js could work, though it threatens to turn into callback spaghetti really fast.
Davidenke, can you post a working jsfiddle with your solution? I just can't get it to work. I'm getting an error in Chrome 18 that nodes that are set to "display: none" with your ".hide" have no method 'getTotalLength'.
Unfortunately, as you seem to agree, you probably can't do this elegantly in Raphael.
However, if, by some stroke of %deity% you don't need to support IE for this particular feature, you could forgo the Raphael API and manipulate the SVG directly. Then, perhaps, you could rig a mask to ride along the path and reveal the line at a natural pace.
You could degrade gracefully in IE to simply show the path using Raphael, without animation.
i was just doing exactly this. The first thing i tried was Anton's solution but the performance sucks.
In the end the easiest way to get the result i wanted was to use the alternative "keyframe" syntax for the animate function.
draw the final path invisibly, then generate a whole bunch of key frames by using getSubpath in a loop.
create a new path that is visible and equals the first keyframe.
then do something like:
path.anmimate({ keyFrameObject, timeframe });
you shouldn't need a keyframe for every pixel that you want to draw. After playing around with the parameters, i found that a value of 100px per keyframe worked for the complexity/size of what i was trying to "draw"
Just an update to this, you could try Lazy Line Painter
Have you tried Raphael's animateAlong? You can see it in action on a demo page.
Alright, here's my thoughts on this… The solution's too far from ideal.
To gradually show the path mean we should show it, like, dot by dot. And vector paths consist not of dots, but of curves, so it appears to me there's no ‘natural’ way to gradually ‘draw’ the path in vector graphics. (Though I'm fairly new to this and may be mistaken.)
The only way would be to somehow convert a path to a number of dots and show them one by one.
Currently my workaround is to draw a path, make it invisible, break it into a number of subpaths, and show that subpaths one by one.
This isn't hard to do with Raphael, but it's not elegant either, and quite slow on a large paths. Not accepting my answer, hoping there's a better way…

Question about the javascript-canvas object (save, transform, restore)

I've been playing around with canvas a lot lately. Now I am trying to build a little UI-library, here is a demo to a simple list (Note: Use your arrow keys, Chrome/Firefox only)
As you can tell, the performance is kinda bad - this is because I delete and redraw every item on every frame:
this.drawItems = function(){
this.clear();
if(this.current_scroll_pos != this.scroll_pos){
setTimeout(function(me) { me.anim(); }, 20, this);
}
for (var i in this.list_items){
var pos = this.current_scroll_pos + i*35;
if(pos > -35 && pos < this.height){
if(i == this.selected){
this.ctx.fillStyle = '#000';
this.ctx.fillText (this.list_items[i].title, 5, pos);
this.ctx.fillStyle = '#999';
} else {
this.ctx.fillText (this.list_items[i].title, 5, pos);
}
}
}
}
I know there must be better ways to do this, like via save() and transform() but I can't wrap my head around the whole idea - I can only save the whole canvas, transform it a bit and restore the whole canvas. The information and real-life examples on this specific topic are also pretty rare, maybe someone here can push me in the right direction.
One thing you could try to speed up drawing is:
Create another canvas element (c2)
Render your text to c2
Draw c2 in the initial canvas with the transform you want, simply using drawImage
drawImage takes a canvas as well as image elements.
Ok, I think I got it. HTML5 canvas uses a technique called "immediate mode" for drawing, this means that the screen is meant to be constantly redrawn. What sounds odd (and slow) first is actually a big advantage for stuff like GPU-acceleration, also it is a pretty common technique found in opengl or sdl. A bit more information here:
http://www.8bitrocket.com/2010/5/15/HTML-5-Canvas-Creating-Gaudy-Text-Animations-Just-Like-Flash-sort-of/
So the redrawing of every label in every frame is totally OK, I guess.

Categories