Skip to content

Instantly share code, notes, and snippets.

@fuweichin
Last active January 30, 2024 15:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fuweichin/f7b675c7a5bab86dd1488962e646c6cc to your computer and use it in GitHub Desktop.
Save fuweichin/f7b675c7a5bab86dd1488962e646c6cc to your computer and use it in GitHub Desktop.
Power Saving Mode detection
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Power Saving Mode detection</title>
</head>
<body>
<div>Power Saving Mode? <code id="powerSavingMode"></code></div>
<script type="module">
import {detectPowerSavingMode} from './power-saving.js';
document.addEventListener('DOMContentLoaded', () => {
let span = document.querySelector('#powerSavingMode');
span.textContent = '';
if(self !== top && window.safari){
alert('The detection may not work as expected in <iframe> for Safari');
}
let requestIdleCallback = window.requestIdleCallback || queueMicrotask;
requestIdleCallback(()=>{
detectPowerSavingMode().then((result) => {
span.textContent = '' + result;
});
});
});
</script>
</body>
</html>
/**
* detect iOS/iPad/macOS Low Power Mode, Chromium Energy Saver Mode, and maybe future Firefox power saving mode
* @async
* @method detectPowerSavingMode
* @returns {boolean|undefined} return `undefined` if not sure
*/
export function detectPowerSavingMode() {
// for iOS/iPadOS Safari, and maybe MacBook macOS Safari (not tested)
if (/(iP(?:hone|ad|od)|Mac OS X)/.test(navigator.userAgent)) {
// In Low Power Mode, cumulative delay effect happens on setInterval()
return new Promise((resolve) => {
let fps = 60;
let interval = 1000 / fps;
let numFrames = 30;
let startTime = performance.now();
let i = 0;
let handle = setInterval(() => {
if (i < numFrames) {
i++;
return;
}
clearInterval(handle);
let actualInterval = (performance.now() - startTime) / numFrames;
let ratio = actualInterval / interval; // 1.3x or more in Low Power Mode, 1.1x otherwise
// alert(actualInterval+' '+interval);
console.log(actualInterval, interval, ratio);
resolve(ratio > 1.3);
}, interval);
});
}
// for Safari, Chromium, and maybe future Firefox
return detectFrameRate().then((frameRate) => {
// In Battery Saver Mode frameRate will be about 30fps or 20fps,
// otherwise frameRate will be closed to monitor refresh rate (typically 60Hz)
if (frameRate < 34) {
return true;
}
// FIXME fallback to regard as Low Power Mode when battery power is low (down to 20%)
else if (navigator.getBattery) {
return navigator.getBattery().then((battery) => {
return (!battery.charging && battery.level <= 0.2) ? true : false;
});
}
return undefined;
});
}
export function detectFrameRate() {
return new Promise((resolve) => {
let numFrames = 30;
let startTime = performance.now();
let i = 0;
let tick = () => {
if (i < numFrames) {
i++;
requestAnimationFrame(tick);
return;
}
let frameRate = numFrames / ((performance.now() - startTime) / 1000);
resolve(frameRate);
};
requestAnimationFrame(() => {
tick();
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment