userChrome.js を使って SVG ZoomAndPan
FirefoxにはSVGのズームやパンが実装されていない。
拡張機能ではthe firefox zoom and pan extensionがあるが、この拡張機能は読み込んだデータのContent-Type
がimage/svg+xml
だったら動くと言うもの。XHTML等のXMLアプリケーションとしてSVGが仕込まれていた時は機能しないのだ。
そこが不満だったので作ってみた*1。拡張にするにはちょっとアレな実装なのでuserChrome.js*2に登録。
機能
複数の埋め込まれたSVGに対応している。
Zoom
Alt + スクロールすることで拡大・縮小が可能。約1.5倍ずつ拡大・縮小される。
Pan
Ctrl + ドラッグでパンとなる。
元に戻す
Ctrl + Shift+クリック(マウスダウン)で元に戻る
やっていること
Source
/** * @author teramako [email protected] * @version 1.0 * @projectDescription SVG Zoom And Pan * @license MPL 1.1/GPL 2.0/LGPL 2.1 */ (function() { var startPoint = {}; var currentSVG = {}; var content = document.getElementById('content'); content.addEventListener('mousedown',svgOnMouseDown,false); content.addEventListener('DOMMouseScroll',svgZoom,false); // getSVGElement {{{ /** * @param {Element} target event.target * @return {Object} */ function getSVGElement(target){ var svgElement; if ( target instanceof SVGSVGElement ){ svgElement = target; } else { svgElement = target.ownerSVGElement; } currentSVG = { element: svgElement, x: svgElement.viewBox.baseVal.x, y: svgElement.viewBox.baseVal.y, zoomX: svgElement.width.baseVal.value / svgElement.viewBox.baseVal.width, zoomY: svgElement.height.baseVal.value / svgElement.viewBox.baseVal.height }; if ( ! svgElement.hasAttribute('__orgX') ){ svgElement.setAttribute('__orgX', currentSVG.x); svgElement.setAttribute('__orgY', currentSVG.y); svgElement.setAttribute('__orgWidth', svgElement.viewBox.baseVal.width); svgElement.setAttribute('__orgHeight', svgElement.viewBox.baseVal.height); } return currentSVG; } // }}} // svgOnMouseDown {{{ /** * Ctrl + MouseDown -> start pan * Ctrl + Shift + MouseDown -> reset * @param {Event} evt */ function svgOnMouseDown(evt){ if ( evt.ctrlKey && evt.target.namespaceURI == 'http://www.w3.org/2000/svg' ){ var svg = getSVGElement(evt.target); if ( ! evt.shiftKey ){ // pan svg.element.style.cursor = '-moz-grabbing'; content.addEventListener('mousemove',svgOnMouseMove,true); content.addEventListener('mouseup',svgOnMouseUp,true); startPoint = { x: evt.clientX, y: evt.clientY }; } else { // reset svg.element.viewBox.baseVal.width = svg.element.getAttribute('__orgWidth'); svg.element.viewBox.baseVal.height = svg.element.getAttribute('__orgHeight'); svg.element.viewBox.baseVal.x = svg.element.getAttribute('__orgX'); svg.element.viewBox.baseVal.y = svg.element.getAttribute('__orgY'); } } } // }}} // svgOnMouseMove {{{ /** * start Pan * @param {Event} evt */ function svgOnMouseMove(evt){ if ( startPoint && currentSVG && evt.ctrlKey ){ currentSVG.element.viewBox.baseVal.x = (startPoint.x - evt.clientX) / currentSVG.zoomX + currentSVG.x; currentSVG.element.viewBox.baseVal.y = (startPoint.y - evt.clientY) / currentSVG.zoomY + currentSVG.y; } } // }}} // svgOnMouseUp {{{ /** * Reset varible and event handler */ function svgOnMouseUp(){ if ( startPoint || currentSVG ){ currentSVG.element.style.cursor = 'auto'; currentSVG = null; startPoint = null; } content.removeEventListener('mousemove',svgOnMouseMove,true); content.removeEventListener('mouseup',svgOnMouseUp,true); } // }}} // svgZoom {{{ /** * Alt + Scroll up -> Zoom In * Alt + Scroll down -> Zoom Out * @param {Event} evt */ function svgZoom(evt){ if ( evt.altKey && evt.target.namespaceURI == 'http://www.w3.org/2000/svg' ){ var svg = getSVGElement(evt.target); if ( evt.detail < 0 ){ // Zoom In svg.element.viewBox.baseVal.width /= 1.5; svg.element.viewBox.baseVal.height /= 1.5; } else { svg.element.viewBox.baseVal.width *= 1.5; svg.element.viewBox.baseVal.height *= 1.5; } evt.stopPropagation(); } } // }}} }()); // vim:set ts=2 sw=2 sts=0 foldmethod=marker: