这是您切换标签时首先看到的内容。
这是解释favicon是什么的一种方式。标签区域比大多数人假设的要珍贵得多。如果做得好,除了作为带有图标的标签外,它还可以成为代表网页内容或正在发生什么的完美广告牌。

当您不在活动标签上时,Favicon 实际上最有用。这是一个示例
想象一下,您正在将最近夏季度假的照片备份到云服务中。在上传过程中,您打开了一个新标签页,收集有关您去过的地方的详细信息,以便稍后为这些照片添加注释。一件事接着一件事,现在您在第七个标签页上观看 Casey Neistat。但是,在焦急地不时检查云服务页面以查看照片是否已上传之前,您无法继续您的 YouTube 马拉松。
在这种情况下,我们可以发挥创意!如果我们可以动态更改该 favicon 中的像素并显示上传进度会怎么样?这正是我们将在本文中要做的。
在受支持的浏览器中,我们可以借助 JavaScript、HTML <canvas>
和一些古老的几何图形将加载/进度动画显示为 favicon。
直接进入主题,我们将从最简单的部分开始:将图标和 canvas 元素添加到 HTML 中。
<head>
<link rel="icon" type="image/png" href="" width=32px>
</head>
<body>
<canvas width=32 height=32></canvas>
</body>
在实际使用中,您可能希望隐藏页面上的<canvas>
,一种方法是使用 HTML hidden
属性。
<canvas hidden width=32 height=32></canvas>
我将让<canvas>
在页面上可见,以便您可以看到 favicon 和 canvas 图像一起动画。
favicon 和 canvas 都采用标准的 favicon 大小:32 个正方形像素。
出于演示目的,为了触发加载动画,我在页面上添加了一个按钮,单击该按钮将启动动画。这也包含在 HTML 中
<button>Load</button>
现在让我们设置 JavaScript。首先,检查 canvas 支持
onload = ()=> {
canvas = document.querySelector('canvas'),
context = canvas.getContext('2d');
if (!!context) {
/* if canvas is supported */
}
};
接下来,添加按钮点击事件处理程序,该处理程序将提示 canvas 中的动画。
button = document.querySelector('button');
button.addEventListener('click', function() {
/* A variable to track the drawing intervals */
n = 0,
/* Interval speed for the animation */
loadingInterval = setInterval(drawLoader, 60);
});
drawLoader
将是每隔 60 毫秒执行一次绘制操作的函数,但在编写代码之前,我想定义要绘制的正方形线条的样式。让我们做一个渐变。
/* Style of the lines of the square that'll be drawn */
let gradient = context.createLinearGradient(0, 0, 32, 32);
gradient.addColorStop(0, '#c7f0fe');
gradient.addColorStop(1, '#56d3c9');
context.strokeStyle = gradient;
context.lineWidth = 8;
在drawLoader
中,我们将按百分比绘制线条:在前 25 个间隔内,将逐步绘制顶线;在第二季度,将绘制右线;依此类推。
动画效果是通过在每个间隔重新绘制前一个间隔的线条稍微长一点之前擦除<canvas>
来实现的。
在每个间隔期间,一旦在 canvas 中完成绘制,它就会快速转换为 PNG 图像,并将其分配为 favicon。
function drawLoader() {
with(context) {
clearRect(0, 0, 32, 32);
beginPath();
/* Up to 25% */
if (n<=25){
/*
(0,0)-----(32,0)
*/
// code to draw the top line, incrementally
}
/* Between 25 to 50 percent */
else if(n>25 && n<=50){
/*
(0,0)-----(32,0)
|
|
(32,32)
*/
// code to draw the top and right lines.
}
/* Between 50 to 75 percent */
else if(n>50 && n<= 75){
/*
(0,0)-----(32,0)
|
|
(0,32)----(32,32)
*/
// code to draw the top, right and bottom lines.
}
/* Between 75 to 100 percent */
else if(n>75 && n<=100){
/*
(0,0)-----(32,0)
| |
| |
(0,32)----(32,32)
*/
// code to draw all four lines of the square.
}
stroke();
}
// Convert the Canvas drawing to PNG and assign it to the favicon
favicon.href = canvas.toDataURL('image/png');
/* When finished drawing */
if (n === 100) {
clearInterval(loadingInterval);
return;
}
// Increment the variable used to keep track of the drawing intervals
n++;
}
现在到绘制线条的数学和代码了。
以下是如何在最初 25 个间隔的每个间隔中逐步绘制顶线
n = current interval,
x = x-coordinate of the line’s end point at a given interval.
(y-coordinate of the end point is 0 and start point of the line is 0,0)
在所有 25 个间隔完成后,x的值为 32(favicon 和 canvas 的大小)。
所以……
x/n = 32/25
x = (32/25) * n
应用此数学并绘制线条的代码为
moveTo(0, 0); lineTo((32/25)*n, 0);
对于接下来的 25 个间隔(右线),我们以类似的方式定位 y 坐标。
moveTo(0, 0); lineTo(32, 0);
moveTo(32, 0); lineTo(32, (32/25)*(n-25));
这是使用其余代码绘制所有四条线的指令。
function drawLoader() {
with(context) {
clearRect(0, 0, 32, 32);
beginPath();
/* Up to 25% of the time assigned to draw */
if (n<=25){
/*
(0,0)-----(32,0)
*/
moveTo(0, 0); lineTo((32/25)*n, 0);
}
/* Between 25 to 50 percent */
else if(n>25 && n<=50){
/*
(0,0)-----(32,0)
|
|
(32,32)
*/
moveTo(0, 0); lineTo(32, 0);
moveTo(32, 0); lineTo(32, (32/25)*(n-25));
}
/* Between 50 to 75 percent */
else if(n>50 && n<= 75){
/*
(0,0)-----(32,0)
|
|
(0,32)----(32,32)
*/
moveTo(0, 0); lineTo(32, 0);
moveTo(32, 0); lineTo(32, 32);
moveTo(32, 32); lineTo(-((32/25)*(n-75)), 32);
}
/* Between 75 to 100 percent */
else if(n>75 && n<=100){
/*
(0,0)-----(32,0)
| |
| |
(0,32)----(32,32)
*/
moveTo(0, 0); lineTo(32, 0);
moveTo(32, 0); lineTo(32, 32);
moveTo(32, 32); lineTo(0, 32);
moveTo(0, 32); lineTo(0, -((32/25)*(n-100)));
}
stroke();
}
// Convert the Canvas drawing to PNG and assign it to the favicon
favicon.href = canvas.toDataURL('image/png');
/* When finished drawing */
if (n === 100) {
clearInterval(loadingInterval);
return;
}
// Increment the variable used to keep track of drawing intervals
n++;
}
就这样!您可以从此 GitHub 仓库查看并下载演示代码。额外奖励:如果您正在寻找圆形加载程序,请查看此仓库。
您可以使用任何形状,如果您在 canvas 绘制中使用fill
属性,则会产生不同的效果。
只是好奇,为什么使用
if(!!context)
检查而不是仅仅使用if(context)
?!!
不会只是将真值转换为true
吗,这与if(condition)
检查的内容完全相同?我是否错过了此方法可以更好地处理的某些情况?+1
看到这个用例时,我也问了自己同样的问题!
这是因为
context
不是布尔值,所以您不能只执行if(context)
,这就像执行if(3)
一样,这没有多大意义。(即使我不知道 js 目前是如何处理这些情况的)因此,为了获得布尔值,
context
首先使用!
取反,这始终返回布尔值。但是由于该布尔值的方向错误,我们使用另一个!
再次取反。因此,基本上,如果
context
为null
,则!context
将返回true
,因此我们执行!!context
(您可以将其视为!(!context)
)以获得我们需要的false
。如果它确实存在,则
!context
将为 false,因此我们再次执行!!context
以获得true
作为结果。Scott,你说得对:) 请参阅 https://mdn.org.cn/en-US/docs/Glossary/Truthy
Favicon 也可以用作某种滚动文本消息:https://kawalekkodu.pl/wiadomosc-z-czapy-czyli-o-nietypowym-wykorzystaniu-favicon
附言:请记住,setInterval/setTimeout 会变慢并停止,因为标签处于非活动状态。
分配给 favicon 的行似乎对我不起作用。“favicon.href = …”的执行会产生控制台错误“favicon 未定义”。有什么建议吗?
Konrad,检查文章底部附近链接的 GitHub 存储库中的代码。script.js 文件显示了所需的声明:favicon = document.querySelector(‘link[rel*=”icon”]’);
Preethi,也许编辑文章以包含此代码行可以消除任何混淆。
在 Chrome 中观察到,如果标签处于活动状态,动画速度很快,如果标签处于非活动状态,则动画速度很慢。我猜这是一个性能优化。
这不是优化问题。这是我在上面评论中提到的内容。请查看。
非常棒的想法!
但是,是否有
setInterval
的替代方案,因为它在非活动标签上速度极慢?是的,请查看我上面的评论。我的博客链接在那里,但使用波兰语(使用 Google 翻译),并且那里也有解决方案。
所有基于时间的 API 在标签处于非活动状态时都会变慢。这是浏览器为了帮助节省电池电量和其他计算机资源而做出的有意决定。在现实世界中,您可能会使用实时进度数据,因此动画可能会不稳定,但不会仅仅因为标签处于非活动状态而“变慢”。如果您只关心执行动画而不是显示实际数据,则可以使用另一种方法,即检测每帧之间实际经过了多少时间,并将 canvas 绘制为时间的百分比,而不是每帧递增 1。当标签处于非活动状态时,帧率仍然会下降,但动画的持续时间仍然一致。
这太棒了!演示链接和 GitHub 代码使它非常清晰(文章中的代码缺少一些关键部分)
到目前为止,我只看到 Twitter 的 favicon 更改了通知(甚至在我的书签栏中)。这是一个我很想使用的巧妙技巧