5
\$\begingroup\$

I have this code that places a marker and on mouse-over this marker is scaled out and then back to the 'original' scale:

this.drawPerson = function () {
   self.svg.append("path")
        .attr("d", personPath)
        .attr("transform", "translate(100,100)scale(0.1)")
        .attr("class", "member")
        .style("fill", "steelblue")
        .on("mouseover", function(){
            d3.select(this).transition()
                .style("fill", "red")
                .attr("transform", "translate(100,100)scale(0.2)")
            })
        .on("mouseout", function() {
            d3.select(this).transition()
                .style("fill", "steelblue")
                .attr("transform", "translate(100,100)scale(0.1)")
            });
}

The x and y are the coordinates for the position on the canvas.

Here is the example: http://jsfiddle.net/SuTZR/8/

\$\endgroup\$
  • 1
    \$\begingroup\$ What's wrong with it as it stands? Performance? \$\endgroup\$ – Matt Gibson Jul 24 '12 at 8:40
  • \$\begingroup\$ i was thinking that the mouse out could just be reset as the original, rather then having to set it again. similar to the way is done with css \$\endgroup\$ – khinester Jul 24 '12 at 9:28
  • \$\begingroup\$ Like when you mouseout of an element with css hover set to something different? I think I see what you mean, but you are doing an animated transform, which is a lot more complex than simply switching from state to state. You could use CSS hover for this if the browser supported a transform like that natively, but until CSS3 is widely adopted, it's not going to be possible. Examples here: tympanus.net/codrops/2011/11/07/animated-buttons-with-css3 \$\endgroup\$ – Matt Gibson Jul 24 '12 at 10:05
  • \$\begingroup\$ As usual, IE spoils it for the rest of us: w3schools.com/cssref/css3_browsersupport.asp \$\endgroup\$ – Matt Gibson Jul 24 '12 at 10:07
  • \$\begingroup\$ @MattGibson Since IE<9 doesn't support SVG at all, I don't think that is a problem. \$\endgroup\$ – Inkbug Jul 25 '12 at 9:17
2
\$\begingroup\$

There does not seem to be a great way of solving this.

The best I can propose is to capture the style that you will re-set to into a function and use that function both during initialization and mouseout, this makes the code DRY'er, but not necessarily nicer:

this.drawPerson = function () {

  function style( svg ){
    return svg.style("fill", "steelblue")      
              .attr("transform", "translate(100,100)scale(0.1)");
  }

   style( self.svg.append("path")
        .attr("d", personPath)
        .attr("class", "member") )
        .on("mouseover", function(){
            d3.select(this).transition()
                .style("fill", "red")
                .attr("transform", "translate(100,100)scale(0.2)")
        })
        .on("mouseout", function() {
            style( d3.select(this).transition() )
        });
}
\$\endgroup\$
1
\$\begingroup\$

Okay so this answer does not try to change your code by functionalizing anything or what have you ... but it does optimize it:

function MyClient() {
    var self = this,
        personPath = "m 1.4194515,-160.64247 c 33.5874165,0 60.8159465,-25.97005 60.8159465,-58.00952 0,-32.0404 -27.22755,-58.0114 -60.8159465,-58.0114 -33.5883965,0 -60.8159415,25.971 -60.8159415,58.0114 0,32.0404 27.228527,58.00952 60.8159415,58.00952 z m 81.9575765,26.25762 C 70.531608,-146.64352 55.269688,-153.983 0.08110256,-153.983 c -55.19742156,0 -70.08915856,7.96609 -82.28062656,19.59815 -12.197359,11.62926 -8.081167,135.7024419 -8.081167,135.7024419 L -63.292733,-59.848397 -46.325227,122.37766 2.6291765,29.116913 48.308878,122.37766 64.467298,-59.848397 91.457218,1.3175919 c 0,-8e-4 4.76917,-123.4484419 -8.08019,-135.7024419 z",
        fullCanvas = "100%",
        w = $('#canvas').width(),
        mapCanvasHeight = (w * 0.75),
        transformVal = "translate(100,100)scale(0.";

    this.init = function() {
        self.drawCanvas();
        self.drawRect();
        self.drawPerson();
    }

    this.drawCanvas = function () {
        self.svg = d3.select('#canvas')
            .append('svg:svg')
            .attr({
                width:fullCanvas,
                height:fullCanvas,
                viewBox:("0 0 " + w + " " + mapCanvasHeight)
            });
    }

    this.drawRect = function () {
        self.svg
            .append("rect")
            .attr({
                x:0,
                y:0,
                width:w,
                height:mapCanvasHeight,
                fill:"black"
            });
    }

    this.drawPerson = function () {
       self.svg
           .append("path")
           .attr({
               d:personPath,
               transform:transformVal+"1)",
               class:"member",
               fill:"steelblue"
           })
           .on({
               mouseenter:function(){
                    d3.select(this).transition()
                        .style({
                            fill:"red"
                        })
                        .attr({
                            transform:transformVal+"2)"
                        });
               },
               mouseleave:function() {
                d3.select(this).transition()
                    .style({
                        fill:"steelblue",
                    })
                    .attr({
                        transform:transformVal+"1)"
                    });
               }
           });        
    }

    this.init();
};

var MyClient;
jQuery(function() {
    MyClient = new MyClient();
});

The key here is the DOM object use of your methods:

  • style
  • attr
  • on

This will be faster for three reasons, one major and two minor:

  • Major = by consolidating your assignments of attributes and mouseenter / mouseleave into a single bind you are applying to the object once instead of many times consecutively, and as we all know the most expensive action in jQuery is DOM querying
  • Minor = changing mouseover and mouseout to mouseenter and mouseleave respectively prevents excessive firing (mouseover will fire everytime you move the mouse and it is over the object)
  • Minor = use of the DOM object rather than string means less parsing and conversion efforts for the JS compiler to perform

Here is a working jsFiddle.

I also did a couple of minor tweaks like consolidate the variables at the top, assign the "100%" value to a variable, rename your width variable (width is a native DOM property for all elements, bad idea to use it as a variable name), and set your transform value to a variable except for the 1 or 2 at the end, but these are more coding style than optimizations.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.