Keep Screen On - A page to prevent your device from sleeping
一个有意思的网站,这个网站只有一个按钮,打开按钮(如下图)并保持网站所在浏览器tab,就能阻止屏幕熄屏,对PC、Mac、Android、iOS 均有效。实现上,如果浏览器支持 Wake Lock API ,网站会调用 navigator.wakeLock.request(“screen”) 来请求屏幕唤醒,这样能一直保持不熄屏
如果不支持该API,网站会在后台播放一个无声的短视频来模拟用户活动,从而防止屏幕休眠。来源@卡颂
这个网站使用的是 NoSleep.js: Prevent display sleep and enable wake lock in any Android or iOS web browser. 这个包
const { webm , mp4 } = require ( "./media.js" );
// Detect iOS browsers < version 10
const oldIOS = () =>
typeof navigator !== "undefined" &&
parseFloat (
(
"" +
( / CPU . * OS ( [0-9_] {3,4} ) [0-9_] {0,1}| (CPU like) . * AppleWebKit . * Mobile / i . exec (
navigator.userAgent
) || [ 0 , "" ])[ 1 ]
)
. replace ( "undefined" , "3_2" )
. replace ( "_" , "." )
. replace ( "_" , "" )
) < 10 &&
! window.MSStream;
// Detect native Wake Lock API support
const nativeWakeLock = () => "wakeLock" in navigator;
class NoSleep {
constructor () {
this .enabled = false ;
if ( nativeWakeLock ()) {
this ._wakeLock = null ;
const handleVisibilityChange = () => {
if ( this ._wakeLock !== null && document.visibilityState === "visible" ) {
this . enable ();
}
};
document. addEventListener ( "visibilitychange" , handleVisibilityChange);
document. addEventListener ( "fullscreenchange" , handleVisibilityChange);
} else if ( oldIOS ()) {
this .noSleepTimer = null ;
} else {
// Set up no sleep video element
this .noSleepVideo = document. createElement ( "video" );
this .noSleepVideo. setAttribute ( "title" , "No Sleep" );
this .noSleepVideo. setAttribute ( "playsinline" , "" );
this . _addSourceToVideo ( this .noSleepVideo, "webm" , webm);
this . _addSourceToVideo ( this .noSleepVideo, "mp4" , mp4);
this .noSleepVideo. addEventListener ( "loadedmetadata" , () => {
if ( this .noSleepVideo.duration <= 1 ) {
// webm source
this .noSleepVideo. setAttribute ( "loop" , "" );
} else {
// mp4 source
this .noSleepVideo. addEventListener ( "timeupdate" , () => {
if ( this .noSleepVideo.currentTime > 0.5 ) {
this .noSleepVideo.currentTime = Math. random ();
}
});
}
});
}
}
_addSourceToVideo ( element , type , dataURI ) {
var source = document. createElement ( "source" );
source.src = dataURI;
source.type = `video/${ type }` ;
element. appendChild (source);
}
get isEnabled () {
return this .enabled;
}
enable () {
if ( nativeWakeLock ()) {
return navigator.wakeLock
. request ( "screen" )
. then (( wakeLock ) => {
this ._wakeLock = wakeLock;
this .enabled = true ;
console. log ( "Wake Lock active." );
this ._wakeLock. addEventListener ( "release" , () => {
// ToDo: Potentially emit an event for the page to observe since
// Wake Lock releases happen when page visibility changes.
// (https://web.dev/wakelock/#wake-lock-lifecycle)
console. log ( "Wake Lock released." );
});
})
. catch (( err ) => {
this .enabled = false ;
console. error ( `${ err . name }, ${ err . message }` );
throw err;
});
} else if ( oldIOS ()) {
this . disable ();
console. warn ( `
NoSleep enabled for older iOS devices. This can interrupt
active or long-running network requests from completing successfully.
See https://github.com/richtr/NoSleep.js/issues/15 for more details.
` );
this .noSleepTimer = window. setInterval (() => {
if ( ! document.hidden) {
window.location.href = window.location.href. split ( "#" )[ 0 ];
window. setTimeout (window.stop, 0 );
}
}, 15000 );
this .enabled = true ;
return Promise . resolve ();
} else {
let playPromise = this .noSleepVideo. play ();
return playPromise
. then (( res ) => {
this .enabled = true ;
return res;
})
. catch (( err ) => {
this .enabled = false ;
throw err;
});
}
}
disable () {
if ( nativeWakeLock ()) {
if ( this ._wakeLock) {
this ._wakeLock. release ();
}
this ._wakeLock = null ;
} else if ( oldIOS ()) {
if ( this .noSleepTimer) {
console. warn ( `
NoSleep now disabled for older iOS devices.
` );
window. clearInterval ( this .noSleepTimer);
this .noSleepTimer = null ;
}
} else {
this .noSleepVideo. pause ();
}
this .enabled = false ;
}
}
module . exports = NoSleep;
这段核心代码的原理主要是首先在 contructor
构造函数中检查是否支持远程的屏幕唤醒锁 WakeLock API ,如果支持,则设置相应的监听器,如果是旧的 IOS 设备,则初始化一个定时器,如果都不是,则设置一个空白没有音量的视频元素来模拟用户的行为,阻止屏幕休眠。
检测原生WakeLock API 支持:通过检查 navigator
对象中是否存在 wakeLock
属性,来确定浏览器是否支持屏幕唤醒锁。
如果只是原生的WakeLock API , 就请求屏幕唤醒锁
对于老旧的 IOS 设备。方法通过重定向页面和停止加载来阻止屏幕休眠
对于不支持 WakeLock API 的其他设备,通过循环播放一个小视频来阻止屏幕进行休眠
视频资源使用 Base64
编码的 URL 形式嵌入。