Transition on class properties

1,657 views
Skip to first unread message

Porlus

unread,
Jun 18, 2012, 9:50:56 AM6/18/12
to d3...@googlegroups.com
Hi everyone !
I'm playing around with d3, very great stuff !

My code is becoming hard to maintain.
So, i'm trying to separate the animation code from styling part.

Typically, I would write this kind of code:

MyScript.js
function update(){
myCircle.transition().duration(1000)
.style("fill",function(){
if(toggle)
return "red";
else
return "blue";
});
toggle = 1 - toggle;
}
// an update loop...

Now, to get a separated style description, i want to write something like that:

Beautiful.css
circle.m { fill: red; }
circle.f { fill: blue; }

MyScript.js
function update(){
myCircle.transition().duration(1000)
.attr("class",function(){
if(toggle)
return "m";
else
return "f";
});
toggle = 1 - toggle;
}
// an update loop...

As we should expect, the class change toggles the color modification, but without any transition.

Is there any simple way to interpolate and easily apply a transition on all properties from two classes ?

My thanks in advance,
Porlus

Ian Johnson

unread,
Jun 18, 2012, 3:26:41 PM6/18/12
to d3...@googlegroups.com
It seems like you are trying to transition something that isn't really a transition. What it looks like you want is for something to change discretely after some amount of time. For that you can use the .end(function(d,i) { ... }) of the transition to change your class at the end of the transition.

The code you have now will continuously set the fill color to the same color until the very end. transitions don't make sense on class names (you don't want to automatically transition between two class names, you just want to switch). It comes down to the difference between continuous and discrete values, you interpolate between continuous values and you toggle between discrete ones.

Maybe I misunderstand your purpose, but does this make sense?
--
Ian Johnson

Porlus

unread,
Jun 18, 2012, 4:19:27 PM6/18/12
to d3...@googlegroups.com
Thank you for you answer.
It makes sense, but not exactly for my purpose.
I totally agree that making a transition on class is a kind of nonsense.
What i hope to find is an effective way to get a continuous transition based on class switch without to much code.

For example, let A and B be two classes with A {attribute_0:valueA_0,... ,attribute_n:valueA_n} and B {attribute_0:valueB_0,... ,attribute_n:valueB_n}.
With an object being classed first with A, i'm looking for a beautiful way to write something like:
object.transition().duration(1000).attr("class","B") // which is actually only a class switch
and get a continuous transition with the same behaviour than:
object.transition.duration(1000)
.attr("attribute_0",valueB_0)
...
.attr("attribute_n",valueB_n); // long to write and hard to maintain

With a "global" interpolation between each common attributes from the starting class to the goal class, it would be possible to separate the style description (in a separate stylesheet) from the animation code.

Le lundi 18 juin 2012 21:26:41 UTC+2, Ian Johnson a écrit :
It seems like you are trying to transition something that isn't really a transition. What it looks like you want is for something to change discretely after some amount of time. For that you can use the .end(function(d,i) { ... }) of the transition to change your class at the end of the transition.

The code you have now will continuously set the fill color to the same color until the very end. transitions don't make sense on class names (you don't want to automatically transition between two class names, you just want to switch). It comes down to the difference between continuous and discrete values, you interpolate between continuous values and you toggle between discrete ones.

Maybe I misunderstand your purpose, but does this make sense?

Ian Johnson

unread,
Jun 19, 2012, 1:59:17 AM6/19/12
to d3...@googlegroups.com
As far as I know there is no elegant way to do that at the moment. I'd love to be proved wrong though :)

Kai Chang

unread,
Jun 19, 2012, 2:03:23 AM6/19/12
to d3...@googlegroups.com
Can this be accomplished with CSS transitions?

Porlus

unread,
Jun 19, 2012, 10:43:39 AM6/19/12
to d3...@googlegroups.com
Thanks for your replies.
I have checked the CSS transitions solution:
it seems that CSS transitions on SVG are not fully supported on every browser.
Moreover, it only applies on some specific SVG attributes in the webkit based browsers.

In the end, I have produced this preliminary solution:
to sum up, it retreives all the css attributes of the target class for each element of the transition group.
Then, it applies new style attributes for each element.
A small example is given here:

The piece of code:
d3.transition.prototype.classTransition = function (className) {
// find the css rules on the className and nodename + className 
// adapted from http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript
                var getCSSRules = function(ruleName,p) {
                    var cssStyleRules = new Array();
                    var elems = ruleName.split(".");
                    if( elems && elems[0].length > 0 ){
                        var rules = getCSSRules("."+elems[1],p+1);
                        for(var i in rules){
                            cssStyleRules.push(rules[i]);
                        }
                    }
                    if(document.styleSheets){
                        var found = false;
                        for(var styleSheetNum = 0; styleSheetNum < document.styleSheets.length && !found ; styleSheetNum++){
                            var styleSheet = document.styleSheets[styleSheetNum];
                            var ruleNum = 0;
                            var cssRule=false;
                            do {
                                if (styleSheet.cssRules) {
                                    cssRule = styleSheet.cssRules[ruleNum];
                                } else {
                                    cssRule = styleSheet.rules[ruleNum];
                                }
                                if (cssRule){
                                    if (cssRule.selectorText.toLowerCase()==ruleName) {
                                        cssStyleRules.push(cssRule);
                                        found = true;
                                    }
                                }
                                ruleNum++;
                            } while (cssRule && !found)
                        }
                    }
                    return cssStyleRules;
                }
// retreive the set of style attributes
                var rulesToAttributeArray = function rulesToAttributeArray(cssStyleRules) {
                    var attributes = {};
                    for(var cssStyleRule in cssStyleRules){
                        var attribute = true;
                        for(var i = 0;attribute;i++){
                            attribute = cssStyleRules[cssStyleRule].style[i];
                            if(attribute && !attributes.attribute){
                                attributes[attribute] = cssStyleRules[cssStyleRule].style[attribute];
                            }
                        }
                    }
                    return attributes
                };
                
// if a function is given -- more tests should be required here
                if(typeof className === 'function'){
                    className = className.call(this);
                }
// apply on each element of the transition group
                this.each(function(){
                    var localClassName = this.nodeName+"."+className;
                    var rules = rulesToAttributeArray(getCSSRules(localClassName,0));
   // get the attributes and apply them
                    for(var attribute in rules){
                        d3.select(this).transition().style(attribute,rules[attribute]);
                    }
                });
                return this;
            }
// a continuous transition on a class switch
circle.transition()
                .duration(5000)
                .classTransition(function(){
                    if(draw){
Reply all
Reply to author
Forward
0 new messages