Управление вертикальной прокруткой и горизонтальным считыванием списка в веб-приложении iOS с помощью JavaScript

Я создаю веб-приложение, специально предназначенное для телефонов (в первую очередь iPhone, но Android и WP находятся на горизонте...).

один из экранов содержит прокручиваемый список элементов. Я хотел бы, чтобы список вел себя аналогично встроенному почтовому приложению iOS.

другими словами...

  1. если пользователь касается списка и перемещается вверх или вниз, список прокручивается по вертикали.
  2. если пользователь щелкает вверх или вниз, список прокручивается вертикально с естественным импульсом
  3. если пользователь касается списка и перемещается только left-конкретный элемент скользит влево, показывая кнопку удаления.
  4. главное - список должен прокручиваться или элемент должен скользить, НО НИКОГДА ОБА.

So-важно выяснить, каковы намерения пользователя, что означает, что мне, вероятно, нужно предотвратить любой ответ, пока я не пойму ли пользователь двигает ее пальцем вертикально или горизонтально.

просто установив эти стили CSS в контейнере списка...

overflow-y: auto;
-webkit-overflow-scrolling: touch;

... Я получаю #1 & #2 выше. Итак, мне нужно выяснить, как реализовать #3.

моей первой мыслью было реализовать что-то вроде этого (псевдокод)...

  1. создать touchstart прослушиватель событий в контейнере списка. В обратном вызове сохраните X-и y-координаты начального касания пользователя позиция.
  2. создать touchmove прослушиватель событий в контейнере списка. В обратном вызове выясните, как далеко переместился палец пользователя (например, delta_x и delta_y)
  3. если delta_x и delta_y оба меньше, чем 10 пикселей - ничего не делайте (не прокручивайте список или не сдвигайте элемент) - поскольку мы еще не выяснили, планирует ли пользователь двигаться вверх/вниз или влево / вправо.
  4. если или delta_x или delta_y более 10 пикселей - мы можем предположить, что пользователь переместился достаточно далеко, чтобы выразить свое намерение. Если delta_y > delta_x, предположим, что она движется вверх/вниз и позволяет списку прокручиваться, но не скользит элемент. Если delta_x > delta_y, предположим, что она движется влево / вправо, поэтому мы должны позволить элементу скользить, но не позволять списку прокручиваться.

Я ожидал, что буду использовать event.метод preventDefault() в touchstart или touchmove в контроль, когда прокрутка должна начаться. Е. Г.,

div.addEventListener("touchstart", function(e) {
    touchStart = {
        x: e.touches[0].pageX,
        y: e.touches[0].pageY
    }
}, false);
div.addEventListener("touchmove", function(e) {
    touchNow = {
        x: e.touches[0].pageX,
        y: e.touches[0].pageY
    }
    var
        dx = touchStart.x - touchNow.x,
        dy = touchStart.y - touchNow.y;
    if ((Math.abs(dx) < 10) && (Math.abs(dy) < 10)) {
        // prevent scrolling
        e.preventDefault();
    } else if (Math.abs(dx) > Math.abs(dy) < 10) {
        // moving right/left - slide item
    } else {
        // moving up/down - allow scrolling
    }
}, false);

однако-это не работает. Независимо от того, как далеко вы двигаетесь, список никогда свитки.

очевидно-я не понимаю, что вызывает прокрутку и какое событие.в этом контексте предполагается, что preventDefault ().

Итак-есть ли способ выполнить то, что я ищу?

Я надеюсь на чистое решение JavaScript (поэтому я понимаю его лучше), но подход jQuery было бы неплохо. Я определенно надеюсь избежать плагина / библиотеки / фреймворка jQuery, если это вообще возможно...

спасибо заранее!

3 ответов


Я не предлагаю изобретать велосипед. Существует несколько библиотек, которые поддерживают обнаружение жестов. Из которых я предлагаю использовать молоток.js для обнаружения события касания.

он не имеет никаких зависимостей, и он маленький, только 3.96 kB minified + gzipped!

и все дело в обработке touch событий, ничего больше.

в вашем случае, Hammer имеет встроенный swipe обнаружение.

вы можете настроить жест салфетки по умолчанию, указав:

  • направление: направление, в котором вы хотите обнаружить жеста (подробнее)
  • порог: минимальное расстояние, необходимое для распознавания
  • скорость: минимальная скорость, необходимая для распознавания (блок в px в ms)

    и больше.

ниже простой пример: (фрагмент стека, похоже, имеет некоторые проблемы с эмуляцией сенсорных событий, скрипка работает нормально):

var myElement = document.getElementById("container");
var hammertime = new Hammer(myElement, {});
hammertime.get('swipe').set({
  direction: 2 // 2 stands for left
})
hammertime.on('swipe', function(event) {
  event.target.querySelector(".delete").classList.add("show");
});
* {
  margin: 0;
  padding: 0;
}
#container {
  width: 250px;
  height: 300px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: dodgerblue;
}
#list {
  height: 100%;
  list-style: none;
}
#list li {
  position: relative;
  box-sizing: border-box;
  width: 100%;
  height: 50px;
  border: 2px solid #fff;
}
#list li span.delete {
  display: inline-block;
  position: absolute;
  left: 250px;
  width: 50px;
  height: 40px;
  line-height: 40px;
  margin: 3px;
  text-align: center;
  background: #fff;
  transition: left 0.5s ease-in;
}
#list li span.delete.show {
  left: 170px;
}
<script src="http://cdn.jsdelivr.net/hammerjs/2.0.4/hammer.min.js"></script>
<div id="container">
  <ul id="list">
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
    <li class="item"><span class="delete">&#215;</span>
    </li>
  </ul>
</div>

Если вам больше интересно узнать , как работает сенсорное событие, а не получить работу, то я предлагаю посмотреть под капотом молотка.


есть немного молоток.JS и jQuery плагин также, для тех, кто не может расстаться с jQuery.


у меня была аналогичная проблема, когда я реализовывал слайд-шоу. Это решение, которое сработало для меня, надеюсь, оно поможет вам:

function touchStart(event){
   if (!event) event = window.event;
   if(!touched){ 
      touched = true;
      var touchObj = event.changedTouches[0];
      touchXPos  = parseInt(touchObj.clientX);
      touchYPos  = parseInt(touchObj.clientY);
   }    
   return false;
}

когда пользователь касается экрана, cooridnate позиции хранятся в touchXPos, touchYPos и логическая переменная "Touch" имеет значение true.

function touchMove(event){
   if (!event) event = window.event;

   if(touched){
       var touchObj = event.changedTouches[0];
       var distanceX = touchXPos - parseInt(touchObj.clientX);
       var distanceY = touchYPos - parseInt(touchObj.clientY);


   if(!touchDirection) {
       if(Math.abs(distanceX) > Math.abs(distanceY)){
          if (distanceX > 0)  touchDirection = "right";
          else if (distanceX < 0) touchDirection = "left";
       }

       else{
          if (distanceY > 0) { touchDirection = "up"; }
          else if (distanceY < 0) {touchDirection = "down"; }
       }
   } 

   if (((touchDirection == "right") )|| ((touchDirection == "left"))){

       //update touchDirection
       if(Math.abs(distanceX) > Math.abs(distanceY)){
          if (distanceX > 0)  touchDirection = "right";
          else if (distanceX < 0) touchDirection = "left";
       }

       touchXPos = parseInt(touchObj.clientX);
       slideshow_mask.scrollLeft += distanceX;
   }

   else if (((touchDirection == "up") ) || ((touchDirection == "down") )){
       return false;
   }

   event.preventDefault();
   return false;
}

подобно вашему подходу, я определяю Дельта-x и Дельта-y всякий раз, когда движется прикосновение, которое я назвал distanceX и distanceY. Когда это первый " ход " я определить touchdirection, что может быть вправо, влево, вверх или вниз. Следующее движение, которое регистрируется, может остаться только в течение этого начального Копли, т. е. вверх/вниз или влево/вправо. Таким образом, если начальное направление было, например, "вправо", пользователь может скользить только горизонтально (влево или вправо), но никогда вверх/вниз.

поскольку мой код был разработан для перемещения слайд-шоу по горизонтали, меня просто заинтересовало горизонтальное движение (- >в фрагменте кода Вы можете видеть, что я обновил направление (слева, справа), сохранил новую xPosition и переместил слайд-шоу в этом направлении), но не должно быть слишком сложно адаптировать его к вертикальной прокрутке.


У меня была такая же задача, и я написал чистый Javascript lib спер.js горизонтальная прокрутка списка