当前位置: 首页 > news >正文

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;

相关文章:

  • ceph HEALTH_WARN clock skew detected on mon.f, mon.o, mon.p, mon.q
  • 并发和多线程
  • 宇树机器人G1 SDK实战和交付
  • Linux下centos系统中使用docker容器中的ollama下载deepseek速度太慢解决办法
  • python中with语句和os模块讲解
  • Java 语法新特性(Records、Pattern Matching、Sealed Classes)深度解析(11/17/21)✨
  • 深入理解 Spring Bean 生命周期的执行流程
  • 数仓搭建(hive):DWS层(服务数据层)
  • 二级指针略解【C语言】
  • idea升级安装新版本无法启动
  • 【学习笔记】Cadence电子设计全流程(一)Cadence 生态及相关概念
  • 【大语言模型_3】ollama本地加载deepseek模型后回答混乱问题解决
  • 一文读懂 KYC:金融、IT 领域的关键应用与实践
  • 算法学习笔记之递推求解
  • (LLaMa Factory)大模型训练方法--监督微调(Qwen2-0.5B)
  • 利用多线程加速ESMC-6B模型API调用以及403Forbidden问题的解决
  • Redis数据结构总结-整数集合
  • 创建虚拟机遇到的问题
  • Mybatis MyBatis框架的缓存 一级缓存
  • Fino1: 关于推理增强型大型语言模型在金融领域的可迁移性
  • 2025五一档新片电影总票房破亿
  • 日菲同意扩大安全合作,外交部:反对任何在本地区拉帮结派的做法
  • 澎湃读报丨解放日报9个版聚焦:上海,加快建成具有全球影响力的科技创新高地
  • 企业取消“大小周”引热议,半月谈:不能将显性加班变为隐性加班
  • 一周人物|卡鲁等入围英国特纳奖,李学明新展中国美术馆
  • 俄罗斯延长非法滞留外国人限期离境时间至9月