2017-03-11 1 views
1

Le but est simple, en utilisant une roulette de la souris, zoomer sur un point spécifique (où la souris est). Cela signifie qu'après le zoom, la souris sera dans le même endroit à peu près au même endroit de l'image.Comment zoomer sur un point spécifique (pas de toile)?

Example of mouse mantaining position after zooming (à titre indicatif, je ne me soucie pas si vous utilisez des dauphins, des canards ou madonna pour l'image)

Je ne veux pas utiliser de la toile, et jusqu'à présent, je l'ai essayé quelque chose comme ceci:

HTML

<img src="whatever"> 

JS

function zoom(e){ 
    var deltaScale = deltaScale || -e.deltaY/1000; 
    var newScale = scale + deltaScale; 
    var newWidth = img.naturalWidth * newScale; 
    var newHeight = img.naturalHeight * newScale; 
    var x = e.pageX; 
    var y = e.pageY; 
    var newX = x * newWidth/img.width; 
    var newY = y * newHeight/img.height; 
    var deltaX = newX - x; 
    var deltaY = newY - y; 
    setScale(newScale); 
    setPosDelta(-deltaX,-deltaY); 
} 

function setPosDelta(dX, dY) { 
    var imgPos = getPosition(); 
    setPosition(imgPos.x + dX, imgPos.y + dY); 
} 

function getPosition() { 
    var x = parseFloat(img.style.left); 
    var y = parseFloat(img.style.top); 
    return { 
     x: x, 
     y: y 
    } 
} 

function setScale(n) { 
    scale = n; 
    img.width = img.naturalWidth * n; 
    img.height = img.naturalHeight * n; 
} 

Ce que cela tente de faire est de calculer les coordonnées x, y de l'oeil du dauphin avant et après le zoom, et après avoir calculé la distance entre ces deux points, il soustrait de la gauche, haut position afin de corriger le déplacement du zoom, sans succès particulier. Le zoom se produit naturellement en prolongeant l'image vers la droite et vers le bas, de sorte que la correction essaie de se retirer vers la gauche et vers le haut pour garder la souris sur cet œil de dauphin! Mais certainement pas. Dites-moi, quel est le problème avec le code/maths? Je pense que cette question n'est pas trop large, étant donné que je ne pouvais pas trouver de solutions autres que la toile.

Merci!

[EDIT] IMPORTANT

CSS transformer les questions d'ordre, si vous suivez la réponse choisie, assurez-vous de commander la transition d'abord, puis l'échelle. Les transformations CSS sont exécutées à rebours (de droite à gauche) afin que la mise à l'échelle soit traitée en premier, puis la traduction.

Répondre

3

Voici une implémentation du zoom sur un point. Le code utilise le CSS 2D transform et comprend le panoramique de l'image sur un clic et faites glisser. C'est facile à cause de l'absence de changement d'échelle. L'astuce lors du zoom est de normaliser la quantité de décalage en utilisant l'échelle actuelle (en d'autres termes: la diviser par l'échelle actuelle) d'abord, puis d'appliquer la nouvelle échelle à ce décalage normalisé. Cela maintient le curseur exactement là où il est indépendant de l'échelle.

var scale = 1, 
 
    panning = false, 
 
    xoff = 0, 
 
    yoff = 0, 
 
    start = {x: 0, y: 0}, 
 
    doc = document.getElementById("document"); 
 

 
function setTransform() { 
 
    doc.style.transform = "translate(" + xoff + "px, " + yoff + "px) scale(" + scale + ")"; 
 
} 
 

 
doc.onmousedown = function(e) { 
 
    e.preventDefault(); 
 
    start = {x: e.clientX - xoff, y: e.clientY - yoff};  
 
    panning = true; 
 
} 
 

 
doc.onmouseup = function(e) { 
 
    panning = false; 
 
} 
 

 
doc.onmousemove = function(e) { 
 
    e.preventDefault();   
 
    if (!panning) { 
 
    return; 
 
    } 
 
    xoff = (e.clientX - start.x); 
 
    yoff = (e.clientY - start.y); 
 
    setTransform(); 
 
} 
 

 
doc.onwheel = function(e) { 
 
    e.preventDefault(); 
 
    // take the scale into account with the offset 
 
    var xs = (e.clientX - xoff)/scale, 
 
     ys = (e.clientY - yoff)/scale, 
 
     delta = (e.wheelDelta ? e.wheelDelta : -e.deltaY); 
 

 
    // get scroll direction & set zoom level 
 
    (delta > 0) ? (scale *= 1.2) : (scale /= 1.2); 
 

 
    // reverse the offset amount with the new scale 
 
    xoff = e.clientX - xs * scale; 
 
    yoff = e.clientY - ys * scale; 
 

 
    setTransform();   
 
}
html, body { 
 
    margin: 0; 
 
    padding: 0; 
 
    width: 100%; 
 
    height: 100%; 
 
    overflow: hidden; 
 
} 
 

 
#document { 
 
    width: 100%; 
 
    height: 100%; 
 
    transform-origin: 0px 0px; 
 
    transform: scale(1) translate(0px, 0px); 
 
}
<div id="document"> 
 
    <img style="width: 100%" 
 
     src="http://lorempixel.com/output/animals-q-c-1920-1920-9.jpg" /> 
 
</div> 
 

+0

J'aime cette version plus, merci , bon travail! Pour une raison quelconque, j'étais bloqué sur celui-ci. – undefined

3

Ceci est une mise en œuvre qui est plus proche de votre idée originale en utilisant des décalages haut et à gauche et de modifier l'attribut largeur de l'image au lieu d'utiliser la transformée de css mon autre réponse.

var scale = 1.0, 
 
    img = document.getElementById("image"), 
 
    deltaX = 0, 
 
    deltaY = 0; 
 

 
// set the initial scale once the image is loaded 
 
img.onload = function() { 
 
    scale = image.offsetWidth/image.naturalWidth; 
 
} 
 

 
img.onwheel = function(e) { 
 
    e.preventDefault(); 
 

 
    // first, remove the scale so we have the native offset 
 
    var xoff = (e.clientX - deltaX)/scale, 
 
    yoff = (e.clientY - deltaY)/scale, 
 
    delta = (e.wheelDelta ? e.wheelDelta : -e.deltaY); 
 

 
    // get scroll direction & set zoom level 
 
    (delta > 0) ? (scale *= 1.05) : (scale /= 1.05); 
 

 
    // limit the smallest size so the image does not disappear 
 
    if (img.naturalWidth * scale < 16) { 
 
    scale = 16/img.naturalWidth; 
 
    } 
 

 
    // apply the new scale to the native offset 
 
    deltaX = e.clientX - xoff * scale; 
 
    deltaY = e.clientY - yoff * scale; 
 

 
    // now modify the attributes of the image to reflect the changes 
 
    img.style.top = deltaY + "px"; 
 
    img.style.left = deltaX + "px"; 
 

 
    img.style.width = (img.naturalWidth * scale) + "px"; 
 
} 
 

 
window.onresize = function(e) { 
 
    document.getElementById("wrapper").style.width = window.innerWidth + "px"; 
 
    document.getElementById("wrapper").style.height = window.innerHeight + "px"; 
 
} 
 

 
window.onload = function(e) { 
 
    document.getElementById("wrapper").style.width = window.innerWidth + "px"; 
 
    document.getElementById("wrapper").style.height = window.innerHeight + "px"; 
 
}
html, 
 
body { 
 
    height: 100%; 
 
    width: 100%; 
 
    margin: 0; 
 
    padding: 0; 
 
    overflow: hidden; 
 
} 
 

 
div { 
 
    overflow: hidden; 
 
}
<div id="wrapper" style="position:relative;"> 
 
    <img id="image" style="width:100%;position:absolute;top:0px;left:0px;" src="http://lorempixel.com/output/animals-q-c-1920-1920-9.jpg" alt=""> 
 
</div>

+0

Vous devriez vérifier la mise à l'échelle de cette réponse, elle est inversée et très instable. Merci pour les deux options cependant, j'apprécie vraiment! – undefined

+0

Ok, j'ai changé le zoom pour utiliser la technique de lissage que j'ai utilisée dans l'autre réponse. J'ai aussi baissé le ratio pour qu'il soit encore plus lisse. – fmacdee

0

J'ai aimé les deux postes de fmacdee.J'ai factorisé le code qu'il a créé pour en faire une version réutilisable qui peut être appelée sur n'importe quelle image.

il suffit d'appeler:

var imageScaler = new ImageScaler(document.getElementById("image")); 
imageScaler.setup(); 

et inclure ce code quelque part dans votre projet:

var ImageScaler = function(img) 
{ 
    this.img = img; 
    this.scale = this.getImageScale(); 
    this.panning = false; 

    this.start = {x: 0, y: 0}; 
    this.delta = {x: 0, y: 0}; 
}; 

ImageScaler.prototype = 
{ 
    constructor: ImageScaler, 

    setup: function() 
    { 
     this.setupEvents(); 
    }, 

    setupEvents: function() 
    { 
     var img = this.img; 
     var callBack = this.onScale.bind(this); 
     var touchDown = this.touchDown.bind(this), 
     touhcMove = this.touchMove.bind(this), 
     touchUp = this.touchUp.bind(this); 

     img.onwheel = callBack; 
     img.onmousedown = touchDown; 
     img.onmousemove = touhcMove; 
     img.onmouseup = touchUp; 
    }, 

    getImageScale: function() 
    { 
     var img = this.img; 
     return img.offsetWidth/img.naturalWidth; 
    }, 

    getMouseDirection: function(e) 
    { 
     return (e.wheelDelta ? e.wheelDelta : -e.deltaY); 
    }, 

    getOffset: function(e) 
    { 
     var scale = this.scale, 
     delta = this.delta; 
     // first, remove the scale so we have the native offset 
     return { 
      x: (e.clientX - delta.x)/scale, 
      y: (e.clientY - delta.y)/scale 
     }; 
    }, 

    scaleElement: function(x, y, scale) 
    { 
     var img = this.img; 
     img.style.top = y + "px"; 
     img.style.left = x + "px"; 
     img.style.width = (img.naturalWidth * scale) + "px"; 
    }, 

    minScale: 0.2, 

    updateScale: function(delta) 
    { 
     // get scroll direction & set zoom level 
     var scale = (delta > 0) ? (this.scale *= 1.05) : (this.scale /= 1.05); 

     // limit the smallest size so the image does not disappear 
     if (scale <= this.minScale) 
     { 
      this.scale = this.minScale; 
     } 
     return this.scale; 
    }, 

    touchDown: function(e) 
    { 
     var delta = this.delta; 
     this.start = {x: e.clientX - delta.x, y: e.clientY - delta.y}; 
     this.panning = true; 
    }, 

    touchMove: function(e) 
    { 
     e.preventDefault();   
     if (this.panning === false) 
     { 
      return; 
     } 

     var delta = this.delta, 
     start = this.start; 
     delta.x = (e.clientX - start.x); 
     delta.y = (e.clientY - start.y); 
     console.log(delta, start) 

     this.scaleElement(delta.x, delta.y, this.scale); 
    }, 

    touchUp: function(e) 
    { 
     this.panning = false; 
    }, 

    onScale: function(e) 
    { 
     var offset = this.getOffset(e); 
     e.preventDefault(); 

     // get scroll direction & set zoom level 
     var delta = this.getMouseDirection(e); 
     var scale = this.updateScale(delta); 

     // apply the new scale to the native offset 
     delta = this.delta; 
     delta.x = e.clientX - offset.x * scale; 
     delta.y = e.clientY - offset.y * scale; 

     this.scaleElement(delta.x, delta.y, scale); 
    } 
}; 

J'ai fait un violon pour voir les résultats: http://jsfiddle.net/acqo5n8s/12/