浏览器标签页徽章通知

12

我一直在寻找如何在网站页面的标签上获得类似下面图片中的通知徽章,但是没有找到什么有效的方法。我做了一个聊天网站,想让人们知道他们所在的频道是否有新的通知。但我需要知道如何在站点图标上获取一个红色徽章按钮或类似的东西。

最好像Discord那样做:

输入图像描述信息

我试过这个方法,但好像不起作用:

var count = 0;

var title = document.title;

function changeTitle() {
    count++;
    var newTitle = '(' + count + ') ' + title;
    document.title = newTitle;
}

function newUpdate() {
    update = setInterval(changeTitle, 2000);
}
var docBody = document.getElementById('site-body');
docBody.onload = newUpdate;

他们可能正在更新网站图标。实际上,这并不难做,并且有一些JavaScript库,例如favico.js可以帮助你完成这个任务。 - Lewis
@Lewis 是正确的。discord 正在更新网站图标。请查看这些图片 Image 1, Image 2。当您阅读未读通知时,您可以在 discord 的 head 标签中看到网站图标正在更改。 - panda
所以它们有从1到99的图标? - Cohen
也许,如果出于效率考虑 - 或者,它可以成为生成动态favicon png的脚本的一部分。 - Lewis
1个回答

29

简而言之: 使用新图标图片更新 <link> 元素。

enter image description here

基本操作步骤:

  • <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /> 元素中获取图像来源
  • 创建一个内存画布(Canvas)元素
  • 应用图标图片
  • 在上面绘制形状
  • 在上面绘制文本值
  • 使用画布元素ImageData的最终组合更新<link>元素的href属性

使用方法:

const myBadgerOptions = {}; // See: constructor for customization options
const myBadger = new Badger(myBadgerOptions);

// Live update value example:
myBadger.value = 3;

// Remove badge example:
// myBadger.value = 0;

// If needed, get the generated base64 image data: 
// console.log(myBadger.dataURL);

构造函数:

/**
 * Add notification badge (pill) to favicon in browser tab
 * @url stackoverflow.com/questions/65719387/
 */
class Badger {
  constructor(options) {
    Object.assign(
      this, {
        backgroundColor: "#f00",
        color: "#fff",
        size: 0.6,      // 0..1 (Scale in respect to the favicon image size)
        position: "ne", // Position inside favicon "n", "e", "s", "w", "ne", "nw", "se", "sw"
        radius: 8,      // Border radius
        src: "",        // Favicon source (dafaults to the <link> icon href)
        onChange() {},
      },
      options
    );
    this.canvas = document.createElement("canvas");
    this.src = this.src || this.faviconEL.getAttribute("href");
    this.ctx = this.canvas.getContext("2d");
  }

  faviconEL = document.querySelector("link[rel$=icon]");

  _drawIcon() {
    this.ctx.clearRect(0, 0, this.faviconSize, this.faviconSize);
    this.ctx.drawImage(this.img, 0, 0, this.faviconSize, this.faviconSize);
  }

  _drawShape() {
    const r = this.radius;
    const xa = this.offset.x;
    const ya = this.offset.y;
    const xb = this.offset.x + this.badgeSize;
    const yb = this.offset.y + this.badgeSize;
    this.ctx.beginPath();
    this.ctx.moveTo(xb - r, ya);
    this.ctx.quadraticCurveTo(xb, ya, xb, ya + r);
    this.ctx.lineTo(xb, yb - r);
    this.ctx.quadraticCurveTo(xb, yb, xb - r, yb);
    this.ctx.lineTo(xa + r, yb);
    this.ctx.quadraticCurveTo(xa, yb, xa, yb - r);
    this.ctx.lineTo(xa, ya + r);
    this.ctx.quadraticCurveTo(xa, ya, xa + r, ya);
    this.ctx.fillStyle = this.backgroundColor;
    this.ctx.fill();
    this.ctx.closePath();
  }

  _drawVal() {
    const margin = (this.badgeSize * 0.18) / 2;
    this.ctx.beginPath();
    this.ctx.textBaseline = "middle";
    this.ctx.textAlign = "center";
    this.ctx.font = `bold ${this.badgeSize * 0.82}px Arial`;
    this.ctx.fillStyle = this.color;
    this.ctx.fillText(this.value, this.badgeSize / 2 + this.offset.x, this.badgeSize / 2 + this.offset.y + margin);
    this.ctx.closePath();
  }

  _drawFavicon() {
    this.faviconEL.setAttribute("href", this.dataURL);
  }

  _draw() {
    this._drawIcon();
    if (this.value) this._drawShape();
    if (this.value) this._drawVal();
    this._drawFavicon();
  }

  _setup() {
    this.faviconSize = this.img.naturalWidth;
    this.badgeSize = this.faviconSize * this.size;
    this.canvas.width = this.faviconSize;
    this.canvas.height = this.faviconSize;
    const sd = this.faviconSize - this.badgeSize;
    const sd2 = sd / 2;
    this.offset = {
      n:  {x: sd2, y: 0 },
      e:  {x: sd, y: sd2},
      s:  {x: sd2, y: sd},
      w:  {x: 0, y: sd2},
      nw: {x: 0, y: 0},
      ne: {x: sd, y: 0},
      sw: {x: 0, y: sd},
      se: {x: sd, y: sd},
    }[this.position];
  }

  // Public functions / methods:

  update() {
    this._value = Math.min(99, parseInt(this._value, 10));
    if (this.img) {
      this._draw();
      if (this.onChange) this.onChange.call(this);
    } else {
      this.img = new Image();
      this.img.addEventListener("load", () => {
        this._setup();
        this._draw();
        if (this.onChange) this.onChange.call(this);
      });
      this.img.src = this.src;
    }
  }

  get dataURL() {
    return this.canvas.toDataURL();
  }

  get value() {
    return this._value;
  }

  set value(val) {
    this._value = val;
    this.update();
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接