I have a pannable app window which works by listening for mousemove
events and then using transform: translate3d(...)
to move the screen accordingly. It's a large app and there is considerable UI work associated to facilitate this functionality. Here comes the MCVE, in which the real workload is mocked by a dummy for
loop:
var container = document.getElementById("container");
var contents = document.getElementById("contents");
var input = document.getElementById("iterations");
var posX = 50;
var posY = 50;
var previousX = null;
var previousY = null;
var mousedownHandler = function (e) {
window.onmousemove = globalMousemoveHandler;
window.onmouseup = globalMouseupHandler;
previousX = e.clientX;
previousY = e.clientY;
}
var globalMousemoveHandler = function (e) {
var now = Date.now();
for (var i = 0, n = parseInt(input.value); i < n; i++);
var elapsed = Date.now() - now;
posX += e.clientX - previousX;
posY += e.clientY - previousY;
previousX = e.clientX;
previousY = e.clientY;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
contents.innerText = elapsed + "ms";
}
var globalMouseupHandler = function (e) {
window.onmousemove = null;
window.onmouseup = null;
previousX = null;
previousY = null;
}
container.onmousedown = mousedownHandler;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
#container {
height: 180px;
width: 600px;
background-color: #ccc;
overflow: hidden;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#container:active {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
<label>Iterations: <input id="iterations" type="number" value="20000000" step="5000000" /></label>
<div id="container">
<div id="contents">
Pannable container contents...
</div>
</div>
JSFiddle example with larger drag-area
Please hold and drag the example.
This rudimentary snippet runs a dummy for
loop every time mousemove
fires, and the duration it takes for the loop to complete is displayed in the draggable container. This is needed to demonstrate the problem below. You may need to adjust the number of iterations, so that the loop takes somewhere above 10ms to run, but not much longer.
This snippet runs as fast as possible in Google Chrome, no problems there. Untested in Firefox.
However, in Microsoft Edge (and presumably, IE11 as well) if globalMousemoveHandler
runs for longer than about 10ms, the browser starts throttling the event mercillesly, making it fire much less frequently, and obliterating the panning progress down to a crawl.
Also quite strange is that the for
loop actually runs faster in Microsoft Edge than in Chrome (almost 50% faster), but the event still fires much less frequently.
This is observable in the above snippet when viewed from the mentioned browsers. Now I understand the theoretical desire behind this functionality, but it renders my application unusable on these browsers -- I also don't really understand what's the point for the throttling to kick in below 16ms (I'm well under the 60 FPS frame budget), but that's besides the question now (although I'd be glad to hear some details about this).
How can I prevent this from happening?
For organizations that require Internet Explorer 11 (IE11) for legacy compatibility, Internet Explorer mode (IE mode) on Microsoft Edge provides a seamless, single browser experience. Users can access legacy applications from within Microsoft Edge without having to switch back to IE11.
When you enable the Disable Internet Explorer 11 as a standalone browser policy, all IE11 activity is redirected to Microsoft Edge and users have the following experience: The IE11 icon on the Start Menu will be removed, but the one on the taskbar will remain.
When users try to launch shortcuts or file associations that use IE11, they will be redirected to open the same file/URL in Microsoft Edge. When users try to launch IE11 by directly invoking the iexplore.exe binary, Microsoft Edge will launch instead.
The new Microsoft Edge is designed to detect and block known trackers. We let you decide what trackers to block. You can choose from three levels of tracking prevention: Basic, Balanced, and Strict. Balanced is selected as the default.
Here is an example that uses requestAnimationFrame.
var container = document.getElementById("container");
var contents = document.getElementById("contents");
var input = document.getElementById("iterations");
var posX = 50;
var posY = 50;
var previousX = null;
var previousY = null;
var mousedownHandler = function (e) {
window.onmousemove = globalMousemoveHandler;
window.onmouseup = globalMouseupHandler;
previousX = e.clientX;
previousY = e.clientY;
}
var globalMousemoveHandler = function (e) {
posX += e.clientX - previousX;
posY += e.clientY - previousY;
previousX = e.clientX;
previousY = e.clientY;
window.requestAnimationFrame(function () {
var now = Date.now();
for (var i = 0, n = parseInt(input.value); i < n; i++);
var elapsed = Date.now() - now;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
contents.innerText = elapsed + "ms";
});
}
var globalMouseupHandler = function (e) {
window.onmousemove = null;
window.onmouseup = null;
previousX = null;
previousY = null;
}
container.onmousedown = mousedownHandler;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
#container {
height: 180px;
width: 600px;
background-color: #ccc;
overflow: hidden;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#container:active {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
<label>Iterations: <input id="iterations" type="number" value="20000000" step="5000000" /></label>
<div id="container">
<div id="contents">
Pannable container contents...
</div>
</div>
This stops the heavy lifting from blocking the handler function, effectively preventing the issue from happening, at the cost of a 16ms increased latency.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With