PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

详解用SVG给 favicon 添加标识

青灯夜游
青灯夜游 转载
2022-09-07 10:30:16 1563浏览

怎么使用svg给 favicon 添加标识?下面本篇文章给大家介绍一下使用 svg 生成带标识的 favicon的方法,希望对大家有所帮助!

之前做了一个 Chrome 插件,可以根据地址的不同生成不同的图标,这样可以很方便的区分不同的开发环境,效果如下

image-20220507151813352

主要实现过程其实不复杂,首先获取网站 favicon,然后给 favicon 添加标识,重新绘制生成就行了

image-20220515170432251

其中,这里的图标就是通过 SVG 生成的,下面看看具体实现吧。【推荐学习:css视频教程】

一、favicon 的获取方式

想知道获取方式,可以先了解设置方式。

一般有两种方式可以设置网站的 favicon

第一种,通过 link 标签设置(需要rel="icon"属性)

<link>

第二种,直接在网站根目录放一张favicon.ico(必须是这个名称,浏览器默认的),html 中什么也不用设置

- 网站目录
    index.html
    favicon.ico

如果以上都没有设置,那么大概率会看到以下错误

image-20220515115822814

了解这些,获取就简单了,先通过link获取,只要rel包含icon就行了

const link = document.querySelector('link[rel~="icon"]');

如果找不到,可以请求/favicon.ico,这里直接添加一个link

function getLink(){
    const link = document.querySelector('link[rel~="icon"]');
    if (link) {
        return link
    } else {
        const link = document.createElement('link');
        link.rel = "icon";
        link.href = "/favicon.ico"
        document.head.append(link);
        return link
    }
}

这样获取的link就保证存在了,然后就是绘制

二、利用 canvas 进行绘制

由于需要合成图像,所以需要先绘制原有图像。提到图像绘制,可以想到 canvas 绘制,只需要一点点 canvas 基础知识就足够了。具体实现如下

const canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
    canvas.height = img.height;
    canvas.width = img.width;
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    let dataURL = canvas.toDataURL("image/png");
    resolve(dataURL);
    canvas = null;
};
img.src = url;

由于存在/favicon.ico没有设置的情况,所以需要在 img 加载失败的时候给一张默认图

img.onerror = () => {
    resolve("默认图base64");
}

这样就能获取到 favicon 的图像信息了

三、利用 SVG 进行图片合成

在上面的基础上,其实可以继续通过 canvas 进行绘制,再绘制一个标签也不是难事。不过这里可以采用 SVG 的方式来进行绘制,有以下一些优点

  • 成本更低,比 canvas 更易理解

  • 灵活性高,可以通过 CSS 进行一些样式控制

首先,我们可以在 HTML 中自由的、像正常网页开发一样,绘制这样一个布局,相信没有什么难度

image-20220515162514000

  <strong>local</strong>
  <img>

由于宽度有限,所以需要强制文本换行,超出隐藏,关键样式如下

strong{
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  text-transform: uppercase;
  background-color: orange;
  color: #fff;
  padding: 0px 2px;
  border-radius: 2px;
  font-size: 12px;
  box-sizing: border-box;
  max-width: 100%;
  width: max-content;
  height: 16px;
  line-height: 16px;
  word-break: break-all;
  overflow: hidden;
}

接着,将这一段 html 放入 foreignObject标签中,关于  foreignObject 的作用,有兴趣的可以参考张鑫旭老师的这篇文章 SVG 简介与截图等应用,在这里,你可以简单理解为是可以包含 HTML 的标签,而 SVG 本身也是一种图片,这样就达到了合成图像的目的

img

具体实现如下

const link = getLink();
const icon = await img2Base64(link.href);
const favicon = `data:image/svg+xml;charset=utf-8,
  
  <strong>local</strong>
  <img>
  `.replace(/\n/g, '').replace(/\t/g, '').replace(/#/g, '%23')

这里需要注意几点

  • img 标签在 svg 中需要写成<img>闭合形态,不然会被认为结构错误

  • img 只能使用内联图片,比如 base64,这也是为何绘制原始 favicon 的原因

  • 如果使用内联 svg,需要对 svg 进行转义

  • 字符串中的单双引号问题也需要注意一下

然后,将生成的 SVG 直接设置为 favicon

link.href= favicon;

image-20220515171925842

这样就能正常的渲染了~

完整实现可以参考项目:https://github.com/XboxYan/auto-dev-favicon-chrome-plugin

四、一些局限性

首先是兼容性。

目前只有 Chrome 和 Firefox 是支持的,为了兼容其他浏览器,可以用一个 .ico来兜底

<link><link>

另外,在 Chrome 上还有一个限制(不知道是不是Chrome 更新后的限制),当 favicon 使用 svg 格式图片时,此时的 svg 会处于一种secure-static-mode,在这种模式下,所有动画都不会执行,会处于第一帧,比如下面这个例子

<svg>
  <foreignobject>
      
        <style>
        html,body{
            margin: 0;
            height: 100%
        }
        div{
            height: 100%;
            background: pink;
            animation: hue 3s infinite;
        }
        @keyframes hue {
            to {
                filter: hue-rotate(360deg)
            }
        }
        </style>
        <div></div>
      
    </foreignobject></svg>

很简单的一个背景颜色动画。在 Firefox 上是用作 favicon 是有动画的

Kapture 2022-05-15 at 19.08.20.gif

但是,Chrome 上却不行,只有禁止的第一帧

image-20220515191044404

所以之前想实现标识文本滚动的效果可以就此打住了

比较类似的还有媒体查询,之前在网上看到有这样的实现,直接在 SVG 中实现黑暗模式

<svg>
    <style>
        path {
            fill: #fff;
        }
        rect {
            fill: #B09AFE;
        }
        @media (prefers-color-scheme: dark) {
            path {
                fill: #B09AFE;
            }
            rect {
                fill: #fff;
            }
        }
    </style>
    <rect></rect>
    <path></path></svg>

但是也是同样的问题,只有 Firefox 下可行,Chrome是静止不动的

Kapture 2022-05-15 at 19.18.39

总的来说,SVG 在绘制方面提供了无限可能,不仅仅是本文中的案例,觉得 canvas 过于复杂的都可以考虑这一方案

(学习视频分享:web前端

声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除