Как сделать так, чтобы пользовательский ввод диапазона работал на сенсорных экранах? - PullRequest
0 голосов
/ 03 апреля 2020

У меня есть пользовательский ползунок диапазона.

Однако проблема, с которой я сталкиваюсь, заключается в том, что мне не удается заставить скользящее движение «большого пальца» (в данном случае ?) работать на сенсорных устройствах.

Вот проект в прямом эфире: https://wagon-city-guides.herokuapp.com/spots/32

Если вы проверите его на своем мобильном телефоне (я использую iPhone), вы все равно увидеть границу оригинального большого пальца (пока я специально его оставил), который скользит и работает, но пламя (пользовательский большой палец) не появляется ... в то время как оно отлично работает для устройств щелчка.

Я ищу только решение Vanilla JS. 101

Вот мой код:

class RatingSlider {
  constructor() {
    this.ratingSliderForm = document.querySelector(".js-rating-slider-form");
    this.ratingSliderInput = document.querySelector(".js-rating-slider-input");
    this.ratingSliderThumb = document.querySelector(".js-rating-slider-thumb");
    this.ratingSliderValue = document.querySelector(".js-rating-slider-value");
    this.ratingSliderIcon = document.querySelector(".js-rating-slider-icon");
    this.isPressed = false;
    this.moveEvent;
    this.holdEvent;
    this.releaseEvent;
    this.bind();
  }

  handleSliding(event) {
    if (!this.isPressed) {
      return;
    }
    if (
      event.offsetX > 0 &&
      event.offsetX < this.ratingSliderInput.offsetWidth
    ) {
      this.ratingSliderThumb.style.left = `${event.offsetX - 10}px`;
      this.ratingSliderIcon.style.transform = `scale(${1 +
        this.ratingSliderInput.value / 150})`;
      this.ratingSliderValue.innerText = `${this.ratingSliderInput.value}°`;
    }
  }

  setRating() {
    this.ratingSliderThumb.style.left = `${(this.ratingSliderInput.offsetWidth /
      100) *
      this.ratingSliderInput.value -
      10}px`;
    this.ratingSliderIcon.style.transform = `scale(${1 +
      this.ratingSliderInput.value / 150})`;
    this.ratingSliderValue.innerText = `${this.ratingSliderInput.value}°`;
    this.ratingSliderInput.addEventListener(
      `${this.holdEvent}`,
      () => (this.isPressed = true)
    );
    this.ratingSliderInput.addEventListener(`${this.releaseEvent}`, () => {
      this.isPressed = false;
      this.ratingSliderForm.submit();
    });
  }

  setEvents() {
    if ("ontouchstart" in document.documentElement) {
      this.moveEvent = "touchmove";
      this.holdEvent = "touchstart";
      this.releaseEvent = "touchend";
    } else {
      this.moveEvent = "mousemove";
      this.holdEvent = "mousedown";
      this.releaseEvent = "mouseup";
    }
  }

  bind() {
    if (!this.ratingSliderForm) {
      return;
    }
    this.setEvents();
    this.setRating();
    this.ratingSliderInput.addEventListener(`${this.moveEvent}`, event =>
      this.handleSliding(event)
    );
  }
}

export default RatingSlider;

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Проблема в том, что у сенсорных событий нет свойств offsetX и offsetY. Их значения возвращаются undefined в мобильных устройствах. Итак, вам нужно добавить их к событию.

В самом начале метода handleSliding(event) добавьте:

if ("ontouchstart" in document.documentElement) {
  event = addOffsetsOnTouch(event);
}

function addOffsetsOnTouch(e) {
  let touch = e.touches[0] || event.changedTouches[0];
  let target = document.elementFromPoint(touch.clientX, touch.clientY);
  event.offsetX = touch.clientX - target.getBoundingClientRect().x;
  event.offsetY = touch.clientY - target.getBoundingClientRect().y
  return e;
}
0 голосов
/ 10 апреля 2020

После ответа Азаметзин Я изменил метод обработки смещения для сенсорных и не сенсорных устройств следующим образом:

  handleOffsetOnChange(event) {
    if ("ontouchstart" in document.documentElement) {
      let touch = event.touches[0];
      let target = this.ratingSliderInput;
      event.offsetX = touch.clientX - target.getBoundingClientRect().x;
    }
    if (
      event.offsetX > 0 &&
      event.offsetX < this.ratingSliderInput.offsetWidth
    ) {
      this.ratingSliderThumb.style.left = `${event.offsetX - 10}px`;
    }
  }

Вот весь файл:

class RatingSlider {
  constructor() {
    this.ratingSliderForm = document.querySelector(".js-rating-slider-form");
    this.ratingSliderInput = document.querySelector(".js-rating-slider-input");
    this.ratingSliderThumb = document.querySelector(".js-rating-slider-thumb");
    this.ratingSliderValue = document.querySelector(".js-rating-slider-value");
    this.ratingSliderIcon = document.querySelector(".js-rating-slider-icon");
    this.isPressed = false;
    this.setEvents();
    this.setPositionThumb();
    this.setThumbStyle();
    this.bind();
  }
  setEvents() {
    this.moveEvent;
    this.startEvent;
    this.endEvent;
    if ("ontouchstart" in document.documentElement) {
      this.moveEvent = "touchmove";
      this.startEvent = "touchstart";
      this.endEvent = "touchend";
    } else {
      this.moveEvent = "mousemove";
      this.startEvent = "mousedown";
      this.endEvent = "mouseup";
    }
  }

  setThumbStyle() {
    this.ratingSliderIcon.style.transform = `scale(${1 +
      this.ratingSliderInput.value / 150})`;
    this.ratingSliderValue.innerText = `${this.ratingSliderInput.value}°`;
  }

  setPositionThumb() {
    this.ratingSliderThumb.style.left = `${(this.ratingSliderInput.offsetWidth /
      100) *
      this.ratingSliderInput.value -
      10}px`;
  }

  handleOffsetOnChange(event) {
    if ("ontouchstart" in document.documentElement) {
      let touch = event.touches[0];
      let target = this.ratingSliderInput;
      event.offsetX = touch.clientX - target.getBoundingClientRect().x;
    }
    if (
      event.offsetX > 0 &&
      event.offsetX < this.ratingSliderInput.offsetWidth
    ) {
      this.ratingSliderThumb.style.left = `${event.offsetX - 10}px`;
    }
  }

  bind() {
    if (!this.ratingSliderForm) {
      return;
    }
    window.addEventListener("resize", () => this.setPositionThumb());

    this.ratingSliderInput.addEventListener(
      this.startEvent,
      () => (this.isPressed = true)
    );

    this.ratingSliderInput.addEventListener(this.endEvent, () => {
      this.isPressed = false;
      this.ratingSliderForm.submit();
    });

    this.ratingSliderInput.addEventListener(this.moveEvent, (event) => {
      if (!this.isPressed) {
        return;
      }
      this.handleOffsetOnChange(event);
      this.setThumbStyle();
    });
  }
}

export default RatingSlider;

...