1 /** 2 * Returns a new zoom behavior to be registered on mousewheel events. 3 * 4 * @class Implements interactive zooming using mousewheel events. Register this 5 * behavior on panels to allow zooming. This behavior can be used in tandem with 6 * {@link pv.Behavior.pan} to allow both panning and zooming: 7 * 8 * <pre> .event("mousedown", pv.Behavior.pan()) 9 * .event("mousewheel", pv.Behavior.zoom())</pre> 10 * 11 * The zoom behavior currently supports only mousewheel events; support for 12 * keyboard shortcuts and gesture events to improve accessibility may be added 13 * in the future. 14 * 15 * <p>The implementation of this behavior relies on the panel's 16 * <tt>transform</tt> property, which specifies a matrix transformation that is 17 * applied to child marks. Note that the transform property only affects the 18 * panel's children, but not the panel itself; therefore the panel's fill and 19 * stroke will not change when the contents are zoomed. The built-in support for 20 * transforms only supports uniform scaling and translates, which is sufficient 21 * for panning and zooming. Note that this is not a strict geometric 22 * transformation, as the <tt>lineWidth</tt> property is scale-aware: strokes 23 * are drawn at constant size independent of scale. 24 * 25 * <p>Panels have transparent fill styles by default; this means that panels may 26 * not receive mousewheel events to zoom. To fix this problem, either given the 27 * panel a visible fill style (such as "white"), or set the <tt>events</tt> 28 * property to "all" such that the panel receives events despite its transparent 29 * fill. 30 * 31 * <p>The zoom behavior has optional support for bounding. If enabled, the user 32 * will not be able to zoom out farther than the initial bounds. This feature is 33 * designed to work in conjunction with the pan behavior. 34 * 35 * @extends pv.Behavior 36 * @see pv.Panel#transform 37 * @see pv.Mark#scale 38 * @param {number} speed 39 */ 40 pv.Behavior.zoom = function(speed) { 41 var bound; // whether to bound to the panel 42 43 if (!arguments.length) speed = 1 / 48; 44 45 /** @private */ 46 function mousewheel() { 47 var v = this.mouse(), 48 k = pv.event.wheel * speed, 49 m = this.transform().translate(v.x, v.y) 50 .scale((k < 0) ? (1e3 / (1e3 - k)) : ((1e3 + k) / 1e3)) 51 .translate(-v.x, -v.y); 52 if (bound) { 53 m.k = Math.max(1, m.k); 54 m.x = Math.max((1 - m.k) * this.width(), Math.min(0, m.x)); 55 m.y = Math.max((1 - m.k) * this.height(), Math.min(0, m.y)); 56 } 57 this.transform(m).render(); 58 pv.Mark.dispatch("zoom", this.scene, this.index); 59 } 60 61 /** 62 * Sets or gets the bound parameter. If bounding is enabled, the user will not 63 * be able to zoom out farther than the initial panel bounds. Bounding is not 64 * enabled by default. If this behavior is used in tandem with the pan 65 * behavior, both should use the same bound parameter. 66 * 67 * <p>Note: enabling bounding after zooming has already occurred will not 68 * immediately reset the transform. Bounding should be enabled before the zoom 69 * behavior is applied. 70 * 71 * @function 72 * @returns {pv.Behavior.zoom} this, or the current bound parameter. 73 * @name pv.Behavior.zoom.prototype.bound 74 * @param {boolean} [x] the new bound parameter. 75 */ 76 mousewheel.bound = function(x) { 77 if (arguments.length) { 78 bound = Boolean(x); 79 return this; 80 } 81 return Boolean(bound); 82 }; 83 84 return mousewheel; 85 }; 86