-
Notifications
You must be signed in to change notification settings - Fork 40
/
event-timing.html
143 lines (132 loc) · 4.34 KB
/
event-timing.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="Chrome event timing demo" />
<meta name="viewport" content="initial-scale=1"/>
<meta charset="utf-8">
<title>Chrome event timing demo</title>
<style id="jsbin-css">
#target {
background-color: lightblue;
padding: 20px;
border: 1px solid black;
touch-action: none;
}
</style>
<script src="https://wicg.github.io/InputDeviceCapabilities/inputdevicecapabilities-polyfill.js"></script>
</head>
<body>
<div>
<h3>Shows the time between each requestAnimationFrame, and the time between each event and the following requestAnimationFrame.</h3>
See <a href="https://event-timing.glitch.me/">Jake Archibald's page</a> for a visual version of this.
<p>For maximum smoothness, movement events (those other than first/last) should be delivered exactly once per frame, just before rAF. Today this is done by <a href="https://www.youtube.com/watch?v=Q8m9sHdyXnE">
Android Jelly Bean and later</a> and <a href="https://developer.apple.com/videos/wwdc/2015/?id=233">iOS 9</a>. We're
<a href="https://docs.google.com/document/d/1L2JTgYMksmXgujKxxhyV45xL8jNhbCh60NQHoueKyS4/edit#heading=h.30fbj1j9p434">
planning on doing this in Chrome itself</a> when not supported by the underlying OS / hardware. If present, the times
in paranthesis represent the timestamps of events from
<a href="https://w3c.github.io/pointerevents/extension.html#dom-pointerevent-getcoalescedevents">getCoalescedEvents</a>.
</div>
<div id=target>Start touch/mouse drag here</div>
<div id=log></div>
<script>
var frames;
var recording = false;
function now() {
if ('performance' in window)
return performance.now();
return Date.now();
}
function getCoalescedEventTimes(e) {
var result = [];
if (window.PointerEvent && e instanceof window.PointerEvent && e.getCoalescedEvents) {
e.getCoalescedEvents().forEach(e => {
result.push(e.timeStamp);
});
}
return result;
}
function onstart(e) {
if (recording)
return;
e.preventDefault();
frames = [{eventtimes:[now()], coalescedeventtimes: [getCoalescedEventTimes(e)]}];
recording = true;
requestAnimationFrame(onframe);
}
function onmove(e) {
if (!recording || !frames.length)
return;
e.preventDefault();
frames[frames.length - 1].eventtimes.push(now());
frames[frames.length - 1].coalescedeventtimes.push(getCoalescedEventTimes(e));
}
function round(x) {
return Math.round(x*10)/10;
}
function onend(e) {
if (!recording)
return;
e.preventDefault();
recording = false;
frames[frames.length - 1].eventtimes.push(now());
frames[frames.length - 1].coalescedeventtimes.push(getCoalescedEventTimes(e));
}
function showOutput() {
var str = "";
for (var i = 0; i < frames.length; i++) {
str += "frame[" + i + "]";
if (i > 0)
str += " " + round(frames[i].time - frames[i-1].time) + "ms";
str += ": events";
var et = frames[i].eventtimes;
var cet = frames[i].coalescedeventtimes;
for (var j = 0; j < et.length; j++) {
var t = frames[i].time - et[j];
str += " " + round(t) + "ms";
if (cet[j].length > 0) {
str += '(<i>';
for (var h = 0; h < cet[j].length; h++) {
if (h > 0)
str += ", ";
str += round(frames[i].time - cet[j][h]);
}
str += '</i>)';
}
}
str += "<br/>";
}
log.innerHTML = str;
}
function onframe(timestamp) {
frames[frames.length - 1].time = now();
if (!recording) {
showOutput();
return;
}
frames.push({eventtimes: [], coalescedeventtimes: []});
requestAnimationFrame(onframe);
}
// Don't get confused by mouse events derived from touch.
function addMouseListener(target, type, handler) {
target.addEventListener(type, function(e) {
if (e.sourceCapabilities.firesTouchEvents)
return;
handler(e);
});
}
if (window.PointerEvent) {
target.addEventListener('pointerdown', onstart);
document.addEventListener('pointermove', onmove);
document.addEventListener('pointerup', onend);
document.addEventListener('pointercancel', onend);
} else {
addMouseListener(target, 'mousedown', onstart);
addMouseListener(window, 'mousemove', onmove);
addMouseListener(document, 'mouseup', onend);
target.addEventListener('touchstart', onstart);
target.addEventListener('touchmove', onmove);
target.addEventListener('touchend', onend);
}
</script>
</body>
</html>