展示通知【译】

Posted by Neil Ning on 2023-08-19
翻译

前言

该文章翻译自web.dev的Notifications系列文章,本篇文章原文链接点击这里

通知的选项分为两部分,一部分处理可视化部分,本文将会讲解这一部分。另外一部分是关于通知行为的,将下一章节解释。
你可以在这个网站试验不同的通知选项在不同浏览器和平台上的差异。

可视化选项

调用通知API来显示通知是很简单的:

1
<ServiceWorkerRegistration>.showNotification(<title>, <options>);

title和options参数都是可选的。title是字符串,options选项可以包含下面的字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"//": "Visual Options",
"body": "<String>",
"icon": "<URL String>",
"image": "<URL String>",
"badge": "<URL String>",
"dir": "<String of 'auto' | 'ltr' | 'rtl'>",
"timestamp": "<Long>"

"//": "Both visual & behavioral options",
"actions": "<Array of Strings>",
"data": "<Anything>",

"//": "Behavioral Options",
"tag": "<String>",
"requireInteraction": "<boolean>",
"renotify": "<Boolean>",
"vibrate": "<Array of Integers>",
"sound": "<URL String>",
"silent": "<Boolean>",
}

下图可以看到相关的可视化选项:
options.jpg

title和options选项

下面是一个没有title和options选项时,Windows平台下Chrome浏览器里通知显示框:
option1.jpg
可以看到,浏览器的名称就是默认的title,“New notification”占位符是默认通知消息体。如果在设备上安装了渐进式Web应用,应用的名字将会替代浏览器名称:
option3.jpg
如果我们运行以下代码:

1
2
3
4
5
6
7
const title = 'Simple Title';

const options = {
body: 'Simple piece of body text.\nSecond line of body text :)',
};

registration.showNotification(title, options);

在Linux的Chrome浏览器中,通知框的显示如下:
option4.jpg
在Linux的Firefox中显示如下:
option5.jpg

在Linux的Chrome中,超长title和body的通知框:
option6.jpg

在Linux的Firefox中body将会被折叠,当把浏览器光标放在通知框上时,body将会完整显示。
option7-1.jpg
option7.jpg

Windows平台下的Firefox中,通知框的显示如下:
option8.jpg
option9.jpg

可以看到,相同的代码在不同的浏览器中显示效果是不同的,在不同平台的相同浏览器中显示效果也是不同的。Chrome和Firefox利用了操作系统中通知,例如在MacOS下的系统通知不支持图片和actions选项(快捷回复)。在所有系统下的Chrome浏览器可以自定义通知框。通过访问chrome://flags/#enable-system-notifications可以启用该选项。

icon

icon选项本质上是一个小的图片,在代码中icon选项是一个URL:

1
2
3
4
5
6
7
const title = 'Icon Notification';

const options = {
icon: '/images/demos/icon-512x512.png',
};

registration.showNotification(title, options);

在Linux的Chrome中显示如下:
icon.jpg

在Linux的Firefox中显示如下:
icon2.jpg
遗憾的是对于icon选项所支持的图片尺寸,还没有统一的规范。Android平台可以是一个64dp的图片(64px * 设备像素比)。假设最高的设备像素比是3。则至少需要192px的icon图标。

一些浏览器要求icon图片的URL必须是HTTPS的,如果使用第三方图片,需要注意这一点。

Badge

badge是一个小的单色图标,用来说明通知来自于哪里。

1
2
3
4
5
6
7
const title = 'Badge Notification';

const options = {
badge: '/images/demos/badge-128x128.png',
};

registration.showNotification(title, options);

在写这边文章时,badge只能在Android的Chrome中使用。
badge.jpg

在其他浏览器上(或没有badge选项的Chrome浏览器),显示的是浏览器的图标。
badge1.jpg
icon选项一样,badge也没有统一的标准说明图片的尺寸。这片Android相关文章推荐的是使用24px * 设备像素比。即72px的图片是最佳的尺寸(假设最高设备像素比是3)。

Image

image选项用来向用户展示一个较大的图片。当你需要向用户展示预览图时候该选项是很有用的。

1
2
3
4
5
6
7
const title = 'Image Notification';

const options = {
image: '/images/demos/unsplash-farzad-nazifi-1600x1100.jpg',
};

registration.showNotification(title, options);

在Linux的Chrome浏览器中显示效果如下:
image.jpg

在Android的Chrome中图片会被按比例裁剪:
image2.jpg

由于桌面或移动端平台比例的不同,所以很难有一个统一的标准。桌面版的Chrome浏览器的比例是4:3,并不会填充剩余空间,所以最佳的方式提供该比例的图片,并允许Android平台裁剪图片。Android平台可以使用450dp的图片。即只存为1350px的图片。

Actions

可以定义actions选项展示不同的按钮。

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
const title = 'Actions Notification';

const options = {
actions: [
{
action: 'coffee-action',
type: 'button',
title: 'Coffee',
icon: '/images/demos/action-1-128x128.png',
},
{
action: 'doughnut-action',
type: 'button',
title: 'Doughnut',
icon: '/images/demos/action-2-128x128.png',
},
{
action: 'gramophone-action',
type: 'button',
title: 'Gramophone',
icon: '/images/demos/action-3-128x128.png',
},
{
action: 'atom-action',
type: 'button',
title: 'Atom',
icon: '/images/demos/action-4-128x128.png',
},
],
};

registration.showNotification(title, options);

每个action可以定义titleaction(本质上是个ID)、icontype。title和icon会显示在通知框中,ID用来识别被点击的按钮。type可以省略,默认值是'button'。在写这篇文章时,只有Android的Chrome和Opera浏览器支持actions。
上面的例子中定义了四个action,你可以定义更多的action。如果想知道浏览器支持的action数量,可以查看window.Notification?.maxActions

1
2
3
4
5
6
7
const maxVisibleActions = window.Notification?.maxActions;

if (maxVisibleActions) {
options.body = `Up to ${maxVisibleActions} notification actions can be displayed.`;
} else {
options.body = 'Notification actions are not supported.';
}

在桌面端,action按钮可以显示图片的颜色:
action-button.jpg

不过在Android6和之前的版本,图标的颜色必须匹配系统颜色:
action-button1.jpg

在Android7和之后的版本中,action的图标不会显示。

Chrome桌面浏览器有望实现像安装那样的行为,即随着系统的主题风格更改图标的颜色,现在,可以将图标的颜色设为#333333,来匹配Chrome浏览器中文字的颜色。桌面的Chrome浏览器中最佳的尺寸是24px * 24px。抹平这些平台差异的最佳实践如下:

  • 为图标设置一致的颜色,以保证对用户来说所有的图标颜色是一致的
  • 确保Chrome中设置项能正常显示,其他的平台可能会有不同的展示方式
  • 测试不同的尺寸,128px * 128px在我的安卓手机上显示正常,但是桌面浏览器的显示效果很差。
  • 或者完全不展示action的图标

Actions(快捷回复)

你可以定义action的type字段为’text‘,以实现在通知框中添加一个行内回复:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const title = 'Alexey Rodionov';

const options = {
body: 'How are you doing? )',
image: '/images/demos/avatar-512x512.jpg',
icon: '/images/demos/icon-512x512.png',
badge: '/images/demos/badge-128x128.png',
actions: [
{
action: 'reply',
type: 'text',
title: 'Reply',
icon: '/images/demos/action-5-128x128.png',
}
],
};

registration.showNotification(title, options);

在Android中的显示效果如下:
reply1.jpg

点击action的按钮会打开一个文本输入框:
reply2.jpg
还可以自定义文本输入框的占位提示符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const title = 'Alexey Rodionov';

const options = {
body: 'How are you doing? )',
icon: '/images/demos/avatar-512x512.jpg',
badge: '/images/demos/badge-128x128.png',
actions: [
{
action: 'reply',
type: 'text',
title: 'Reply',
icon: '/images/demos/action-5-128x128.png',
placeholder: 'Type text here',
}
],
};

registration.showNotification(title, options);

reply3.jpg
在Windows的Chrome中,文本输入框会直接显示出来,而没有按钮:

reply4.jpg

你还可以添加多个行内快捷回复或者添加一个按钮和一个快捷回复:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const title = 'Poll';

const options = {
body: 'Do you like this photo?',
image: '/images/demos/cat-image.jpg',
icon: '/images/demos/icon-512x512.png',
badge: '/images/demos/badge-128x128.png',
actions: [
{
action: 'yes',
type: 'button',
title: '👍 Yes',
},
{
action: 'no',
type: 'text',
title: '👎 No (explain why)',
placeholder: 'Type your explanation here',
},
],
};

registration.showNotification(title, options);

reply5.jpg

对齐方式

dir参数可以允许定义文字的对齐方式,右对齐或者左对齐。在测试过程中,文字的对齐方式似乎是由文字决定的,而不是该参数,不过根据规范,该参数还可以设置action相关选项的对齐方式。你可以显示的设置该字段,或者由浏览器根据当前的文字决定展示的对齐方式。该参数支持的值有autoltr, rtl
在Linux的Chrome中右对齐的语言显示效果如下:
alb1.jpg

在Firefox中的显示效果如下:
alb.jpg

抖动

vibrate选项允许你定义一个抖动模式,当通知框展示的时候会有抖动的效果,不过这需要用户的设备允许抖动(例如设备处于非静音模式)。vibrate选项的格式是个由数字组成的数组,第一个数字表示震动的毫秒数,紧接着第二个数字是非抖动的毫秒数,以此类推。

1
2
3
4
5
6
7
8
9
10
11
12
const title = 'Vibrate Notification';

const options = {
// Star Wars shamelessly taken from the awesome Peter Beverloo
// https://tests.peter.sh/notification-generator/
vibrate: [
500, 110, 500, 110, 450, 110, 200, 110, 170, 40, 450, 110, 200, 110, 170,
40, 500,
],
};

registration.showNotification(title, options);

该选项只支持某些特定的设备。

声音

sound选项允许你定义提示音,当消息框展示时,提示音会播放,写这篇文章时,还没有浏览器支持该选项。

1
2
3
4
5
6
7
const title = 'Sound Notification';

const options = {
sound: '/demos/notification-examples/audio/notification-sound.mp3',
};

registration.showNotification(title, options);

时间戳

timestamp选项允许你定义推送事件的发生时间,该选项是一个UNIX时间戳的毫秒数。

1
2
3
4
5
6
7
8
const title = 'Timestamp Notification';

const options = {
body: 'Timestamp is set to "01 Jan 2000 00:00:00".',
timestamp: Date.parse('01 Jan 2000 00:00:00'),
};

registration.showNotification(title, options);

用户体验的最佳实践

我见过最差的用户体验是展示的通知框不包含任何有用的信息。你应该考虑你为什么要发送消息,并确保利用所有的选项来帮助用户理解他们为什么要读这个通知。不过,坦白地讲,人们很容易的认为自己不会犯同样的错误,但是又很容易掉入这些陷阱中。下面是一些可以避免的错误:

  • 不要将你的网址放在title和body中,浏览器的通知框已经包含了你的域名,所以没有必要重复。
  • 尽可能多的展示可利用的信息,例如当你需要展示某个用户发送的消息时,不要使用你有“一条新消息”和“点击阅读”作为title和body,而使用”John发给你一条消”息作为title,并把消息的一部分作为body.

浏览器特性检测

写这篇文章时,Chrome浏览器和Firefox浏览器通知选项的支持度方面还有较大的差异。幸运的是,你可以使用window.Notification的原型来检测浏览器是否支持某个选项。假设我们想知道浏览器通知是否支持action按钮,我们可以使用下面的代码:

1
2
3
4
5
if ('actions' in window.Notification?.prototype) {
// Action buttons are supported.
} else {
// Action buttons are NOT supported.
}

这样我们就能在不同的浏览器中展示不同的效果,检测其他特性的方式只需要将'action'替换成其他选项即可。