Der JavaScript hinter Touch-freundlichen Slidern

Avatar of Kevin Foley
Kevin Foley am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Beitrag ist ein Gastbeitrag von Kevin Foley. Kevin ist Entwickler bei Squarespace und beschäftigt sich unter anderem mit deren Entwicklerplattform. Er hat kürzlich an einer wischbaren Bildergalerie gearbeitet und sich bereit erklärt, einige seiner Arbeiten hier zu teilen!

Vor ein paar Wochen hat Chris ein Tutorial zum Erstellen eines Sliders mit gleitenden Hintergrundbildern veröffentlicht. Etwa zur gleichen Zeit arbeitete ich an einigen neuen wischbaren Galerien, also schlug Chris vor, dass ich ein Tutorial schreibe, wie man seinem Slider Wischunterstützung hinzufügt. Hier ist es!

Bei der Erstellung einer wischbaren Galerie gibt es zwei Techniken – soweit ich weiß –, aus denen man wählen kann. Man kann die Scroll-Position animieren oder das Element mit translate bewegen. Jede hat Vor- und Nachteile.

Translate verwenden

Das Verschieben des Sliders mit translate bietet den Vorteil von Hardwarebeschleunigung und Subpixel-Animation. Allerdings kann es beim ersten Touch-Ereignis zu einer kleinen Verzögerung – nur wenige zehn Millisekunden – kommen, bevor der Slider zu wischen beginnt. Dies ist nicht gut dokumentiert, ich habe es einfach in meiner Erfahrung bemerkt.

Overflow Scroll verwenden

Overflow scroll reagiert extrem schnell auf die anfängliche Berührung, da es nativ im Browser integriert ist. Sie müssen nicht auf den Event-Listener in JavaScript warten. Aber Sie verzichten auf die gesamte Glätte des Verschiebens von Elementen mit translate.

Für dieses Tutorial werden wir translate verwenden, da es meiner Meinung nach besser aussieht.

Das HTML

Das HTML in diesem Beispiel unterscheidet sich vom ursprünglichen Beispiel von Chris. Anstatt das Bild als Hintergrundbild festzulegen, werden wir es als Element festlegen. Das ermöglicht es uns, das Bild zu bewegen, um diesen coolen Schwenkeffekt mit translate zu erzielen, anstatt die Hintergrundposition zu animieren.

<div class="slider-wrap">
  <div class="slider" id="slider">
    <div class="holder">
      <div class="slide-wrapper">
        <div class="slide"><img class="slide-image" src="//farm8.staticflickr.com/7347/8731666710_34d07e709e_z.jpg" /></div>
        74
      </div>
      <div class="slide-wrapper">
        <div class="slide"><img class="slide-image" src="//farm8.staticflickr.com/7384/8730654121_05bca33388_z.jpg" /></div>
        64
      </div>
      <div class="slide-wrapper">
        <div class="slide"><img class="slide-image" src="//farm8.staticflickr.com/7382/8732044638_9337082fc6_z.jpg" /></div>
        82
      </div>
    </div>
  </div>
</div>

Das CSS

Zum größten Teil ist das CSS das gleiche wie bei Chris, daher werde ich nicht wiederholen, wie das Layout eingerichtet wird. Aber es gibt ein paar wichtige Unterschiede.

Anstatt einfach nur overflow scroll hinzuzufügen, müssen wir die Slides animieren. Dazu verwenden wir eine Klasse, um den Übergang einzurichten und ihn mit JavaScript hinzuzufügen, wenn wir ihn benötigen.

.animate { transition: transform 0.3s ease-out; }

IE 10 behandelt Touch-Ereignisse anders als mobile Webkit-Browser wie Chrome und Safari. Wir werden die Webkit-Touch-Ereignisse in JavaScript behandeln, aber in IE10 können wir diesen gesamten Slider (fast) mit nichts anderem als CSS erstellen.

.ms-touch.slider {
  overflow-x: scroll;
  overflow-y: hidden;
  
  -ms-overflow-style: none;
  /* Hides the scrollbar. */
  
  -ms-scroll-chaining: none;
  /* Prevents Metro from swiping to the next tab or app. */
  
  -ms-scroll-snap-type: mandatory;
  /* Forces a snap scroll behavior on your images. */
  
  -ms-scroll-snap-points-x: snapInterval(0%, 100%);
  /* Defines the y and x intervals to snap to when scrolling. */
}

Da diese Eigenschaften für die meisten Leute wahrscheinlich neu sind (sie waren es auch für mich), werde ich jede einzelne durchgehen und erklären, was sie tut.

-ms-scroll-chaining

Das Surface-Tablet wechselt die Browser-Tabs, wenn Sie über die Seite wischen, wodurch alle Wischereignisse für Entwickler nutzlos werden. Glücklicherweise ist es sehr einfach, dieses Verhalten zu überschreiben, indem Sie Scroll-Chaining für jedes gegebene Element auf none setzen.

-ms-scroll-snap-type

Wenn diese Eigenschaft auf mandatory gesetzt ist, überschreibt sie das Standard-Scrollverhalten des Browsers und zwingt ein scrollbares Element dazu, an einem bestimmten Intervall zu stoppen.

-ms-scroll-snap-points-x

Diese Eigenschaft legt die Intervalle fest, an denen das scrollbare Element einrastet. Sie akzeptiert zwei Zahlen: die erste Zahl ist der Startpunkt; die zweite ist das Snap-Intervall. In diesem Beispiel ist jeder Slide die volle Breite des übergeordneten Elements, was bedeutet, dass das Intervall 100% betragen sollte – d.h. das Element rastet bei 100%, 200%, 300% und so weiter ein.

-ms-overflow-style

Diese Eigenschaft ermöglicht es Ihnen, die Scrollleiste zu verbergen, wenn Sie sie auf none setzen.

Das JavaScript

Das Erste, was wir in JavaScript tun müssen, ist, zu erkennen, welche Art von Touch-Gerät wir verwenden. IE 10 verwendet Pointer-Ereignisse, während Webkit "touchstart", "touchmove" und "touchend" hat. Da der IE 10-Slider (fast) komplett in CSS ist, müssen wir das erkennen und dem Wrapper eine Klasse hinzufügen.

if (navigator.msMaxTouchPoints) {
  $('#slider').addClass('ms-touch');
}

Ziemlich einfach. Wenn Sie den Slider bis zu diesem Zeitpunkt testen würden, wäre es eine funktionierende wischbare Diashow. Aber wir müssen immer noch den Schwenkeffekt auf die Bilder hinzufügen.

if (navigator.msMaxTouchPoints) {
  $('#slider').addClass('ms-touch');

  // Listed for the scroll event and move the image with translate.
  $('#slider').on('scroll', function() {
    $('.slide-image').css('transform','translate3d(-' + (100-$(this).scrollLeft()/6) + 'px,0,0)');
  });
}

Und das war's für IE 10.

Nun zum Webkit-Weg. Dies wird alles in der else-Anweisung verpackt. Zuerst müssen wir nur ein paar Variablen definieren.

else {
  var slider = {

    // The elements.
    el: {
      slider: $("#slider"),
      holder: $(".holder"),
      imgSlide: $(".slide-image")
    },

    // The stuff that makes the slider work.
    slideWidth: $('#slider').width(), // Calculate the slider width.

    // Define these as global variables so we can use them across the entire script.
    touchstartx: undefined,
    touchmovex: undefined, 
    movex: undefined,
    index: 0,
    longTouch: undefined,
    // etc

Dann müssen wir die Funktion initialisieren und die Ereignisse definieren.

    // continued

    init: function() {
      this.bindUIEvents();
    },

    bindUIEvents: function() {

      this.el.holder.on("touchstart", function(event) {
        slider.start(event);
      });

      this.el.holder.on("touchmove", function(event) {
        slider.move(event);
      });

      this.el.holder.on("touchend", function(event) {
        slider.end(event);
      });

    },

Nun zu den spaßigen Dingen, die tatsächlich Dinge passieren lassen, wenn man den Slider wischt.

Touchstart

Auf dem iPhone (und den meisten anderen Touch-Slidern) springt der Slider zurück in seine ursprüngliche Position, wenn Sie ihn langsam, nur ein wenig, bewegen. Aber wenn Sie es schnell tun, wird er zum nächsten Slide weitergeschaltet. Diese schnelle Bewegung wird als "Flick" bezeichnet. Und es gibt keine native Möglichkeit, einen Flick zu erkennen, daher müssen wir einen kleinen Trick anwenden. Bei touchstart initialisieren wir eine setTimeout-Funktion und setzen eine Variable nach einer bestimmten Zeit.

this.longTouch = false;
setTimeout(function() {
  // Since the root of setTimout is window we can’t reference this. That’s why this variable says window.slider in front of it.
  window.slider.longTouch = true;
}, 250);

Wir müssen auch die ursprüngliche Position der Berührung ermitteln, um unsere Animation zu ermöglichen. Wenn Sie das noch nie gemacht haben, ist es etwas seltsam. JavaScript ermöglicht es Ihnen, Multitouch-Ereignisse zu definieren, sodass Sie dem Touch-Event eine Zahl übergeben können, die die Anzahl der Finger repräsentiert, auf die Sie achten. Für dieses Beispiel interessiere ich mich wirklich nur für einen Finger/Daumen, daher steht im folgenden Code-Beispiel die [0] dafür.

// Get the original touch position.
this.touchstartx =  event.originalEvent.touches[0].pageX;

Bevor wir beginnen, den Slider zu bewegen, werde ich die animate-Klasse entfernen. Ich weiß, dass es im Moment keine animate-Klasse auf den Elementen gibt, aber wir brauchen sie für die nachfolgenden Slides. Wir fügen sie bei touchend wieder einem Element hinzu.

$('.animate').removeClass('animate');

Touchmove

Das touchmove-Ereignis verhält sich ähnlich wie Scroll-Ereignisse in JavaScript. Das heißt, wenn Sie etwas beim Scrollen tun, wird es mehrmals ausgeführt, während das Scrollen stattfindet. Wir werden also die touchmove-Position kontinuierlich abrufen, während der Finger/Daumen bewegt wird.

// Continuously return touch position.
this.touchmovex =  event.originalEvent.touches[0].pageX;

Dann führen wir eine schnelle Berechnung durch, indem wir die touchstart-Position, die wir im letzten Ereignis erhalten haben, und die touchmove-Position verwenden, um herauszufinden, wie der Slide verschoben werden soll.

// Calculate distance to translate holder.
this.movex = this.index*this.slideWidth + (this.touchstartx - this.touchmovex);

Dann müssen wir das Bild schwenken, wie Chris es im Originalbeispiel getan hat. Wir verwenden dieselben magischen Zahlen wie er.

// Defines the speed the images should move at.
var panx = 100-this.movex/6;

Jetzt müssen wir eine Logik einbauen, um Randfälle zu behandeln. Wenn Sie sich auf dem ersten oder letzten Slide befinden, stoppt diese Logik das Schwenken des Bildes, wenn Sie in die falsche Richtung scrollen (d.h. in Richtung kein Inhalt). Dies ist vielleicht nicht die beste Methode, um dies zu handhaben, aber für mich funktioniert es im Moment.

if (this.movex < 600) { // Makes the holder stop moving when there is no more content.
  this.el.holder.css('transform','translate3d(-' + this.movex + 'px,0,0)');
}
if (panx < 100) { // Corrects an edge-case problem where the background image moves without the container moving.
  this.el.imgSlide.css('transform','translate3d(-' + panx + 'px,0,0)');
 }

Touchend

Im touchend-Ereignis müssen wir herausfinden, wie weit der Benutzer den Slide bewegt hat, mit welcher Geschwindigkeit und ob diese Aktion zum nächsten Slide weitergeschaltet werden soll.

Zuerst müssen wir genau sehen, wie groß die zurückgelegte Distanz des Wischvorgangs war. Wir berechnen den Absolutwert der zurückgelegten Distanz, um zu sehen, ob der Benutzer gewischt hat.

// Calculate the distance swiped.
var absMove = Math.abs(this.index*this.slideWidth - this.movex);

Jetzt ermitteln wir, ob der Slider weitergeschaltet werden soll. Alle anderen Berechnungen in diesem Beispiel basieren auf der Index-Variable, also ist dies die eigentliche Logik hinter dem Skript. Es prüft, ob der Benutzer den Mindestabstand gewischt hat, um den Slider weiterzuschalten, oder ob die Bewegung ein Flick war. Und wenn eines dieser beiden Kriterien erfüllt ist, in welche Richtung ging der Wischvorgang.

// Calculate the index. All other calculations are based on the index.
if (absMove > this.slideWidth/2 || this.longTouch === false) {
  if (this.movex > this.index*this.slideWidth && this.index < 2) {
    this.index++;
  } else if (this.movex < this.index*this.slideWidth && this.index > 0) {
    this.index--;
  }
}

Nun fügen wir die animate-Klasse hinzu und setzen die neue Translate-Position.

// Move and animate the elements.
this.el.holder.addClass('animate').css('transform', 'translate3d(-' + this.index*this.slideWidth + 'px,0,0)');
this.el.imgSlide.addClass('animate').css('transform', 'translate3d(-' + 100-this.index*50 + 'px,0,0)');

Fazit

Ja, Hurra für IE 10.

Aber im Ernst, das Erstellen von wischbaren Galerien ist eine ziemliche Plage, wenn man bedenkt, wie verbreitet dieses Idiom auf mobilen Betriebssystemen ist. Und das Endergebnis wird nicht so gut sein wie natives Wischen. Aber es wird nah dran sein.

Hier ist die vollständige Demo

Check out this Pen!

Am besten probieren Sie es hier auf Ihrem Touch-Gerät aus.