Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Touch события (https://javascript.ru/forum/events/78566-touch-sobytiya.html)

Spirtikys 04.10.2019 16:03

Touch события
 
Доброго времени суток.
Суть в том, что у нас есть возможность отлавливать touch события, но в то же время, не могу понять, как просто можно понять, что палец, ушел с конкретного дом элемента.
Сначала на нем срабатывает touch start, при видении пальца внутри элемента отрабатывает touch move, как только я выхожу из элемента, touch move продолжает работать (проверенно на последнем Chrome), touch end возникает в момент как палец был поднят с экрана.
Если простая возможность, как с движением мыши (mouseover, mouseout)?

Nexus 05.10.2019 13:45

У TouchEvent's есть свойство touches, которое хранит список Touch'ей.
Каждый Touch хранит координаты и элемент, на котором произошло событие.
<div class="outside">
  <div class="inside"></div>
</div>
<style>.inside {
  max-width:30%;
  min-height: 30vh;
  margin: auto;
  background-color: gray;
}</style>
<script>(() => {
  const inside = document.querySelector('.inside');
  const styles = getComputedStyle(inside, null);
  const coordinates = {
    x: [
      inside.offsetLeft, 
      inside.offsetLeft + parseFloat(styles.width.replace('px', ''))
    ],
    y: [
      inside.offsetTop, 
      inside.offsetTop + parseFloat(styles.height.replace('px', ''))
    ]
  };

  document.addEventListener('touchmove', function (event) {
    const hovered = [].some.call(event.touches, touch => {
      const xInside = touch.clientX >= coordinates.x[0] && touch.clientX <= coordinates.x[1];
      const yInside = touch.clientY >= coordinates.y[0] && touch.clientY <= coordinates.y[1];

      return xInside && yInside;
    });

    inside.style.backgroundColor = hovered ? 'green' : 'red';
  });
})()</script>

рони 05.10.2019 16:26

Nexus,
что-то у меня координаты не совпадают.
предложу такой вариант ...

<!DOCTYPE html>

<html>
<head>
  <title>Untitled</title>
  <meta charset="utf-8">

</head>

<body>
<div class="outside">
  <div class="inside"></div>
</div>
<style>
html{
    height: 1800px;
    width: 1800px;
}
.outside {
    padding: 8px;
    background-color: #FFD700;
}

.inside {
  max-width:30%;
  min-height: 30vh;
  margin: auto;
  background-color: gray;
}</style>
<script>
(() => {
  const inside = document.querySelector('.inside');
  document.addEventListener('touchmove', function (event) {
  const box = inside.getBoundingClientRect();
  const coordinates = {
    x: [
      box.left,
      box.left + box.width
    ],
    y: [
      box.top,
      box.top + box.height
    ]
  };
  const hovered = [...event.touches].some(touch => {  console.log(touch)
  const xInside = touch.clientX >= coordinates.x[0] && touch.clientX <= coordinates.x[1];
  const yInside = touch.clientY >= coordinates.y[0] && touch.clientY <= coordinates.y[1];
      return xInside && yInside;
  });

    inside.style.backgroundColor = hovered ? 'green' : 'red';
  });
})()</script>

</body>
</html>

Malleys 06.10.2019 20:51

Цитата:

Сообщение от Spirtikys
Есть ли... возможность, как с движением мыши (mouseover, mouseout)?

Вы можете описáть, как должны обрабатываться события mouseover и mouseout. Затем добавить обобщённый код, который переводит прикосновения к этим событиям.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<style>

html {
	background: black;
	color: white;
}

section#images {
	display: grid;
	grid-template: repeat(3, 200px) / repeat(3, 200px);
}

@media(max-width: 620px) {
	section#images {
		grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
		grid-auto-rows: 200px;
	}
}

section#images > img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	filter: grayscale(1) opacity(0.5);
	transition: 300ms filter;
}

section#images > img.focus {
	filter: none;
}

	</style>
</head>
<body>
	<section id="images">
		<img src="https://picsum.photos/id/237/400/400">
		<img src="https://picsum.photos/id/238/400/400">
		<img src="https://picsum.photos/id/239/400/400">
		<img src="https://picsum.photos/id/247/400/400">
		<img src="https://picsum.photos/id/248/400/400">
		<img src="https://picsum.photos/id/249/400/400">
		<img src="https://picsum.photos/id/257/400/400">
		<img src="https://picsum.photos/id/258/400/400">
		<img src="https://picsum.photos/id/259/400/400">
	</section>
	<button onclick="
		this.textContent = document.fullscreenElement ?
			'↗️ View Fullscreen' : '↙️ Exit Fullscreen';
		document.fullscreenElement ?
			document.exitFullscreen() :
			document.documentElement.requestFullscreen();
	">↗️ View Fullscreen</button>
	<script>

addEventListener("mouseover", function(event) {
	event.target.classList.add("focus");
});

addEventListener("mouseout", function(event) {
	event.target.classList.remove("focus");
});

/* dispatch `mouseover` and `mouseout` events for touch device */
{
function dispatchMouseEventAtTarget(type, node, eventInitDict = {}) {
	return dispatchEvent(Object.defineProperties(new MouseEvent(type, eventInitDict), {
		target: { value: node }
	}));
}
const map = new Map();
for(const type of ["start", "move", "end", "cancel"])
	addEventListener("touch" + type, function(event) {
		if(event instanceof TouchEvent) {
			for(const touch of event.changedTouches) {
				if(event.type === "touchstart") {
					map.set(touch.identifier, touch.target);
					dispatchMouseEventAtTarget("mouseover", event.target);
				} else if(event.type === "touchmove") {
					const currentTarget = map.get(touch.identifier);
					const x = touch.pageX - window.pageXOffset;
					const y = touch.pageY - window.pageYOffset;
					const target = document.elementFromPoint(x, y);

					if(currentTarget !== target) {
						if(currentTarget) dispatchMouseEventAtTarget("mouseout", currentTarget);
						if(target) dispatchMouseEventAtTarget("mouseover", target);
						map.set(touch.identifier, target);
					}
				} else {
					const currentTarget = map.get(touch.identifier);
					if(currentTarget) setTimeout(dispatchMouseEventAtTarget, 40, "mouseout", currentTarget);
					map.delete(touch.identifier);
				}
			}
		}
	});
}
	</script>
</body>
</html>

рони 06.10.2019 21:23

Цитата:

Сообщение от Malleys
Вы можете описать как должны обрабатываться события mouseover и mouseout.

а можно пример или это есть уже в коде?

Malleys 06.10.2019 21:45

Цитата:

Сообщение от рони
а можно пример или это есть уже в коде?

Да, в коде идет пример, в котором добавляется класс focus при наведении мыши, и удаляется при отведении мыши. Смотрите стр. 53-59 в примере выше.

На стр. 61-95 находится код, который переводит события прикосновения в события mouseover и mouseout.

Цитата:

Сообщение от рони
а можно пример или это есть уже в коде?

Это так непонятно, если пропустить запятую! Наверное, лучше так: Вы можете описáть, как должны обрабатываться события mouseover и mouseout. Затем...

рони 06.10.2019 22:01

Цитата:

Сообщение от Malleys
Это так непонятно

у меня лезет null в строке 88, идёт ошибка Cannot read property 'classList' of null.

рони 06.10.2019 22:19

Malleys,
хром, консоль, режим эмуляции, увожу курсор с картинки за пределы "устройства". возникает ошибка.

Malleys 06.10.2019 22:40

Цитата:

Сообщение от рони
хром, консоль, режим эмуляции, увожу курсор с картинки за пределы "устройства".

А такое, а то я думал, что это вообще не работает! Тогда добавил ещё проверку на наличие элементов!

Spirtikys 07.10.2019 14:05

Мой нынешний вариант реализации через elementFromPoint, при touch move я проверяю не вышел ли я с элемента.
На данный момент, подводных камней не обнаружил. По уходу с элемента убиваю touch move и жду снова touch start.

зы Очень похож на то, что было предложено Nexus и рони, но без ручного счета.
зыы Надеялся, что, все таки, есть более простые вариант, как с мышью, но, оказалось, что нет - это расстроило.


Часовой пояс GMT +3, время: 12:17.