web-dev-qa-db-fra.com

Pincez pour zoomer avec CSS3

J'essaie de mettre en œuvre des gestes de pincement pour zoomer exactement comme dans Google Maps. J'ai assisté à une présentation de Stephen Woods - "Création d'interfaces Responsive HTML5 Touch" - sur le problème et utilisé la technique mentionnée. L'idée est de définir l'origine de la transformation de l'élément cible à (0, 0) et son échelle à le point de la transformation, puis traduisez l'image pour la maintenir centrée sur le point de la transformation. 

Dans mon code de test, la mise à l'échelle fonctionne bien. L'image effectue un zoom avant et arrière entre les traductions suivantes. Le problème est que je ne calcule pas correctement les valeurs de traduction. J'utilise jQuery et Hammer.js pour les événements tactiles. Comment puis-je ajuster mon calcul dans le rappel de transformation afin que l'image reste centrée au point de transformation?

CoffeeScript (#test-resize est une div avec une image d'arrière-plan)

image = $('#test-resize')

hammer = image.hammer ->
  prevent_default: true
  scale_treshold: 0

width = image.width()
height = image.height()
toX = 0
toY = 0
translateX = 0
translateY = 0
prevScale = 1
scale = 1

hammer.bind 'transformstart', (event) ->

  toX = (event.touches[0].x + event.touches[0].x) / 2
  toY = (event.touches[1].y + event.touches[1].y) / 2

hammer.bind 'transform', (event) ->

  scale = prevScale * event.scale
  shiftX = toX * ((image.width() * scale) - width) / (image.width() * scale)
  shiftY = toY * ((image.height() * scale) - height) / (image.height() * scale)
  width = image.width() * scale
  height = image.height() * scale

  translateX -= shiftX
  translateY -= shiftY

  css = 'translateX(' + @translateX + 'px) translateY(' + @translateY + 'px) scale(' + scale + ')'
  image.css '-webkit-transform', css
  image.css '-webkit-transform-Origin', '0 0'

hammer.bind 'transformend', () ->
  prevScale = scale
23
Maros Hluska

J'ai réussi à le faire fonctionner. 

jsFiddle demo

Dans la démo jsFiddle, cliquer sur l'image représente un geste de pincement centré sur le point de clic. Les clics ultérieurs augmentent le facteur d'échelle d'une quantité constante. Pour rendre cela utile, vous voudriez faire la balance et traduire les calculs beaucoup plus souvent sur un événement de transformation (hammer.js en fournit un).

La clé pour le faire fonctionner était de calculer correctement les coordonnées du point d'échelle par rapport à l'image. J'ai utilisé event.clientX/Y pour obtenir les coordonnées de l'écran. Les lignes suivantes convertissent l'écran en coordonnées d'image:

x -= offset.left + newX
y -= offset.top + newY

Ensuite, nous calculons une nouvelle taille pour l'image et trouvons les distances à traduire. L'équation de traduction est tirée de le discours de Stephen Woods .

newWidth = image.width() * scale
newHeight = image.height() * scale

newX += -x * (newWidth - image.width) / newWidth
newY += -y * (newHeight - image.height) / newHeight

Enfin, nous adaptons et traduisons

image.css '-webkit-transform', "scale3d(#{scale}, #{scale}, 1)"         
wrap.css '-webkit-transform', "translate3d(#{newX}px, #{newY}px, 0)"

Nous faisons toutes nos traductions sur un élément wrapper pour nous assurer que translate-Origin reste en haut à gauche de notre image.

19
Maros Hluska

J'ai utilisé avec succès cet extrait pour redimensionner des images sur phonegap, en utilisant hammer et jquery.

Si cela intéresse quelqu'un, je l'ai traduit en JS.

function attachPinch(wrapperID,imgID)
{
    var image = $(imgID);
    var wrap = $(wrapperID);

    var  width = image.width();
    var  height = image.height();
    var  newX = 0;
    var  newY = 0;
    var  offset = wrap.offset();

    $(imgID).hammer().on("pinch", function(event) {
        var photo = $(this);

        newWidth = photo.width() * event.gesture.scale;
        newHeight = photo.height() * event.gesture.scale;

        // Convert from screen to image coordinates
        var x;
        var y;
        x -= offset.left + newX;
        y -= offset.top + newY;

        newX += -x * (newWidth - width) / newWidth;
        newY += -y * (newHeight - height) / newHeight;

        photo.css('-webkit-transform', "scale3d("+event.gesture.scale+", "+event.gesture.scale+", 1)");      
        wrap.css('-webkit-transform', "translate3d("+newX+"px, "+newY+"px, 0)");

        width = newWidth;
        height = newHeight;
    });

}
6
Raphael Toselli

J'ai regardé partout sur Internet et sur quoi que ce soit, jusqu'à ce que je tombe sur le seul plugin/bibliothèque qui fonctionne - http://cubiq.org/iscroll-4

var myScroll;
myScroll = new iScroll('wrapper');

où wrapper est votre identifiant comme dans id = "wrapper"

<div id="wrapper">
    <img src="smth.jpg" />
</div>
2

Pas une vraie réponse, mais un lien vers un plug = in qui fait tout pour vous. Bon travail! 

https://github.com/timmywil/jquery.panzoom

(Merci 'Timmywil', qui que vous soyez)

0
Ideogram

C'est quelque chose que j'ai écrit il y a quelques années en Java et que j'ai récemment converti en JavaScript.

function View()
{
 this.pos = {x:0,y:0};
 this.Z = 0;
 this.zoom = 1;
 this.scale = 1.1;
 this.Zoom = function(delta,x,y)
 {
  var X = x-this.pos.x;
  var Y = y-this.pos.y;
  var scale = this.scale;
  if(delta>0) this.Z++;
  else
  {
   this.Z--;
   scale = 1/scale;
  }
  this.zoom = Math.pow(this.scale, this.Z);
  this.pos.x+=X-scale*X;
  this.pos.y+=Y-scale*Y;
 }
}

this.Zoom = function(delta,x,y) prend:

  • delta = zoom avant ou arrière
  • x = x position du zoom Origine
  • y = y position du zoom Origine

Un petit exemple:

<script>
var view = new View();
var DivStyle = {x:-123,y:-423,w:300,h:200};
function OnMouseWheel(event)
{
 event.preventDefault();
 view.Zoom(event.wheelDelta,event.clientX,event.clientY);
 div.style.left = (DivStyle.x*view.zoom+view.pos.x)+"px";
 div.style.top = (DivStyle.y*view.zoom+view.pos.y)+"px";
 div.style.width = (DivStyle.w*view.zoom)+"px";
 div.style.height = (DivStyle.h*view.zoom)+"px";
}
function OnMouseMove(event)
{
 view.pos = {x:event.clientX,y:event.clientY};
 div.style.left = (DivStyle.x*view.zoom+view.pos.x)+"px";
 div.style.top = (DivStyle.y*view.zoom+view.pos.y)+"px";
 div.style.width = (DivStyle.w*view.zoom)+"px";
 div.style.height = (DivStyle.h*view.zoom)+"px";
}
</script>
<body onmousewheel="OnMouseWheel(event)" onmousemove="OnMouseMove(event)">
<div id="div" style="position:absolute;left:-123px;top:-423px;width:300px;height:200px;border:1px solid;"></div>
</body>

Cela a été fait avec l'intention d'être utilisé avec une toile et des graphiques, mais cela devrait fonctionner parfaitement pour une mise en page HTML normale

0
Isaac