UNPKG

hansight-datamap

Version:

Data of world map by hansight-llp

287 lines (225 loc) 7.13 kB
(function() { // Utility function to extend Data Map Zoom functionality function Zoom(args) { $.extend(this, { $buttons: $(".zoom-button"), $info: $("#zoom-info"), scale: { max: 50, currentShift: 0 }, $container: args.$container, datamap: args.datamap }); this.init(); } Zoom.prototype.init = function() { var paths = this.datamap.svg.selectAll("path"), subunits = this.datamap.svg.selectAll(".datamaps-subunit"); // preserve stroke thickness paths.attr("vector-effect", "non-scaling-stroke"); // disable click on drag end subunits.call( d3.behavior.drag().on("dragend", function() { d3.event.sourceEvent.stopPropagation(); }) ); this.scale.set = this._getScalesArray(); this.d3Zoom = d3.behavior.zoom().scaleExtent([1, this.scale.max]); this._displayPercentage(1); this._shift('in'); this.listen(); }; Zoom.prototype.listen = function() { this.$buttons.off("click").on("click", this._handleClick.bind(this)); this.datamap.svg .call(this.d3Zoom.on("zoom", this._handleScroll.bind(this))) .on("dblclick.zoom", null); // disable zoom on double-click }; Zoom.prototype.reset = function() { this._shift("reset"); }; Zoom.prototype._handleScroll = function() { var translate = d3.event.translate, scale = d3.event.scale, // limited = this._bound(translate, scale); limited = { translate: translate, scale: scale }; this.scrolled = true; // console.debug('Scrolling Map . . .: '); // console.debug(limited); // console.debug(scale); this._update(limited.translate, limited.scale); }; Zoom.prototype._handleClick = function(event) { var direction = $(event.target).data("zoom"); this._shift(direction); }; Zoom.prototype._shift = function(direction) { var center = [this.$container.width() / 2, this.$container.height() / 2], translate = this.d3Zoom.translate(), translate0 = [], l = [], view = { x: translate[0], y: translate[1], k: this.d3Zoom.scale() }, target = {}, bounded; // console.debug('Beginning shifting map ...'); // console.debug(view); if (direction == "reset") { view.x = -300/1000 * this.$container.width(); view.y = 0; view.k = 1; target.k = 2.5; translate0 = [ (center[0] - view.x) / view.k, (center[1] - view.y) / view.k ]; view.k = target.k; l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y]; view.x += center[0] - l[0]; view.y += center[1] - l[1]; target.x = view.x; target.y = view.y; this.scrolled = true; } else if (direction == "whole") { target.x = 0; target.y = 0; target.k = 1; this.scrolled = true; } else { target.k = this._getNextScale(direction); // debugger translate0 = [ (center[0] - view.x) / view.k, (center[1] - view.y) / view.k ]; view.k = target.k; l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y]; view.x += center[0] - l[0]; view.y += center[1] - l[1]; target.x = view.x; target.y = view.y; // bounded = this._bound([view.x, view.y], view.k); } // console.debug('Shifting map ...'); // console.debug(view); bounded = { translate: [target.x, target.y], scale: target.k }; // console.debug(bounded); this._animate(bounded.translate, bounded.scale); }; Zoom.prototype._bound = function(translate, scale) { var width = this.$container.width(), height = this.$container.height(); translate[0] = Math.min( (width / height) * (scale - 1), Math.max(width * (1 - scale), translate[0]) ); translate[1] = Math.min(0, Math.max(height * (1 - scale), translate[1])); return { translate: translate, scale: scale }; }; Zoom.prototype._update = function(translate, scale) { // console.debug('Updating Map . . .: '); // console.debug(translate); // console.debug(scale); this.datamap.svg.selectAll("path").attr("vector-effect", "non-scaling-stroke"); this.d3Zoom .translate(translate) .scale(scale); this.datamap.svg.selectAll("g") .attr("transform", "translate(" + translate + ")scale(" + scale + ")"); this._displayPercentage(scale); }; Zoom.prototype._animate = function(translate, scale) { // console.debug('Animating Map . . .: '); // console.debug(translate); // console.debug(scale); var _this = this, d3Zoom = this.d3Zoom; d3.transition().duration(350).tween("zoom", function() { var iTranslate = d3.interpolate(d3Zoom.translate(), translate), iScale = d3.interpolate(d3Zoom.scale(), scale); return function(t) { _this._update(iTranslate(t), iScale(t)); }; }); }; // caculate the displayed scale persentage based on current scale Zoom.prototype._displayPercentage = function(scale) { var value; value = Math.round(Math.log(scale) / Math.log(this.scale.max) * 100); this.$info.text(value + "%"); }; // caculate a scale array from 1 to this.scale.max in 10 steps Zoom.prototype._getScalesArray = function() { var array = [], scaleMaxLog = Math.log(this.scale.max); for (var i = 0; i <= 10; i++) { array.push(Math.pow(Math.E, 0.1 * i * scaleMaxLog)); } return array; }; Zoom.prototype._getNextScale = function(direction) { var scaleSet = this.scale.set, currentScale = this.d3Zoom.scale(), lastShift = scaleSet.length - 1, shift, temp = []; // debugger if (this.scrolled) { for (shift = 0; shift <= lastShift; shift++) { temp.push(Math.abs(scaleSet[shift] - currentScale)); } shift = temp.indexOf(Math.min.apply(null, temp)); if (currentScale >= scaleSet[shift] && shift < lastShift) { shift++; } if (direction == "out" && shift > 0) { shift--; } this.scrolled = false; } else { shift = this.scale.currentShift; if (direction == "out") { shift > 0 && shift--; } else { shift < lastShift && shift++; } } this.scale.currentShift = shift; return scaleSet[shift]; }; // Expose library if (typeof exports === 'object') { $ = require('jquery'); module.exports = Zoom; } else if (typeof define === "function" && define.amd) { define("zoom", ["require", "jquery"], function(require) { $ = require('jquery'); return Zoom; }); } else { window.Zoom = window.Zoomer = Zoom; } if (window.jQuery) { window.jQuery.fn.zoomer = function(options, callback) { options = options || {}; options.element = this[0]; var zoomer = new Zoom(options); if (typeof callback === "function") { callback(zoomer, options); } return this; }; } })();