2012-07-16 21 views
12

मेरा लक्ष्य एक प्लगइन बनाना है जो पेज मैप पर पैनिंग ऑपरेशंस ज़ूम करने में सक्षम बनाता है, जैसे कि Google मानचित्र वर्तमान में कैसे काम करता है (अर्थ: माउस के साथ स्क्रॉल करना = क्षेत्र के ज़ूमिंग/आउट करना, क्लिक करें & & & रिलीज = पैनिंग) को पकड़ें।CSS3 कर्सर पर ज़ूमिंग

स्क्रॉल करते समय, मैं माउस कर्सर पर ज़ूम ऑपरेशन केंद्रित करना चाहता हूं।

इसके लिए, मैं ऑन-द-फ्लाई CSS3 मैट्रिक्स ट्रांसफॉर्मेशन का उपयोग करता हूं। एकमात्र, अभी तक अनिवार्य, बाधा यह है कि मैं 0px 0px की एक ट्रांसफॉर्म उत्पत्ति के साथ, CSS3 से & स्केल ट्रांसफॉर्मेशन का अनुवाद करने के अलावा किसी अन्य चीज़ का उपयोग नहीं कर सकता।

पैनिंग मेरे प्रश्न के दायरे से बाहर है, क्योंकि मेरे पास पहले से ही काम कर रहा है। जब ज़ूमिंग की बात आती है, तो मैं यह पता लगाने के लिए संघर्ष कर रहा हूं कि गड़बड़ मेरे जावास्क्रिप्ट कोड में कहां है।

एक्स अक्ष और वाई धुरी पर अनुवाद की गणना में समस्या MouseZoom.prototype.zoom फ़ंक्शन में कहीं कहीं होनी चाहिए।

सबसे पहले, यहाँ मेरी एचटीएमएल कोड है: https://github.com/brandonaaron/jquery-mousewheel/

:

<!DOCTYPE html> 
<html> 
<head> 
    <meta name="viewport" content="width = device-width, initial-scale = 1.0, user-scalable = no" /> 
    <meta name="apple-mobile-web-app-capable" content="yes"> 
    <meta name="apple-mobile-web-app-status-bar-style" content="black" /> 
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
    <script src="jquery.mousewheel.min.js"></script> 
    <script src="StackOverflow.js"></script> 
    <style type="text/css" media="all"> 
     #drawing { 
      position: absolute; 
      top: 0px; 
      left: 0px; 
      right:0; 
      bottom:0; 
      z-index: 0; 
      background: url(http://catmacros.files.wordpress.com/2009/09/cats_banzai.jpg) no-repeat; 
      background-position: 50% 50%; 
     } 
    </style> 
    <title>Test</title> 
</head> 
<body> 
    <div id="drawing"></div> 
    <script> 
     var renderer = new ZoomPanRenderer("drawing"); 
    </script> 
</body> 
</html> 

आप देख सकते हैं, मैं jQuery और ब्रैंडन हारून से jQuery माउस व्हील प्लगइन, यहां पाया जा सकता है जो उपयोग कर रहा हूँ

/***************************************************** 
* Transformations 
****************************************************/ 
function Transformations(translateX, translateY, scale){ 
    this.translateX = translateX; 
    this.translateY = translateY; 
    this.scale = scale; 
} 

/* Getters */ 
Transformations.prototype.getScale = function(){ return this.scale; } 
Transformations.prototype.getTranslateX = function(){ return this.translateX; } 
Transformations.prototype.getTranslateY = function(){ return this.translateY; } 

/***************************************************** 
* Zoom Pan Renderer 
****************************************************/ 
function ZoomPanRenderer(elementId){ 
    this.zooming = undefined; 
    this.elementId = elementId; 
    this.current = new Transformations(0, 0, 1); 
    this.last = new Transformations(0, 0, 1); 
    new ZoomPanEventHandlers(this); 
} 

/* setters */ 
ZoomPanRenderer.prototype.setCurrentTransformations = function(t){ this.current = t; } 
ZoomPanRenderer.prototype.setZooming = function(z){ this.zooming = z; } 

/* getters */ 
ZoomPanRenderer.prototype.getCurrentTransformations = function(){ return this.current; } 
ZoomPanRenderer.prototype.getZooming = function(){ return this.zooming; } 
ZoomPanRenderer.prototype.getLastTransformations = function(){ return this.last; } 
ZoomPanRenderer.prototype.getElementId = function(){ return this.elementId; } 

/* Rendering */ 
ZoomPanRenderer.prototype.getTransform3d = function(t){ 
    var transform3d = "matrix3d("; 
    transform3d+= t.getScale().toFixed(10) + ",0,0,0,"; 
    transform3d+= "0," + t.getScale().toFixed(10) + ",0,0,"; 
    transform3d+= "0,0,1,0,"; 
    transform3d+= t.getTranslateX().toFixed(10) + "," + t.getTranslateY().toFixed(10) + ",0,1)"; 
    return transform3d; 
} 

ZoomPanRenderer.prototype.getTransform2d = function(t){ 
    var transform3d = "matrix("; 
    transform3d+= t.getScale().toFixed(10) + ",0,0," + t.getScale().toFixed(10) + "," + t.getTranslateX().toFixed(10) + "," + t.getTranslateY().toFixed(10) + ")"; 
    return transform3d; 
} 

ZoomPanRenderer.prototype.applyTransformations = function(t){ 
    var elem = $("#" + this.getElementId()); 
    elem.css("transform-origin", "0px 0px"); 
    elem.css("-ms-transform-origin", "0px 0px"); 
    elem.css("-o-transform-origin", "0px 0px"); 
    elem.css("-moz-transform-origin", "0px 0px"); 
    elem.css("-webkit-transform-origin", "0px 0px"); 
    var transform2d = this.getTransform2d(t); 
    elem.css("transform", transform2d); 
    elem.css("-ms-transform", transform2d); 
    elem.css("-o-transform", transform2d); 
    elem.css("-moz-transform", transform2d); 
    elem.css("-webkit-transform", this.getTransform3d(t)); 
} 

/***************************************************** 
* Event handler 
****************************************************/ 
function ZoomPanEventHandlers(renderer){ 
    this.renderer = renderer; 

    /* Disable scroll overflow - safari */ 
    document.addEventListener('touchmove', function(e) { e.preventDefault(); }, false); 

    /* Disable default drag opeartions on the element (FF makes it ready for save)*/ 
    $("#" + renderer.getElementId()).bind('dragstart', function(e) { e.preventDefault(); }); 

    /* Add mouse wheel handler */ 
    $("#" + renderer.getElementId()).bind("mousewheel", function(event, delta) { 
     if(renderer.getZooming()==undefined){ 
      var offsetLeft = $("#" + renderer.getElementId()).offset().left; 
      var offsetTop = $("#" + renderer.getElementId()).offset().top; 
      var zooming = new MouseZoom(renderer.getCurrentTransformations(), event.pageX, event.pageY, offsetLeft, offsetTop, delta); 
      renderer.setZooming(zooming); 

      var newTransformation = zooming.zoom(); 
      renderer.applyTransformations(newTransformation); 
      renderer.setCurrentTransformations(newTransformation); 
      renderer.setZooming(undefined); 
     } 
     return false; 
    }); 
} 

/***************************************************** 
* Mouse zoom 
****************************************************/ 
function MouseZoom(t, mouseX, mouseY, offsetLeft, offsetTop, delta){ 
    this.current = t; 
    this.offsetLeft = offsetLeft; 
    this.offsetTop = offsetTop; 
    this.mouseX = mouseX; 
    this.mouseY = mouseY; 
    this.delta = delta; 
} 

MouseZoom.prototype.zoom = function(){ 
    var previousScale = this.current.getScale(); 
    var newScale = previousScale + this.delta/5; 
    if(newScale<1){ 
     newScale = 1; 
    } 
    var ratio = newScale/previousScale; 

    var imageX = this.mouseX - this.offsetLeft; 
    var imageY = this.mouseY - this.offsetTop; 

    var previousTx = - this.current.getTranslateX() * previousScale; 
    var previousTy = - this.current.getTranslateY() * previousScale; 
    var previousDx = imageX * previousScale; 
    var previousDy = imageY * previousScale; 

    var newTx = (previousTx * ratio + previousDx * (ratio - 1))/newScale; 
    var newTy = (previousTy * ratio + previousDy * (ratio - 1))/newScale; 

    return new Transformations(-newTx, -newTy, newScale); 
} 
+2

सुझाव: (1) jsfiddle का उपयोग करें, परिणाम देखने में आसान है (2) अधिक जानकारी में "गड़बड़" का वर्णन करें। –

+0

बस एक टिप - क्या आपने ज़ूमूज़.जेएस नामक प्लगइन देखी है, यदि आपके पास जो भी बनाना है, उसके लिए पिनपॉइंटर्स के बहुत सारे नहीं हो सकते हैं - http://janne.aukia.com/zoomooz/ –

उत्तर

28

transform का उपयोग करते हुए एक goog पाने के लिए: यहाँ StackOverflow.js फ़ाइल की सामग्री है le एक div तत्व एक दिलचस्प विचार की तरह लग रहा था पर व्यवहार जूमिंग नक्शे, तो मैं यह एक छोटे से भुगतान =)

मैं transform-origin (का प्रयोग करेंगे और उसके सहयोगी ब्राउज़र संगतता के लिए जिम्मेदार बताते हैं) माउस को ज़ूम समायोजित करने के लिए उस div पर स्थान जिसे आप स्केल कर रहे हैं। मुझे लगता है कि यह वही कर सकता है जो आप चाहते हैं।

समायोजन मैं उदाहरण के लिए बेला पर कुछ उदाहरण रखा transform-origin

तो तुम्हारा applyTransformations समारोह में हम transform-origin गतिशील imageX और imageY से समायोजित कर सकते हैं, अगर हम MouseZoom (माउस श्रोता) समारोह से मान जाते हैं।

var orig = t.getTranslateX().toFixed() + "px " + t.getTranslateY().toFixed() + "px"; 
    elem.css("transform-origin", orig); 
    elem.css("-ms-transform-origin", orig); 
    elem.css("-o-transform-origin", orig); 
    elem.css("-moz-transform-origin", orig); 
    elem.css("-webkit-transform-origin", orig); 

(इस first fiddle example में मैं सिर्फ अपने translateX और Transformations में translateY इस्तेमाल किया div तत्व पर माउस के स्थान पारित करने के लिए - दूसरे उदाहरण मैं originX और originY को इसका नाम बदलकर अनुवाद चर से अलग करने के लिए में।)

गिना जा रहा है मूल

साथ बस को बदलने अपने MouseZoom गणना कर सकते हैं मूल स्थान में

MouseZoom.prototype.zoom = function(){ 
     var previousScale = this.current.getScale(); 
     var newScale = previousScale + this.delta/10; 
     if(newScale<1){ 
      newScale = 1; 
     } 
     var ratio = newScale/previousScale; 

     var imageX = this.mouseX - this.offsetLeft; 
     var imageY = this.mouseY - this.offsetTop; 

     var newTx = imageX/previousScale; 
     var newTy = imageY/previousScale; 

     return new Transformations(newTx, newTy, newScale); 
    } 

तो यह पूरी तरह से काम करेगा यदि आप एक अलग स्थिति पर ज़ूम करने से पहले पूरी तरह ज़ूम आउट करते हैं। लेकिन किसी भी ज़ूम स्तर पर ज़ूम मूल को बदलने में सक्षम होने के लिए, हम मूल और अनुवाद कार्यक्षमता को जोड़ सकते हैं।

ज़ूमिंग फ्रेम (मेरी मूल जवाब विस्तार)

स्थानांतरण छवि पर बदलने मूल अभी भी उसी तरह गणना की जाती है, लेकिन हम यहाँ एक अलग translateX और translateY का उपयोग संभवत: ज़ूम फ्रेम शिफ्ट करने के लिए (मैं दो पेश किया नए चर जो हमें चाल करने में मदद करते हैं - तो अब हमारे पास originX, originY, translateX और translateY) हैं।

MouseZoom.prototype.zoom = function(){ 
     // current scale 
     var previousScale = this.current.getScale(); 
     // new scale 
     var newScale = previousScale + this.delta/10; 
     // scale limits 
     var maxscale = 20; 
     if(newScale<1){ 
      newScale = 1; 
     } 
     else if(newScale>maxscale){ 
      newScale = maxscale; 
     } 
     // current cursor position on image 
     var imageX = (this.mouseX - this.offsetLeft).toFixed(2); 
     var imageY = (this.mouseY - this.offsetTop).toFixed(2); 
     // previous cursor position on image 
     var prevOrigX = (this.current.getOriginX()*previousScale).toFixed(2); 
     var prevOrigY = (this.current.getOriginY()*previousScale).toFixed(2); 
     // previous zooming frame translate 
     var translateX = this.current.getTranslateX(); 
     var translateY = this.current.getTranslateY(); 
     // set origin to current cursor position 
     var newOrigX = imageX/previousScale; 
     var newOrigY = imageY/previousScale; 
     // move zooming frame to current cursor position 
     if ((Math.abs(imageX-prevOrigX)>1 || Math.abs(imageY-prevOrigY)>1) && previousScale < maxscale) { 
      translateX = translateX + (imageX-prevOrigX)*(1-1/previousScale); 
      translateY = translateY + (imageY-prevOrigY)*(1-1/previousScale); 
     } 
     // stabilize position by zooming on previous cursor position 
     else if(previousScale != 1 || imageX != prevOrigX && imageY != prevOrigY) { 
      newOrigX = prevOrigX/previousScale; 
      newOrigY = prevOrigY/previousScale; 
     } 
     return new Transformations(newOrigX, newOrigY, translateX, translateY, newScale); 
    } 

इस उदाहरण के लिए मैं एक छोटे से अधिक अपने मूल स्क्रिप्ट समायोजित और second fiddle example गयी।

अब हम किसी भी ज़ूम स्तर से माउस कर्सर पर ज़ूम इन और आउट करते हैं। लेकिन फ्रेम शिफ्ट की वजह से हम मूल div को चारों ओर ले जा रहे हैं ("धरती को मापना") ... यदि आप सीमित चौड़ाई और हाइट की वस्तु के साथ काम करते हैं तो मजाकिया लग रहा है (एक तरफ ज़ूम-इन, ज़ूम आउट एक और अंत, और हम एक इंच की तरह की तरह आगे बढ़े)।

"inchworm" प्रभाव से बचना

यह आप उदाहरण के लिए सीमाओं जोड़ सकता है से बचने के लिए इतना है कि बाएं छवि सीमा को उसके मूल एक्स के सही समन्वय करने के लिए ले जा सकते हैं, शीर्ष छवि सीमा स्थानांतरित नहीं कर सकते इसकी मूल वाई स्थिति से कम, और अन्य दो सीमाओं के लिए भी। लेकिन फिर ज़ूम/आउट कर्सर से पूरी तरह से बाध्य नहीं होगा, बल्कि छवि के किनारे (आप छवि स्लाइड को जगह में देखेंगे) example 3 में।

if(this.delta <= 0){ 
     var width = 500; // image width 
     var height = 350; // image height 
     if(translateX+newOrigX+(width - newOrigX)*newScale <= width){ 
      translateX = 0; 
      newOrigX = width; 
     } 
     else if (translateX+newOrigX*(1-newScale) >= 0){ 
      translateX = 0; 
      newOrigX = 0;   
     } 
     if(translateY+newOrigY+(height - newOrigY)*newScale <= height){ 
      translateY = 0; 
      newOrigY = height; 
     } 
     else if (translateY+newOrigY*(1-newScale) >= 0){ 
      translateY = 0; 
      newOrigY = 0; 
     } 
    } 

एक और (थोड़ा भद्दा) विकल्प बस फ्रेम का अनुवाद जब आप पूरी तरह (पैमाने == 1) बाहर ज़ूम रीसेट करने के लिए किया जाएगा।

हालांकि, यदि आप निरंतर तत्वों (बाएं और दाएं किनारे और ऊपर और नीचे किनारे एक साथ बंधे हुए हैं) या बस बहुत बड़े तत्वों के साथ काम करेंगे, तो आपको यह समस्या नहीं होगी।

एक अच्छा स्पर्श के साथ सब कुछ खत्म करने के लिए - हम अपने स्केलिंग ऑब्जेक्ट के आस-पास छिपे हुए ओवरफ्लो के साथ एक पैरेंट फ्रेम जोड़ सकते हैं। तो छवि क्षेत्र ज़ूमिंग के साथ बदल नहीं है। jsfiddle example 4 देखें।

0

हम एक इस के लिए पुस्तकालय प्रतिक्रिया बनाया: https://www.npmjs.com/package/react-map-interaction

यह जूमिंग और पैनिंग और मोबाइल और डेस्कटॉप दोनों पर काम करता है हैंडल।

स्रोत काफी कम और पठनीय है, लेकिन यहाँ अधिक सीधे आपके सवाल का जवाब देना है, हम इस सीएसएस का उपयोग बदलने:

const transform = `translate(${translation.x}px, ${translation.y}px) scale(${scale})`; 
const style = { 
    transform: transform, 
    transformOrigin: '0 0 ' 
}; 

// render the div with that style 

प्राथमिक चाल में से एक ठीक से प्रारंभिक सूचक/माउस के बीच अंतर की गणना कर रहा है एक स्पर्श/माउस चाल होने पर नीचे राज्य और वर्तमान स्थिति। जब माउस नीचे होता है, तो निर्देशांक कैप्चर करें। फिर प्रत्येक माउस चाल (माउस तक) तक दूरी में अंतर की गणना करें। यह अंतर यह सुनिश्चित करने के लिए कि आपके कर्सर के प्रारंभिक बिंदु ज़ूम का केंद्र बिंदु है, अनुवाद को ऑफ़सेट करने की आवश्यकता है।

संबंधित मुद्दे