Wednesday, July 2, 2014

D3 Zoom

D3 Zoom and Pan


There are two ways to implement D3 zooming and panning. The first method is simply apply D3 zoom behavior directly to <svg> tag and then append a <g> tag. The second method is to use two <g> tags with a <rect> overlay.

1. Apply Zoom Behavior to SVG

    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
        .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom));
 
    var group = svg.append("g");
 
    group.selectAll("circle")
        .data(data)
        .enter().append("circle")
        .attr("r", 2.5)
        .attr("transform"function (d) { return "translate(" + d + ")"; });
 
    function zoom() {
        group.attr("transform""translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    }

Notice that the Zoom behavior is not applied to the <g> tag but applied to the parent <svg> tag. Applying the Zoom to <g> tag will result in unsmooth panning. Also applying Zoom to <g> tag will result in that zoom is not triggered on the white space.

2. Apply Zoom Behavior to G tag

    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
        .append("g");
 
    svg.append("rect")
        .attr("class""overlay")
        .attr("width", width)
        .attr("height", height);
 
    svg.selectAll("circle")
        .data(data)
        .enter().append("circle")
        .attr("r", 2.5)
        .attr("transform"function (d) { return "translate(" + d + ")"; });
 
    function zoom() {
        svg.attr("transform""translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    }

Notice that the Zoom behavior is applied to the first <g> tag. The <rect> overlay is added to ensure to capture the zooming and panning event in the white space.



Cloudy JavaScript


Cloudy JavaScript - Cloud Ready JavaScript :-)


1. <Script src=''></Script>

This will load JavaScript synchronously and block the rendering of the rest content. Use this to load jQuery which is the prerequisites for the rest of the code.

Unlike any other ajax API such as $.getJSON and d3.json which is restricted to make calls to the same original domain. <Script> tag call download and execute script (jsonp script) from cross domain site.

To get the benefts of both cross domain calls and asynchronous calls, use next approach.

2.

    function loadasync(url) {
        var head = document.getElementsByTagName("head")[0]; // Find document <head>
        var s = document.createElement("script"); // Create a <script> element
        s.src = url; // Set its src attribute
        head.appendChild(s); // Insert the <script> into head
    }

This will load the JavaScript asynchronously since executing and inserting <Script> doesn't block the rendering of rest content.

An easier way is to use jQuery "getScript" call. Another bonus of using getScript call is that it doesn't leave a trace of JavaScript in the browser developer tools because it removes the <Script> after executing it. The inner working of getScript is shown here:

http://jeremyhixon.com/snippet/loading-javascript-files-asynchronously/

3. Minimize footprint of Cloudy JavaScript

To minimize the footprint of any cloudy script, use this pattern

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
    $.getScript("js/d3mapc.js");
</script>
<div class="d3svgmap"></div>

In the cloudy script, load all other script files as

        $.getScript("url1", function () {
            $.getScript("url2", function () {
                $.getScript("url3", function () {
                    $(function () {
                        ......
                        hideLoading();
                    });
                });
            });
        });