js通知提醒
JS通知总结下来有4种方法,分别是声音提醒、通知提醒、标题变化、icon变化; 比较常见的是标题变化和icon变化;
方式1:声音通知
人性化提示音:xxx平台收到音视频呼叫,请接听; 您有收到一条新的消息;
- 优点:通知即时,无干扰和历史残留;
- 缺点:
- 浏览器要求必须和页面发生一次交互,才能播放; 静音后将无法收到提醒;
- 浏览器静音/系统静音将无法听到播放声音;
备注: 如果手动开启浏览器声音播放权限;将不会有必须与页面发生交互的错误;
方式2:Notification通知(强提醒)
- 优点:强提醒,用户隐藏tab也会收到;
- 缺点
- win系统5-6秒左右会自动隐藏;
- 无法指定常驻时间,设置自定义样式;
- 如果用户拒绝过,将无法再次弹出授权通知,需要用户手动去设置中开启;
- 无法自定义按钮,只能触发点击事件;
- 如果用户授权, 但是系统设置里面关闭了浏览器通知,将无法监测到;
- 大部分用户拒绝后,可能自己找不到设置中哪里开启;
- 由于刷新或异常关闭,会导致老旧的通知存在任务栏;无法释放;
备注:可以手动开启浏览器通知权限; 并设置系统允许浏览器通知
相关代码
/*
Notification.requestPermission;
- 'denied': 用户拒绝显示通知
- 'granted': 用户接受显示通知
- 'default': 用户选择是未知的
*/
function notifyMe() {
if (!("Notification" in window)) {
// 检查浏览器是否支持通知
alert("当前浏览器不支持桌面通知");
} else if (Notification.permission === "granted") {
// 检查是否已授予通知权限;如果是的话,创建一个通知
const notification = new Notification("你好!");
// …
} else if (Notification.permission !== "denied") {
// 我们需要征求用户的许可
Notification.requestPermission().then((permission) => {
// 如果用户接受,我们就创建一个通知
if (permission === "granted") {
const notification = new Notification("你好!");
// 或者
const notification = new Notification("通知标题:", {
body: '通知内容',
icon: '',
})
}
});
}
}
// 判断当前浏览器是否支持通知
if (!("Notification" in window)) {
console.log("此浏览器不支持通知API");
}
// 如果用户已经禁用了通知,提示用户手动开启
if (Notification.permission === "denied") {
alert("您已阻止通知,请在浏览器设置中开启通知权限以获得最佳体验。");
}
// 绑定事件
notification.onclick = function () {
window.focus(); // 点击通知时将焦点切换回窗口
notification.close(); // 关闭通知
};
// 点击通知时打开指定页面
notification.onclick = function () {
window.open("https://baidu.com");
};
方式3:修改Favicon(弱提醒)
- 多个tab也可以看到变化
- 缺点
- 如果最小化将无法看到效果;
- 使用定时器刷新,影响性能;
相关代码
/*
也可以直接替换icon
<link rel="icon" href="/favicon.ico"/>
*/
function changeFavicon(url) {
const link = document.querySelector("link[rel*='icon']") || document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
link.href = url;
document.getElementsByTagName("head")[0].appendChild(link);
}
// 动态生成带数字的Favicon
// 可以生成一个红底里面放数字的图标
function drawFaviconWithNumber(number) {
const canvas = document.createElement("canvas");
canvas.width = 16;
canvas.height = 16;
const context = canvas.getContext("2d");
context.fillStyle = "red";
context.beginPath();
context.arc(8, 8, 8, 0, 2 * Math.PI);
context.fill();
context.fillStyle = "white";
context.font = "10px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText(number, 8, 8);
const url = canvas.toDataURL("image/png");
changeFavicon(url);
}
标题闪烁提示(弱提醒)
- 缺点
- 如果用户切到别的tab,任务栏看不到名称变化效果;
- 当有多个tab时候提示不明显;
- 使用定时器刷新,影响性能;
let title = '有新消息'
setInterval(() => {
if (title === '有新消息'){
document.title = 'xxx平台'
title = 'xxx平台'
} else {
document.title = '有新消息'
title = '有新消息'
}
}, 1000)
辅助方法
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
/*
- 如果页面处于前台且可见,document.hidden 的值为 false
- 如果页面处于后台或被隐藏,document.hidden 的值为 true
*/
if (document.hidden) {
console.log("页面已隐藏,可以弹出通知");
} else {
console.log("页面已激活,不弹出通知");
}
});
// 监听页面关闭或刷新是清空当前弹窗
window.addEventListener('beforeunload', (event) => {
});
封装方法
import notifIcon from '@/assets/icon.png';
// 页面默认icon <link rel="icon" href="/favicon.ico"/>
import faviconIco from '@/assets/favicon.ico';
// 新消息icon <link rel="icon" href="/inCallIcon.ico"/>
import inCallIcon from '@/assets/inCallIcon.svg';
import { Modal } from 'antd';
const { warning } = Modal;
class MediaNotic {
pageIsHide: boolean;
openPageHideNotUpdate: boolean;
toggleTime: any;
notiPerWarning: any;
notification: any;
constructor () {
// 当前页面是是否显示: true_隐藏 false_页面激活;
this.pageIsHide = false;
// 是否开启页面不可见才弹出通知
this.openPageHideNotUpdate = false;
// 切换定时器
this.toggleTime = null;
// 通知权限弹窗
this.notiPerWarning = null;
// 当前通知系统弹窗
this.notification = null;
this.init();
}
init() {
this.getCurentDocumentHide();
this.bindDocumentVisiChange();
this.firstLoadingNotiPermission();
}
/*
是否设置开启页面隐藏才触发通知
*/
setOpenPageHideNotUpdate(open: boolean) {
this.openPageHideNotUpdate = open;
}
open() {
// 如果开启页面隐藏才弹窗
if (this.openPageHideNotUpdate) {
if (!this.pageIsHide) {
this.close();
return;
};
}
this.toggleTime = setTimeout(() => {
this.toggleTime = null;
const title = document.title;
if (title === '有新来电'){
this.resetTitleFavicon();
} else {
document.title = '有新来电';
// this.drawFaviconWithNumber(1);
this.changeFavicon(inCallIcon);
}
this.open();
}, 1000);
}
close() {
if (this.toggleTime) {
clearTimeout(this.toggleTime);
this.toggleTime = null;
};
this.resetTitleFavicon();
if (this.notification) {
this.notification.close();
this.notification = null;
};
}
resetTitleFavicon() {
document.title = '三级调度平台';
this.changeFavicon(faviconIco);
}
changeFavicon(url: string) {
const link: any = document.querySelector("link[rel*='icon']") || document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
link.href = url;
document.getElementsByTagName("head")[0].appendChild(link);
}
/* 获取当前页面是否可见 */
getCurentDocumentHide() {
this.pageIsHide = document.hidden;
}
/* 监听页面可见性变化 */
bindDocumentVisiChange() {
/*
- 如果页面处于前台且可见,document.hidden 的值为 false
- 如果页面处于后台或被隐藏,document.hidden 的值为 true
*/
document.addEventListener("visibilitychange", () => {
this.pageIsHide = document.hidden;
// 如果开启页面隐藏才弹窗
if (this.openPageHideNotUpdate && !this.pageIsHide) {
this.close();
}
});
window.addEventListener('beforeunload', (event) => {
this.close();
});
}
// 动态生成带数字的Favicon
drawFaviconWithNumber(number: number) {
const canvas = document.createElement("canvas");
canvas.width = 16;
canvas.height = 16;
const context: any = canvas.getContext("2d");
context.fillStyle = "red";
context.beginPath();
context.arc(8, 8, 8, 0, 2 * Math.PI);
context.fill();
context.fillStyle = "white";
context.font = "10px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
// context.fillText(number, 8, 8);
const url = canvas.toDataURL("image/png");
this.changeFavicon(url);
}
/* 通知权限
- 如果用户没有赋予就去申请一下;
- 如果用户判断已经拒绝, 就简单提示一下;
*/
firstLoadingNotiPermission() {
if (!("Notification" in window)) {
return;
}
const notPerm = Notification.permission;
// 表示用户还没有指定通知权限
if (notPerm === 'default') {
// 主动获取通知权限
Notification.requestPermission();
return;
} else if (notPerm === 'denied') {
// alert 会阻塞页面加载,不建议使用
// alert('您已阻止通知, 请在浏览器设置中开启通知权限以获得最佳体验!');
if (this.notiPerWarning) {
this.notiPerWarning.destroy();
this.notiPerWarning = null;
};
this.notiPerWarning = warning({
icon: '',
title: '提示',
content: '您已选择关闭通知权限,将无法收到来电邀请通知, 请在设置中开启通知权限以获得最佳体验!',
okText: '确定',
onCancel: () => {
this.notiPerWarning = null;
}
});
// // 超时关闭
// setTimeout(() => {
// if (this.notiPerWarning) {
// this.notiPerWarning.destroy();
// this.notiPerWarning = null;
// }
// }, 15 * 1000);
return;
}
}
openNoti() {
// 如果开启页面隐藏才弹窗
if (this.openPageHideNotUpdate) {
if (!this.pageIsHide) {
this.close();
return;
};
}
if (!("Notification" in window)) {
return;
}
if (this.notification) {
this.notification.close();
this.notification = null;
};
Notification.requestPermission().then((permission) => {
// 如果用户接受,我们就创建一个通知
if (permission === "granted") {
if (this.notification) {
this.notification.close();
this.notification = null;
};
this.notification = new Notification("来电提醒", {
body: 'xxx平台收到音视频邀请, 请前往接听!',
icon: notifIcon,
});
// 绑定事件
this.notification.onclick = function () {
window.focus(); // 点击通知时将焦点切换回窗口
this.notification.close(); // 关闭通知
};
// 系统大概5秒后会自己消失
setTimeout(() => {
if (this.notification) {
this.notification.close();
this.notification = null;
};
}, 30 * 1000);
}
});
}
}
const InCallMediaNotic = new MediaNotic();
export default InCallMediaNotic;