Tricky raphael svg animation - javascript

I've created a 'donut' chart originally from this jsfiddle, using raphael.
I have tweaked this script to suit my needs and currently have this being rendered.
My aim is to animate each slice (at the same time); for example make the blue slice grow to 60%; and the red slice shrink to 40%.
I have been able to redraw the slices by removing the existing one and quickly re-rendering a new one with adjusted values (e.g. 51, 49). But the problem here is that it is instant.
My question is,
(a) Can I animate this without the need to redraw the object (and how)?
(b) If not, how I can animate this effect using a redraw logic?

Yes. There is an example of doing this very thing on the Raphael demos page where you got the pie chart. See the Growing Pie demo.
You should separate the code in which you generate the path into a standalone function so you can use it later to return new paths. In order to use animate(), you'll need to define a function on the customAttributes object; it should return (at least) an object with the path property set to your slice's new path.
Since you have labels, you'll probably want to modify the code such that the pie slices expand/shrink relative to their center, so that you don't have to move the labels, too, since the labels are centered on their slice's "axis."
Update
Here's a JSFiddle with a simple example, pretty much the same as Dmitri's Growing Pie demo, except more like your chart. I export a setValue() method to change slice sizes and call it when the page loads. See his blog post about adding customAttributes, too.
In my last paragraph above, I was off the mark a bit. Your chart wasn't the one with labels; I had them mixed up. Also, it would be harder to keep slices centered, so I didn't do that after all. The animate() function sets each segment to its new starting and ending points on the circle, and Raphael figures out the intermediate points. As you can see, you can pass multiple arguments in an array.
this.customAttributes.slice = function(a0, a1) { /*...*/ }
// ...
chart.push(paper.path().attr({slice:[0, Math.PI/2 ]})

Can't see all the fiddle because I'm on iPod however it sounds like you need to have an animate call inside a function that you will need to write
Use the callback parameter that calls the function it sits inside.
Code your recursively called function so it eventually completes when all the work is done.
Each call to the function will happen at the end of every elapssed time interval you specify...

Related

Why is this.stop() changing the z-index I set with setChildIndex() in Adobe Animate?

I've been having difficulty getting the expected results with setChildIndex().
In this example I have 2 MovieClip instances named redDot and yellowDot, and a black square Shape. I would expect it to place yellowDot on bottom, then square, then redDot.
//make black rectangle shape
var square = new createjs.Shape(new createjs.Graphics().f("#000000").dr(100,100,100,100));
this.addChild(square);
this.setChildIndex(this.yellowDot, 0); //set z-index towards background
this.setChildIndex(square,1);
this.setChildIndex(this.redDot, 2);//towards foreground
Instead I get redDot, yellowDot, then square. Adding this.stop() to the end seems to change it back to the expected order. It's not clear to me what is causing this discrepancy. Looping is disabled in the publish settings. Am I misunderstanding how this function and the Animate timeline work?
I wonder if the stage is not being updated? What happens if you use stage.update() at the end of your code. (or however you update the stage in an animate script).
I believe calling this.stop() is re-rendering the initial state of the clip, which uses the original z-index definition. Probably makes sense to call it before you change the contents programmatically.

Highcharts Column Graph with background color?

I'm trying to make a column graph in Highcharts and it's not working out too hot. I'm essentially trying to give the column a background color that the data point can overlay. I want the effect of stacking without actually stacking if that makes sense. Any thoughts on what I can do?
Essentially I want it to look like a progress bar... there should be a distinct coloring between the top of the bar and the top of the graph itself. I've already tried stacking: percentage, but to no avail. Thoughts?
There are three options that come to mind:
1) To achieve the 'effect' of stacking, actually stack it: use a dummy series with a color set to the background color that you want, and set with values to fill the gap from the actual data.
Example:
http://jsfiddle.net/jlbriggs/Zf8C7/5/
[[edit for questions
The order of the series does not matter for this to work, and neither does the legendIndex.
What does matter, if you are using multiple series, is the stack property for each series - http://api.highcharts.com/highcharts#series.stack
Each group will need its own stack, and the dummy series and real series within each group need to be assigned to the same stack.
In addition, you need to have one overall max value.
Updated example:
http://jsfiddle.net/jlbriggs/4d9tm6b8/4/
(keep in mind, there are much smoother methods to process and build your data - this is a quick and dirty example whose purpose is to show how to properly use the stack property to do what you need)
]]
2) plotBands, as mentioned above
3) using the alternateGridColor property and set your data to skip an x axis
value between each point.
Reference:
- http://api.highcharts.com/highcharts#xAxis.alternateGridColor

Javascript & Canvas: Draw and delete lines to create a "breathing" circle

I would like to create an element, that shows a red circle. Once the user clicks on it, she can record her voice. In order to show the LIVE mode, I'd like to make the circle "breath" according to the incoming frequencies.
I'm experimenting with a <canvas> element. That means it creates a circle that gets bigger and smaller, depending on the variable arcrad. However, the lines are being drawn correctly, but they do not disappear afterwards. I tried to apply .clip() but can't get it to work...
if (arcrad <= 10) arcrad = 10;
analyserContext.beginPath();
analyserContext.arc(100,120,arcrad,0,2*Math.PI);
analyserContext.closePath();
analyserContext.lineWidth = 2;
analyserContext.strokeStyle = 'red';
analyserContext.stroke();
Any ideas - or completely different strategies for this use case?
Canvas will overdraw by default. For your animation you’ll need to clean the canvas at the start of each frame. Use something the following at the start of your drawing function:
analyserContext.clearRect(0,0,200,200);
assuming your canvas is 200 pixels wide and high. It’s worth pointing out that sometimes you don’t want to completely clear the animation field every frame. For example, if you were to draw a semi transparent rectangle over the frame at the beginning (instead of clearing it) then you’d end up with a basic ‘bullet time’ style effect.
It's a normal behavior. Once something it's drawn on the canvas, it's there forever. You have to think like if you were painting something: what has been done cannot be undone.
Luckily, you still have solutions:
1) redraw another circle on top of the first one with the background color. It's really not the recommend way, but it still can be useful
2) use clearRect method (see How to clear the canvas for redrawing)
There are numerous ways to clear a canvas pre drawing to create animation:
How to clear the canvas for redrawing
simplest in my mind:
canvas.width=canvas.width;
though can equally use clearRect (which is actually quicker and won't reset the entire canvas if that is an issue regarding transforms etc!) over the region or whole canvas.
Get the likes of:
http://jsfiddle.net/dw17jxee/

css3 matrix3d value applied using jQuery not taking

I'm experimenting with rotating/animating an environment using CSS3 and javascript. So I have a cube object with faces, which lives inside a "rotator" object, which is the object that gets interacted with in the UI. I want to make it so that when you click a face, it appends it to a div outside of the rotator div, but applies the transformation matrices to the object from its parents, so that it appears with the same orientation on the outside of the "rotator" as it did inside, but can be animated and become independent of the rest of the objects in the "rotator" div.
Here is the function for the click event that should do this:
This depends on several custom functions, please see the fiddle for full code and reference to functions.
jQuery('#top').on('click',function(){
if(jQuery(this).parent().is('#cube')){ // only do this if it isn't already detached
M = jQuery(this).getTransMatrix(); // get the local transform of this
M = M.multiply(jQuery('#rotator').getTransMatrix()); // multiply it by the rotator's transform to get the final transform
jQuery(this).appendTo('#container'); // move this to the container div outside of the rotator
jQuery(this).css(CSS3.prefix+'transition','none'); // make sure that style changes, etc happen instantly
// wtf here
console.log(M.cssTransformString()); // just to check, we see the transform string before it is assigned. It's what it should be in my tests
jQuery(this).css(CSS3.prefix+'transform',M.cssTransformString()); // we apply the string. The wrong transformation results. If I double-check it from the console, it is NOT the string from the previous line
}
});
If you check out the fiddle, you will be able to drag around to rotate the cube. Clicking on the face will have some effect. The top face with the star is the one in question. When clicked, simply does not apply the transformation. Even if I log to console the transform string it's about to apply, the next operation does NOT result in that string being applied. It simply does not stick. HOWEVER, if I click and drag slightly, then release with the cursor still on the face, THEN the transform sticks. I have tried tracing virtually everything and I cannot figure out why the transform is only sticking if you click normally (without holding/moving at all)
Fiddle here:
http://jsfiddle.net/spaceboycoop/PWkB9/

Interactions that affect multiple separate charts in d3.js?

I'm trying to create a data visualization in d3.js that contains two charts: a parallel-axis plot, and horizontal colorbar chart (I just made up that name, but it's basically a series of colored rectangles). Each line in the parallel-axis plot is associated with a set of rectangles in the colorbar chart.
Right now, mousing over a given line highlights that line, and mousing over a given rectangle highlights that set of rectangles. My goal is to also highlight the associated line or set of rectangles on the opposite chart anytime the user mouses over either chart. This seems like it would be pretty straightforward if I generated both charts with the same function. However, it would be much neater (and more reusable) coding style to give each chart its own function and just connect them somehow. I tried having each within-chart mouseover function call a function defined at a higher level that affected both charts, but this didn't seem to have any effect on the chart that wasn't moused-over. Since I still don't feel like I fully understand how d3.js works on an underlying level, I'd really like to have confirmation that this is a viable way to set up my code. My code is long and complicated, and I really just want advice on the structure, so here is the basic outline:
function chart1(){
make chart
function mouseover(d,i){
do stuff
chart1_globalmouseover(d,i);
}
chartElement.on("mouseover", function(d,i){mouseover(d,i)});
}
function chart2(){
make chart
function mouseover(d,i){
do stuff
chart2_globalmouseover(d,i);
}
chartElement.on("mouseover", function(d,i){mouseover(d,i)});
}
function chart1_globalmouseover(d,i){
do stuff in chart 2's mouseover function
}
function chart2_globalmouseover(d,i){
do stuff in chart 1's mouseover function
}
c1 = chart1();
c2 = chart2();
One way to link the two graphs independent of the code used to create them would be to assign IDs or classes to the elements you may want to select. That is, if graph 2 has an element with ID foo, then in a mouse handler for an element of graph 1, you could say d3.select("#foo").style("stroke", "red") for example. Similarly with classes.
This approach allows you to keep the code completely separate. Moreover, if you use classes, you can assign the same class to things you would want to highlight together (e.g. elements representing the same data). Then d3.selectAll(".class") would select and allow you to manipulate all of them. This would work for an arbitrary number of graphs, not just two -- what changes is simply the number of elements that will be selected.

Categories

Resources