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"); 
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
方式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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
标题闪烁提示(弱提醒)
  • 缺点
    • 如果用户切到别的tab,任务栏看不到名称变化效果;
    • 当有多个tab时候提示不明显;
    • 使用定时器刷新,影响性能;
let title = '有新消息'
setInterval(() => {
    if (title === '有新消息'){
        document.title = 'xxx平台'
        title = 'xxx平台'
    } else {
        document.title = '有新消息'
        title = '有新消息'
    }
}, 1000)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
辅助方法
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
    /*
        - 如果页面处于前台且可见,document.hidden 的值为 false
        - 如果页面处于后台或被隐藏,document.hidden 的值为 true
    */
    if (document.hidden) {
        console.log("页面已隐藏,可以弹出通知");
    } else {
        console.log("页面已激活,不弹出通知");
    }
});
// 监听页面关闭或刷新是清空当前弹窗
window.addEventListener('beforeunload', (event) => {
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
封装方法
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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
spop.js是一款纯js toast消息提示通知插件。通过spop.js插件,你可以快速的在网页上制作出漂亮的toast消息提示效果。该js toast消息提示插件的特点还有: 内置4种主题样式:default, success, warning, error。 toast可以显示在屏幕的6个位置:top-left, top-center, top-right, bottom-left, bottom-center, bottom-right。 可以对消息提示进行编组。 提供打开和关闭消息提示时的回调函数。 支持HTML内容。 使用方法 在页面中引入spop.css和spop.js文件。 <link rel="stylesheet" type="text/css" href="./css/spop.css"> [removed][removed] 初始化toast 该js toast消息提示插件最基本的使用方法如下: spop('Default SmallPop'); spop('<h4 class="spop-title">Success</h4>I´m a success SmallPop', 'success'); spop('<strong>Warning pop</strong>', 'warning'); spop('<strong>Error Here!</strong>', 'error'); 分组 可以对消息进行分组,分组后的消息每次只显示一条。 spop({ template: 'All fields are required!', group: 'submit-satus', style: 'error' }); spop({ template: 'Your information has been submitted', group: 'submit-satus', style: 'success' autoclose: 2000 }); 回调函数 在toast消息提示打开和关闭时都可以使用回调函数。 spop({ template: 'Please, close me.', onOpen: function () { document.body.style.background = "#fff"; }, onClose: function() { document.body.style.background = ""; spop({ template: 'Thank you!', style: 'success', autoclose: 2000 }); } }); 配置参数 spop.js消息提示插件的可用配置参数如下: spop({ template : null,// string required. Without it nothing happens! style : 'info',// success, warning or error autoclose : false,// miliseconds position : 'top-right',// top-left top-center bottom-left bottom-center bottom-right icon : true,// or false group : false,// string, add a id reference onOpen : funtion() { }, onClose : funtion() { } }); template :消息提示的模板。可以是一个字符串,或这是HTML代码。 style :toast消息提示的主题样式,可以是info,success, warning 或 error。 autoclose :是否自动关闭。 position :toast消息提示的位置。可以是:top-right,top-left,top-center,bottom-left,bottom-center或bottom-right。 icon :是否显示图标。 group :是否对消息进行分组。 onOpen :toast消息提示打开时的回调函数。 onClose :toast消息提示关闭时的回调函数。
点击体验DeepSeekR1满血版 隐藏侧栏
客服 返回顶部