Skip to content

Instantly share code, notes, and snippets.

@sounisi5011
Last active May 19, 2018 19:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sounisi5011/3523bd9c5b6ef945e4f86b4d7e71a9eb to your computer and use it in GitHub Desktop.
Save sounisi5011/3523bd9c5b6ef945e4f86b4d7e71a9eb to your computer and use it in GitHub Desktop.
Pointer Events drag test
license: mit

Pointer Events drag test

Pointer Eventsを使って、ドラッグ操作を行うコードを書いてみたやつになります。 テストコードなので、Pointer Eventsに非対応の環境は考慮していません。 また、ECMAScript 2015以上のコードで書いているため、古いブラウザでは動いてくれません。

実動サンプル

Note

AndroidのChromeにおいて、なぜかスクロール操作がキャンセルされない。 以下のサンプルは正常に動作するため、今後改善する。

<!doctype html>
<html lang=ja>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta name=format-detection content="telephone=no,email=no,address=no">
<title>Pointer Events drag test</title>
<link rel=preload href=./main.css as=style>
<link rel=preload href=./main.js as=script>
<link href=./main.css rel=stylesheet>
<body>
<h1>Pointer Events drag test</h1>
<div id=output></div>
<h2>Gist</h2>
<p><a href=https://gist.github.com/sounisi5011/3523bd9c5b6ef945e4f86b4d7e71a9eb>gist.github.com<wbr>/sounisi5011<wbr>/3523bd9c5b6ef945e4f86b4d7e71a9eb</a></p>
<script src=./main.js></script>
#output {
box-sizing: border-box;
max-width: 100%;
width: 200px;
height: 200px;
border: solid 1px #ccc;
padding: 1em;
}
.square {
position: absolute;
width: 50px;
height: 50px;
background-color: blue;
}
/**
* @see https://w3c.github.io/pointerevents/#pointer-event-types
*/
const POINTER_EVENT_NAME_LIST = [
'pointerover',
'pointerenter',
'pointerdown',
'pointermove',
'pointerup',
'pointercancel',
'pointerout',
'pointerleave',
'gotpointercapture',
'lostpointercapture',
];
const POINTER_EVENT_PROP_LIST = [
/**
* PointerEvent property list
* @see https://w3c.github.io/pointerevents/#pointerevent-interface
*/
'pointerId',
'width',
'height',
'pressure',
'tangentialPressure',
'tiltX',
'tiltY',
'twist',
'pointerType',
'isPrimary',
/**
* MouseEvent property list
* @see https://www.w3.org/TR/uievents/#interface-mouseevent
* @see https://drafts.csswg.org/cssom-view/#extensions-to-the-mouseevent-interface
*/
'screenX',
'screenY',
'clientX',
'clientY',
'ctrlKey',
'shiftKey',
'altKey',
'metaKey',
'button',
'buttons',
'relatedTarget',
'pageX',
'pageY',
'x',
'y',
'offsetX',
'offsetY',
/**
* UIEvent property list
* @see https://www.w3.org/TR/uievents/#uievent-uievent
*/
'view',
'detail',
/**
* Event property list
* @see https://dom.spec.whatwg.org/#event
*/
'type',
'target',
'srcElement',
'currentTarget',
'eventPhase',
'cancelBubble',
'bubbles',
'cancelable',
'returnValue',
'defaultPrevented',
'composed',
'isTrusted',
'timeStamp',
];
const outputElem = document.getElementById('output');
const squareElem = document.createElement('div');
squareElem.classList.add('square');
/*
* ドラッグ操作用のイベントを定義
*/
const dragStateMap = new WeakMap;
const dragListener = event => {
const { type: eventType, pointerId, pageX: pointerPosX, pageY: pointerPosY } = event;
const squareElemStyle = squareElem.style;
/**
* @see https://w3c.github.io/pointerevents/#dfn-active-buttons-state
* @see https://w3c.github.io/pointerevents/#dom-element-haspointercapture
*/
const isActiveButtonsState = event.buttons !== 0 || squareElem.hasPointerCapture(pointerId);
event.preventDefault();
if (eventType === 'pointerdown' && !dragStateMap.has(squareElem)) {
squareElem.setPointerCapture(pointerId);
/*
* styleが未設定の場合は、設定する
*/
if (String(squareElemStyle.left).length === 0 || String(squareElemStyle.top).length === 0) {
const clientRect = squareElem.getBoundingClientRect();
squareElemStyle.left = `${window.pageXOffset + clientRect.left}px`;
squareElemStyle.top = `${window.pageYOffset + clientRect.top}px`;
}
dragStateMap.set(squareElem, {
initialPointerPosX: pointerPosX,
initialPointerPosY: pointerPosY,
initialSquarePosX: Number.parseFloat(squareElemStyle.left),
initialSquarePosY: Number.parseFloat(squareElemStyle.top),
pending: false,
});
} else if (
(
eventType === 'pointermove' ||
eventType === 'pointerup'
) &&
dragStateMap.has(squareElem) &&
isActiveButtonsState
) {
const dragState = dragStateMap.get(squareElem);
if (!dragState.pending) {
dragState.pending = true;
window.requestAnimationFrame(() => {
squareElemStyle.left = `${dragState.initialSquarePosX + (pointerPosX - dragState.initialPointerPosX)}px`;
squareElemStyle.top = `${dragState.initialSquarePosY + (pointerPosY - dragState.initialPointerPosY)}px`;
dragState.pending = false;
});
}
}
if (eventType === 'pointerup' || eventType === 'pointercancel' || (eventType === 'pointermove' && !isActiveButtonsState)) {
squareElem.releasePointerCapture(pointerId);
dragStateMap.delete(squareElem);
}
};
squareElem.addEventListener('pointerdown', dragListener);
squareElem.addEventListener('pointermove', dragListener);
squareElem.addEventListener('pointerup', dragListener);
squareElem.addEventListener('pointercancel', dragListener);
/*
* デバッグ用のイベントを定義
*/
const pointerListener = event => {
console.groupCollapsed(event.type);
console.dir(event);
const cloneObj = {};
for (const prop of POINTER_EVENT_PROP_LIST) {
cloneObj[prop] = event[prop];
}
console.dir(cloneObj);
console.groupEnd();
};
for (const eventName of POINTER_EVENT_NAME_LIST) {
squareElem.addEventListener(eventName, pointerListener);
}
/*
* 四角形をDOMツリーに追加
*/
outputElem.appendChild(squareElem);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment