I have revisited the time lapse or motion chart in Dex and I am pretty pleased with the results. Previously, the time lapse chart looked like:
While the new charts look like:
There is no real change in functionality, but man, what a difference a gradient fill can make in appearance.
Mouseover is also much more informative. Try it out for yourself here:
D3 Magic
The D3 magic happens in a little function called gradient:
function gradient(baseColor) { var gradientId = "gradient" + baseColor.substring(1) //var lightColor = shadeColor(baseColor, -10) var darkColor = shadeColor(baseColor, -20) var grad = d3.select("#gradients").selectAll("#" + gradientId) .data([ gradientId ]) .enter() .append("radialGradient") .attr("id", gradientId) .attr("gradientUnits", "objectBoundingBox") .attr("fx", "30%") .attr("fy", "30%") grad.append("stop") .attr("offset", "0%") .attr("style", "stop-color:#FFFFFF") // Middle grad.append("stop") .attr("offset", "40%") .attr("style", "stop-color:" + baseColor) // Outer Edges grad.append("stop") .attr("offset", "100%") .attr("style", "stop-color:" + darkColor) return "url(#" + gradientId + ")"; }
This routine will take a base color in the form of #RRGGBB and calculate a gradient out of this and stuffs it into the HTML document. The nice thing here is that you can use any D3 color scheme you desire and the gradients will auto-calculate. Gradients consist of a white core which transitions to it’s base color outward to a darker shade (by 20%) of that color.
The effect looks pretty three dimensional.
The following code must be applied to the circle or other object in need of radial shading:
var colorScale = d3.scale.category20c(); ... .style("fill", function (d) { return gradient(colorScale(color(d))) })
The RGB lightening and darkening routine being borrowed from a question on StackOverflow:
function shadeColor(color, percent) { var R = parseInt(color.substring(1,3),16) var G = parseInt(color.substring(3,5),16) var B = parseInt(color.substring(5,7),16); R = parseInt(R * (100 + percent) / 100); G = parseInt(G * (100 + percent) / 100); B = parseInt(B * (100 + percent) / 100); R = (R<255)?R:255; G = (G<255)?G:255; B = (B<255)?B:255; var RR = ((R.toString(16).length==1)?"0"+R.toString(16):R.toString(16)); var GG = ((G.toString(16).length==1)?"0"+G.toString(16):G.toString(16)); var BB = ((B.toString(16).length==1)?"0"+B.toString(16):B.toString(16)); return "#"+RR+GG+BB; }
Happy Holidays!
Hi Patrick, #2 is a fantastic graph, but the graphics hide the labelling on the Y axis. Can you write the code so that it checks the overlapping and moves the labelling to the outside of the graph? It makes for difficult reading.
The other suggestion would be to bring up in the foreground the “sphere” when you over with the mouse.
Best,
Thanks for the input Corrado. I agree with you — all very good ideas. I’ll need to lay the labels out differently. Moving the active sphere to foreground could be frustrating as larger spheres could obscure smaller ones. ie: you can’t get to the smaller circle because you hovered over a larger one which is behind the smaller one.
Maybe we keep the behavior as is and if the user clicks on the sphere it moves to foreground with a 2nd click putting it back where it was.
Also, intend to make all of the layout fully interactive so you can move the labels out of the way and make configurable with the changes being persisted.
There are so many components, I am making the rounds and improving things incrementally across all of them.
– Pat
Great suggestions! I am looking to add the motion chart capability to Dex Charts with these types of enhancements. Dex, in turn, will inherit these capabilities.
– Pat