Bir sıkışmayı tespit etmenin en basit yolu


87

Bu bir WEB UYGULAMASI yerel bir uygulama değil. Lütfen Objective-C NS komutu kullanmayın.

Bu yüzden iOS'ta "çimdik" olaylarını algılamam gerekiyor. Sorun şu ki, hareketler veya çoklu dokunma olayları yapmak için gördüğüm her eklenti veya yöntem (genellikle) jQuery ile ve güneşin altındaki her hareket için tam bir ek eklenti. Başvurum çok büyük ve kodumda deadwood'a karşı çok hassasım. Tek ihtiyacım olan bir tutam tespit etmek ve jGesture gibi bir şey kullanmak basit ihtiyaçlarım için şişirmenin tam bir yolu.

Ek olarak, bir sıkışmanın manuel olarak nasıl tespit edileceği konusunda sınırlı bir anlayışa sahibim. Her iki parmağımın konumunu alabiliyorum, bunu tespit etmek için doğru karışımı alamıyorum. SADECE sıkışmayı algılayan basit bir pasajı olan var mı?

Yanıtlar:


71

Kullanmak istediğiniz gesturestart, gesturechangeve gestureendolayları . Bunlar, ekrana 2 veya daha fazla parmak dokunduğunda tetiklenir.

Kıstırma hareketiyle ne yapmanız gerektiğine bağlı olarak, yaklaşımınızın ayarlanması gerekecektir. scaleÇarpan kullanıcının tutam jest ne kadar dramatik belirlemek için incelenebilir. Özelliğin nasıl davranacağıyla ilgili ayrıntılar için Apple'ın TouchEvent belgelerine bakın scale.

node.addEventListener('gestureend', function(e) {
    if (e.scale < 1.0) {
        // User moved fingers closer together
    } else if (e.scale > 1.0) {
        // User moved fingers further apart
    }
}, false);

Ayrıca gesturechange, uygulamanızı daha duyarlı hissettirmek için ihtiyaç duyduğunuzda bir sıkışmayı algılamak için olayı durdurabilirsiniz .


59
Bu sorunun özellikle iOS ile ilgili olduğunu biliyorum, ancak soru başlığı genel olarak "Bir sıkışmayı algılamanın en basit yolu". Hareket başlangıcı, hareket değişimi ve hareket sonu olayları iOS'a özeldir ve platformlar arası çalışmaz. Android veya diğer dokunmatik tarayıcılarda ateşlenmezler. Bu çapraz platformu yapmak için touchstart, touchmove ve touchend etkinliklerini kullanın, örneğin bu yanıt stackoverflow.com/a/11183333/375690 .
Phil McCullick

6
@phil Tüm mobil tarayıcıları desteklemenin en basit yolunu arıyorsanız, hammer.js'yi kullanmanız daha iyi olur
Dan Herbert

4
JQuery kullandım $(selector).on('gestureend',...)ve e.originalEvent.scaleyerine kullanmak zorunda kaldım e.scale.
Chad von Nau

3
@ChadvonNau Bunun nedeni jQuery'nin olay nesnesinin "normalleştirilmiş W3C olay nesnesi" olmasıdır. W3C Olay nesnesi içermez scaleözelliği. Satıcıya özel bir özelliktir. Cevabım, görevi vanilla JS ile gerçekleştirmenin en basit yolunu içerirken, zaten JS çerçevelerini kullanıyorsanız, size çok daha iyi bir API sağlayacağı için hammer.js kullanmanız daha iyi olacaktır.
Dan Herbert

1
@superuberduper IE8 / 9'un bir sıkışmayı tespit etme yolu yoktur. IE10'a kadar IE'ye dokunma API'leri eklenmemişti. Orijinal soru özellikle iOS hakkında soruldu, ancak bunu tarayıcılarda ele almak için tarayıcılar arası farklılıkları ortadan kaldıran hammer.js çerçevesini kullanmalısınız.
Dan Herbert

136

Bir pincholayın ne olduğunu düşünün : bir elementin üzerinde, birbirine doğru veya uzaklaşan iki parmak. Hareket olayları, bildiğim kadarıyla oldukça yeni bir standarttır, bu yüzden muhtemelen bunu yapmanın en güvenli yolu aşağıdaki gibi dokunma olaylarını kullanmaktır:

( ontouchstartolay)

if (e.touches.length === 2) {
    scaling = true;
    pinchStart(e);
}

( ontouchmoveolay)

if (scaling) {
    pinchMove(e);
}

( ontouchendolay)

if (scaling) {
    pinchEnd(e);
    scaling = false;
}

İki parmak arasındaki mesafeyi öğrenmek için şu hypotişlevi kullanın :

var dist = Math.hypot(
    e.touches[0].pageX - e.touches[1].pageX,
    e.touches[0].pageY - e.touches[1].pageY);

1
Neden kendi çimdik tespitinizi yazarsınız? Bu, iOS webkit'teki yerel işlevdir. Bu aynı zamanda iyi bir uygulama değildir çünkü iki parmakla kaydırma ile kıstırma arasındaki farkı ayırt edemez. İyi bir tavsiye değil.
mmaclaurin

34
@mmaclaurin çünkü webkit her zaman kıstırma algılamaya sahip değildi (yanılıyorsam düzelt), tüm dokunmatik ekranlar webkit kullanmıyor ve bazen bir kaydırma olayının algılanması gerekmiyor. OP, deadwood kütüphanesi işlevleri olmayan basit bir çözüm istedi.
Jeffrey Sweeney

6
OP, iOS'tan bahsetti, ancak bu, diğer platformları değerlendirirken en iyi cevap. Mesafe hesaplamanızın karekök kısmını bırakmış olmanız dışında. Onu koydum.
tanımsız

3
@BrianMortenson Bu kasıtlıydı; sqrtpahalı olabilir ve genellikle parmaklarınızın bir miktar içeri veya dışarı hareket ettiğini bilmeniz gerekir. Ama .. Pisagor teoremini söyledim ve teknik olarak kullanmıyordum;)
Jeffrey Sweeney

2
@mmaclaurin Sadece (deltaX * deltaY <= 0) bu şekilde iki parmakla kaydırmayı değil tüm kıstırma durumlarını algılayıp algılamadığınızı kontrol edin.
Dolma

30

Hammer.js sonuna kadar! "Dönüşümleri" (kıstırma) işler. http://eightmedia.github.com/hammer.js/

Ama bunu kendiniz uygulamak isterseniz, Jeffrey'nin cevabının oldukça sağlam olduğunu düşünüyorum.


Dan'ın cevabını görmeden önce hammer.js'yi buldum ve uyguladım. Hammer oldukça havalı.
Fresheyeball

Harika görünüyordu, ancak demolar o kadar düzgün değildi. Yakınlaştırmak ve sonra gezinmeye çalışmak gerçekten sarsıcı geldi.
Alex K

3
Hammer'ın, bazıları bunu yazarken oldukça şiddetli olan (özellikle Android) bir kova dolusu olağanüstü hataya sahip olduğunu belirtmek gerekir. Düşünmeye değer.
Single Entity

3
Burada da aynısı, buggy. Hammer'ı denedi, Jeffrey'nin çözümünü kullandı.
Paul

4

Ne yazık ki, tarayıcılar arasında kıstırma hareketlerini tespit etmek umulduğu kadar basit değildir, ancak HammerJS bunu çok daha kolay hale getirir!

Check out HammerJS demo ile Makas Zoom ve Pan . Bu örnek Android, iOS ve Windows Phone üzerinde test edilmiştir.

Kaynak kodunu Pinch Zoom ve Pan with HammerJS'de bulabilirsiniz .

Size kolaylık sağlamak için, işte kaynak kodu:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport"
        content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
  <title>Pinch Zoom</title>
</head>

<body>

  <div>

    <div style="height:150px;background-color:#eeeeee">
      Ignore this area. Space is needed to test on the iPhone simulator as pinch simulation on the
      iPhone simulator requires the target to be near the middle of the screen and we only respect
      touch events in the image area. This space is not needed in production.
    </div>

    <style>

      .pinch-zoom-container {
        overflow: hidden;
        height: 300px;
      }

      .pinch-zoom-image {
        width: 100%;
      }

    </style>

    <script src="https://hammerjs.github.io/dist/hammer.js"></script>

    <script>

      var MIN_SCALE = 1; // 1=scaling when first loaded
      var MAX_SCALE = 64;

      // HammerJS fires "pinch" and "pan" events that are cumulative in nature and not
      // deltas. Therefore, we need to store the "last" values of scale, x and y so that we can
      // adjust the UI accordingly. It isn't until the "pinchend" and "panend" events are received
      // that we can set the "last" values.

      // Our "raw" coordinates are not scaled. This allows us to only have to modify our stored
      // coordinates when the UI is updated. It also simplifies our calculations as these
      // coordinates are without respect to the current scale.

      var imgWidth = null;
      var imgHeight = null;
      var viewportWidth = null;
      var viewportHeight = null;
      var scale = null;
      var lastScale = null;
      var container = null;
      var img = null;
      var x = 0;
      var lastX = 0;
      var y = 0;
      var lastY = 0;
      var pinchCenter = null;

      // We need to disable the following event handlers so that the browser doesn't try to
      // automatically handle our image drag gestures.
      var disableImgEventHandlers = function () {
        var events = ['onclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
                      'onmouseup', 'ondblclick', 'onfocus', 'onblur'];

        events.forEach(function (event) {
          img[event] = function () {
            return false;
          };
        });
      };

      // Traverse the DOM to calculate the absolute position of an element
      var absolutePosition = function (el) {
        var x = 0,
          y = 0;

        while (el !== null) {
          x += el.offsetLeft;
          y += el.offsetTop;
          el = el.offsetParent;
        }

        return { x: x, y: y };
      };

      var restrictScale = function (scale) {
        if (scale < MIN_SCALE) {
          scale = MIN_SCALE;
        } else if (scale > MAX_SCALE) {
          scale = MAX_SCALE;
        }
        return scale;
      };

      var restrictRawPos = function (pos, viewportDim, imgDim) {
        if (pos < viewportDim/scale - imgDim) { // too far left/up?
          pos = viewportDim/scale - imgDim;
        } else if (pos > 0) { // too far right/down?
          pos = 0;
        }
        return pos;
      };

      var updateLastPos = function (deltaX, deltaY) {
        lastX = x;
        lastY = y;
      };

      var translate = function (deltaX, deltaY) {
        // We restrict to the min of the viewport width/height or current width/height as the
        // current width/height may be smaller than the viewport width/height

        var newX = restrictRawPos(lastX + deltaX/scale,
                                  Math.min(viewportWidth, curWidth), imgWidth);
        x = newX;
        img.style.marginLeft = Math.ceil(newX*scale) + 'px';

        var newY = restrictRawPos(lastY + deltaY/scale,
                                  Math.min(viewportHeight, curHeight), imgHeight);
        y = newY;
        img.style.marginTop = Math.ceil(newY*scale) + 'px';
      };

      var zoom = function (scaleBy) {
        scale = restrictScale(lastScale*scaleBy);

        curWidth = imgWidth*scale;
        curHeight = imgHeight*scale;

        img.style.width = Math.ceil(curWidth) + 'px';
        img.style.height = Math.ceil(curHeight) + 'px';

        // Adjust margins to make sure that we aren't out of bounds
        translate(0, 0);
      };

      var rawCenter = function (e) {
        var pos = absolutePosition(container);

        // We need to account for the scroll position
        var scrollLeft = window.pageXOffset ? window.pageXOffset : document.body.scrollLeft;
        var scrollTop = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;

        var zoomX = -x + (e.center.x - pos.x + scrollLeft)/scale;
        var zoomY = -y + (e.center.y - pos.y + scrollTop)/scale;

        return { x: zoomX, y: zoomY };
      };

      var updateLastScale = function () {
        lastScale = scale;
      };

      var zoomAround = function (scaleBy, rawZoomX, rawZoomY, doNotUpdateLast) {
        // Zoom
        zoom(scaleBy);

        // New raw center of viewport
        var rawCenterX = -x + Math.min(viewportWidth, curWidth)/2/scale;
        var rawCenterY = -y + Math.min(viewportHeight, curHeight)/2/scale;

        // Delta
        var deltaX = (rawCenterX - rawZoomX)*scale;
        var deltaY = (rawCenterY - rawZoomY)*scale;

        // Translate back to zoom center
        translate(deltaX, deltaY);

        if (!doNotUpdateLast) {
          updateLastScale();
          updateLastPos();
        }
      };

      var zoomCenter = function (scaleBy) {
        // Center of viewport
        var zoomX = -x + Math.min(viewportWidth, curWidth)/2/scale;
        var zoomY = -y + Math.min(viewportHeight, curHeight)/2/scale;

        zoomAround(scaleBy, zoomX, zoomY);
      };

      var zoomIn = function () {
        zoomCenter(2);
      };

      var zoomOut = function () {
        zoomCenter(1/2);
      };

      var onLoad = function () {

        img = document.getElementById('pinch-zoom-image-id');
        container = img.parentElement;

        disableImgEventHandlers();

        imgWidth = img.width;
        imgHeight = img.height;
        viewportWidth = img.offsetWidth;
        scale = viewportWidth/imgWidth;
        lastScale = scale;
        viewportHeight = img.parentElement.offsetHeight;
        curWidth = imgWidth*scale;
        curHeight = imgHeight*scale;

        var hammer = new Hammer(container, {
          domEvents: true
        });

        hammer.get('pinch').set({
          enable: true
        });

        hammer.on('pan', function (e) {
          translate(e.deltaX, e.deltaY);
        });

        hammer.on('panend', function (e) {
          updateLastPos();
        });

        hammer.on('pinch', function (e) {

          // We only calculate the pinch center on the first pinch event as we want the center to
          // stay consistent during the entire pinch
          if (pinchCenter === null) {
            pinchCenter = rawCenter(e);
            var offsetX = pinchCenter.x*scale - (-x*scale + Math.min(viewportWidth, curWidth)/2);
            var offsetY = pinchCenter.y*scale - (-y*scale + Math.min(viewportHeight, curHeight)/2);
            pinchCenterOffset = { x: offsetX, y: offsetY };
          }

          // When the user pinch zooms, she/he expects the pinch center to remain in the same
          // relative location of the screen. To achieve this, the raw zoom center is calculated by
          // first storing the pinch center and the scaled offset to the current center of the
          // image. The new scale is then used to calculate the zoom center. This has the effect of
          // actually translating the zoom center on each pinch zoom event.
          var newScale = restrictScale(scale*e.scale);
          var zoomX = pinchCenter.x*newScale - pinchCenterOffset.x;
          var zoomY = pinchCenter.y*newScale - pinchCenterOffset.y;
          var zoomCenter = { x: zoomX/newScale, y: zoomY/newScale };

          zoomAround(e.scale, zoomCenter.x, zoomCenter.y, true);
        });

        hammer.on('pinchend', function (e) {
          updateLastScale();
          updateLastPos();
          pinchCenter = null;
        });

        hammer.on('doubletap', function (e) {
          var c = rawCenter(e);
          zoomAround(2, c.x, c.y);
        });

      };

    </script>

    <button onclick="zoomIn()">Zoom In</button>
    <button onclick="zoomOut()">Zoom Out</button>

    <div class="pinch-zoom-container">
      <img id="pinch-zoom-image-id" class="pinch-zoom-image" onload="onLoad()"
           src="https://hammerjs.github.io/assets/img/pano-1.jpg">
    </div>


  </div>

</body>
</html>


4

Herhangi bir öğe üzerinde iki parmağınızı sıkıştırarak yakınlaştırmayı algılayın, Hammer.js gibi üçüncü taraf kitaplarla kolay ve güçlük çekmeyin (dikkatli olun, çekiç kaydırmada sorun yaşar!)

function onScale(el, callback) {
    let hypo = undefined;

    el.addEventListener('touchmove', function(event) {
        if (event.targetTouches.length === 2) {
            let hypo1 = Math.hypot((event.targetTouches[0].pageX - event.targetTouches[1].pageX),
                (event.targetTouches[0].pageY - event.targetTouches[1].pageY));
            if (hypo === undefined) {
                hypo = hypo1;
            }
            callback(hypo1/hypo);
        }
    }, false);


    el.addEventListener('touchend', function(event) {
        hypo = undefined;
    }, false);
}

1
Kullanmak daha iyidir gibi görünüyor event.touchesdaha event.targetTouches.
TheStoryCoder

1

Bu cevapların hiçbiri aradığımı başaramadı, bu yüzden kendim bir şeyler yazdım. MacBookPro izleme dörtgenimi kullanarak web sitemdeki bir görüntüyü parmaklarınızla sıkıştırarak yakınlaştırmak istedim. Aşağıdaki kod (jQuery gerektirir) en azından Chrome ve Edge'de çalışıyor gibi görünüyor. Belki bu başkası için faydalı olacaktır.

function setupImageEnlargement(el)
{
    // "el" represents the image element, such as the results of document.getElementByd('image-id')
    var img = $(el);
    $(window, 'html', 'body').bind('scroll touchmove mousewheel', function(e)
    {
        //TODO: need to limit this to when the mouse is over the image in question

        //TODO: behavior not the same in Safari and FF, but seems to work in Edge and Chrome

        if (typeof e.originalEvent != 'undefined' && e.originalEvent != null
            && e.originalEvent.wheelDelta != 'undefined' && e.originalEvent.wheelDelta != null)
        {
            e.preventDefault();
            e.stopPropagation();
            console.log(e);
            if (e.originalEvent.wheelDelta > 0)
            {
                // zooming
                var newW = 1.1 * parseFloat(img.width());
                var newH = 1.1 * parseFloat(img.height());
                if (newW < el.naturalWidth && newH < el.naturalHeight)
                {
                    // Go ahead and zoom the image
                    //console.log('zooming the image');
                    img.css(
                    {
                        "width": newW + 'px',
                        "height": newH + 'px',
                        "max-width": newW + 'px',
                        "max-height": newH + 'px'
                    });
                }
                else
                {
                    // Make image as big as it gets
                    //console.log('making it as big as it gets');
                    img.css(
                    {
                        "width": el.naturalWidth + 'px',
                        "height": el.naturalHeight + 'px',
                        "max-width": el.naturalWidth + 'px',
                        "max-height": el.naturalHeight + 'px'
                    });
                }
            }
            else if (e.originalEvent.wheelDelta < 0)
            {
                // shrinking
                var newW = 0.9 * parseFloat(img.width());
                var newH = 0.9 * parseFloat(img.height());

                //TODO: I had added these data-attributes to the image onload.
                // They represent the original width and height of the image on the screen.
                // If your image is normally 100% width, you may need to change these values on resize.
                var origW = parseFloat(img.attr('data-startwidth'));
                var origH = parseFloat(img.attr('data-startheight'));

                if (newW > origW && newH > origH)
                {
                    // Go ahead and shrink the image
                    //console.log('shrinking the image');
                    img.css(
                    {
                        "width": newW + 'px',
                        "height": newH + 'px',
                        "max-width": newW + 'px',
                        "max-height": newH + 'px'
                    });
                }
                else
                {
                    // Make image as small as it gets
                    //console.log('making it as small as it gets');
                    // This restores the image to its original size. You may want
                    //to do this differently, like by removing the css instead of defining it.
                    img.css(
                    {
                        "width": origW + 'px',
                        "height": origH + 'px',
                        "max-width": origW + 'px',
                        "max-height": origH + 'px'
                    });
                }
            }
        }
    });
}

0

Cevabım Jeffrey'nin cevabından ilham alıyor. Bu cevabın daha soyut bir çözüm verdiği yerde, onu potansiyel olarak nasıl uygulayacağımıza dair daha somut adımlar sağlamaya çalışıyorum. Bu, daha zarif bir şekilde uygulanabilecek bir rehberdir. Daha ayrıntılı bir örnek için MDN web belgelerinin hazırladığı bu eğiticiye bakın .

HTML:

<div id="zoom_here">....</div>

JS

<script>
var dist1=0;
function start(ev) {
           if (ev.targetTouches.length == 2) {//check if two fingers touched screen
               dist1 = Math.hypot( //get rough estimate of distance between two fingers
                ev.touches[0].pageX - ev.touches[1].pageX,
                ev.touches[0].pageY - ev.touches[1].pageY);                  
           }
    
    }
    function move(ev) {
           if (ev.targetTouches.length == 2 && ev.changedTouches.length == 2) {
                 // Check if the two target touches are the same ones that started
               var dist2 = Math.hypot(//get rough estimate of new distance between fingers
                ev.touches[0].pageX - ev.touches[1].pageX,
                ev.touches[0].pageY - ev.touches[1].pageY);
                //alert(dist);
                if(dist1>dist2) {//if fingers are closer now than when they first touched screen, they are pinching
                  alert('zoom out');
                }
                if(dist1<dist2) {//if fingers are further apart than when they first touched the screen, they are making the zoomin gesture
                   alert('zoom in');
                }
           }
           
    }
        document.getElementById ('zoom_here').addEventListener ('touchstart', start, false);
        document.getElementById('zoom_here').addEventListener('touchmove', move, false);
</script>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.