javascript - 触摸图像 slider : next/prev keys interfere with code for current slide

标签 javascript css slider

我有一个图像 slider ,可以通过鼠标拖放或手指在触摸屏上滑动来移动到下一张或上一张图像。到目前为止,一切都很好。 但是由于使用笔记本电脑触摸板滑动相当棘手,所以我毕竟想添加下一个/上一个按钮。现在我的问题是他们弄乱了触摸和鼠标手势的代码。

导致的错误是:

  • 在页面首次加载之后和任何触摸或鼠标手势之前, 下一个按钮需要点击两次才能更改幻灯片
  • 在任何方向的鼠标或触摸事件之后,下一个或上一个
    按钮需要被点击两次才能改变幻灯片
  • 到达 slider 的末端然后滑动回到起点后,无法到达第一张幻灯片( slider 停在幻灯片 2)
  • 这可能与此无关,而是另一个已知错误:有时幻灯片在尝试滑动时会粘在光标上。我在 Chrome 中遇到过这种情况,偶尔在 Opera 中遇到过,但在 Firefox 中没有遇到过,我还不确定为什么会这样。

我使用 console.log 和开发工具来确定和检查这些错误。由于我发现这个 slider 的问题很难解释,所以我制作了一个代码示例。

//  set --n (used for calc in CSS) via JS, after getting
// .container and the number of child images it holds:

const _C = document.querySelector(".slider-container"),
  N = _C.children.length;

_C.style.setProperty("--n", N);

// detect the direction of the motion between "touchstart" (or "mousedown") event
// and the "touched" (or "mouseup") event
// and then update --i (current slide) accordingly
// and move the container so that the next image in the desired direction moves into the viewport

// on "mousedown"/"touchstart", lock x-coordiate
// and store it into an initial coordinate variable x0:
let x0 = null;
let locked = false;

function lock(e) {
  x0 = unify(e).clientX;
  // remove .smooth class
  _C.classList.toggle("smooth", !(locked = true));
}

// next, make the images move when the user swipes:
// was the lock action performed aka is x0 set?
// if so, read current x coordiante and compare it to x0
// from the difference between these two determine what to do next

let i = 0; // counter
let w; //image width

// update image width w on resive
function size() {
  w = window.innerWidth;
}

function move(e) {
  if (locked) {
    // set threshold of 20% (if less, do not drag to the next image)
    // dx = number of pixels the user dragged
    let dx = unify(e).clientX - x0,
      s = Math.sign(dx),
      f = +(s * dx / w).toFixed(2);

    // Math.sign(dx) returns 1 or -1
    // depending on this, the slider goes backwards or forwards

    if ((i > 0 || s < 0) && (i < N - 1 || s > 0) && f > 0.2) {
      _C.style.setProperty("--i", (i -= s));
      f = 1 - f;
    }

    _C.style.setProperty("--tx", "0px");
    _C.style.setProperty("--f", f);
    _C.classList.toggle("smooth", !(locked = false));
    x0 = null;
  }
}

size();

addEventListener("resize", size, false);

// ===============
// drag-animation for the slider when it reaches the end
// ===============

function drag(e) {
  e.preventDefault();

  if (locked) {
    _C.style.setProperty("--tx", `${Math.round(unify(e).clientX - x0)}px`);
  }
}

// ===============
// prev, next
// ===============
let prev = document.querySelector(".prev");
let next = document.querySelector(".next");

prev.addEventListener("click", () => {
  if (i == 0) {
    console.log("start reached");
  } else if (i > 0) {
    // decrease i as long as it is bigger than the number of slides
    _C.style.setProperty("--i", i--);
  }
});

next.addEventListener("click", () => {
  if (i < N) {
    // increase i as long as it's smaller than the number of slides
    _C.style.setProperty("--i", i++);
  }
});

// ===============
// slider event listeners for mouse and touch (start, move, end)
// ===============

_C.addEventListener("mousemove", drag, false);
_C.addEventListener("touchmove", drag, false);

_C.addEventListener("mousedown", lock, false);
_C.addEventListener("touchstart", lock, false);

_C.addEventListener("mouseup", move, false);
_C.addEventListener("touchend", move, false);

// override Edge swipe behaviour
_C.addEventListener(
  "touchmove",
  e => {
    e.preventDefault();
  },
  false
);

// unify touch and click cases:
function unify(e) {
  return e.changedTouches ? e.changedTouches[0] : e;
}
/* parent of book-container & container (slider) */
main {
  overflow: hidden;
  display: flex;
  justify-content: space-between;
}

/* wraps entire slider */
.slider-wrapper {
  overflow: hidden;
  width: 100%;
  position: relative;
}

.slider-nav {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  text-align: center;
  margin: 0;
  padding: 1%;
  background: rgba(0,0,0,0.6);
  color: #fff;
}

/* slider controls*/
.control {
  position: absolute;
  top: 50%;
  width: 40px;
  height: 10px;
  color: #fff;
  font-size: 3rem;
  padding: 0;
  margin: 0;
  line-height: 0;
}

.prev,
.next {
  cursor: pointer;
  transition: all 0.2s ease;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
   user-select: none;
  background: rgba(0,0,0,0.3);
  padding: 1rem;
}

.prev {
  left: 1.1rem;
}

.next {
  right: 1.1rem;
}

.prev:hover,
.next:hover {
  transform: scale(1.5,1.5);
}

.slider-container {
  /* 
  n variable holds number of images to make .container wide enough 
  to hold all its image children 
  that still have the same width as its parent
  */
  display: flex;
  align-items: center;
  overflow-y: hidden;
  width: 100%; /* fallback */
  width: calc(var(--n)*100%);
  height: 31vw; 
  max-height: 100vh;
  transform: translate(calc(var(--i, 0)/var(--n)*-100% + var(--tx, 0px)));
}

/* transition animation for the slider */
.smooth { 
  /* f computes actual animation duration via JS */
  transition: transform calc(var(--f, 1)*.5s) ease-out; 
}

/* images for the slider */
img {
  width: 100%; /* can't take this out either as it breaks Chrome */
  width: calc(100%/var(--n));
  pointer-events: none;
}
<div class="slider-wrapper">

  <div class="slider-container">
    <img src="https://source.unsplash.com/featured?technology">
    <img src="https://source.unsplash.com/featured?dogs">
    <img src="https://source.unsplash.com/featured?cats">
    <img src="https://source.unsplash.com/featured?cake">
    <img src="https://source.unsplash.com/featured?birds">
    <img src="https://source.unsplash.com/featured?cities">
  </div>

  <div class="slider-controls">
    <span class="control prev">&larr;</span>
    <span class="control next">&rarr;</span>
  </div>

</div>
<!-- END slider-wrapper -->

如果有人能帮我解决这个问题,那就太好了,这样通过鼠标或触摸滑动幻灯片的代码就不会与下一个/前一个按钮的代码混淆。

最佳答案

我想我已经找到了为您修复代码的快速方法。

prev.addEventListener("click", () => {
  if (i == 0) {
    console.log("start reached");
  } else if (i > 0) {
    // decrease i as long as it is bigger than the number of slides
    _C.style.setProperty("--i", --i);
  }
});

next.addEventListener("click", () => {
  if (i+1 < N) {
    // increase i as long as it's smaller than the number of slides
    _C.style.setProperty("--i", ++i);
    console.warn(i);
  }
});

您在增加计数器的逻辑时遇到了错误。它在“事后”增加和减少。这会导致您的按钮出现问题。

slider 卡住的问题可以通过缩小浏览器窗口、单击并拖动鼠标到窗口外并在窗口外释放来重现。
提示:您应该为 'mouseleave' 事件实现一个监听器。

附言
整个代码可能会简单得多,如果您需要帮助以“更好”地编写它,请随时联系我:) 我会在有空闲时间时尽力提供帮助:)

关于javascript - 触摸图像 slider : next/prev keys interfere with code for current slide,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59429425/

相关文章:

jquery - 悬停图像按钮以获得最快结果的最佳方法是什么 - CSS、jQuery

javascript - 纯 JS 颜色 slider

swift - 如何快速制作垂直 slider ?

javascript - 当值 = 0 时将 'muted speaker' 图标链接到音量 slider ?

javascript - return this.each(function() { } ) 内的 this 值

javascript - AngularJS - 为值(value)增值

javascript - 如何在同一行中使用 console.log(或其他命令)命令在控制台中添加一些文字?

javascript - 比较 JavaScript 中的属性名称

jquery - 隐藏面板在页面加载时不断出现

internet-explorer - IE 8 : Object doesn't support property or method 'getElementsByClassName'