//A zoom widget for d3 function zoom() { var margin = {top: 10, right: 40, bottom: 150, left: 60}, width = 940 - margin.left - margin.right, height = 1024 - margin.top - margin.bottom changeZoom = function() {}; function chart(div) { div.each(function() { var div = d3.select(this), svg = div.select("svg"), zoomer = svg.select(".zoomer"), viewport = svg.select(".viewport"), pan = svg.select(".pan"), yOffset = 0, xOffset = 20, zoomlevel = 100; var viewportTranslation = function() { return "translate(" + xOffset + "," + yOffset + ")"; } var viewportScale = function() { return "scale(" + 0.01 * zoomlevel + ")"; } var viewportTransform = function() { return viewportTranslation() + "," + viewportScale(); } var applyZoomLevel = function() { svg.select(".icon").attr("transform", "translate(0," + y(zoomlevel) + ")"); svg.select(".viewport").attr("transform", viewportTransform); } changeZoom = function(d) { zoomlevel = Math.min(500, Math.max(10, zoomlevel + d)); applyZoomLevel(); } if (svg.empty()) { svg = div.append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); pan = svg.append("g").attr("class", "pan"); viewport = svg.append("g").attr("class", "viewport").attr("transform", viewportTransform()); //Translate to the right so that the pointer is not on the viewport zoomer = svg.append("g").attr("class", "zoomer"); //Create zoom thingy var y = d3.scale.linear().domain([5, 500]).range([0, height]), yAxis = d3.svg.axis() .scale(y) .orient("left"), zoomerDrag = d3.behavior.drag().origin(function(d,i) { return {x : 0, y: y(zoomlevel)};}).on("drag", dragzoom), zoomerPan = d3.behavior.drag().origin(function(d,i) { return {x: xOffset, y: yOffset }}).on("drag", dragPan); zoomer.append("rect") .attr("width", 20) .attr("height", height) .attr("fill", "#ddd") .attr("fill-opacity", "0.8") .on("click", function() { var newY = Math.min(y(500) - 10, Math.max(y(5) - 10, d3.event.y - 25));//10 is the vertical coordinate of the arrow point zoomlevel = y.invert(newY); applyZoomLevel(); }); zoomer.append("g") .attr("class", "y axis") .call(yAxis); //A simple triangle var iconPath = "M 0,10 20,20 20,0 z"; var iconGroup = zoomer.datum({}).append("g").attr("class", "icon") .attr("mouse", "move"); iconGroup.append("path") .attr("class", "pointer") .attr("d", iconPath) .attr("fill", "#444"); iconGroup .attr("transform", "translate(0," + (y(zoomlevel) - 10) + ")") .call(zoomerDrag); pan.append("rect") .attr("width", width) .attr("height", height) .attr("transform", "translate(20,0)") .attr("fill", "#fff") .call(zoomerPan); function dragzoom(d) { var newY = Math.min(y(500) - 10, Math.max(y(5) - 10, d3.event.y - 10));//10 is the vertical coordinate of the arrow point zoomlevel = y.invert(newY); applyZoomLevel(); } function dragPan(d) { xOffset = d3.event.x; yOffset = d3.event.y; svg.select(".viewport").attr("transform", viewportTransform); } }; }); } chart.height = function(h) { if (!arguments.length) return h; height = h; return chart; } chart.width = function(w) { if (!arguments.length) return w; width = w; return chart; } chart.zoom = function(d) { changeZoom(d); } return chart; }