favicon.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. // Favicon 工具函数
  2. /** 使用 URL 更新 favicon(如恢复默认) */
  3. export function updateFavicon(url: string) {
  4. const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
  5. if (link) {
  6. link.href = url;
  7. } else {
  8. const newLink = document.createElement("link");
  9. newLink.rel = "icon";
  10. newLink.href = url;
  11. document.head.appendChild(newLink);
  12. }
  13. }
  14. /** 移除动态 favicon,恢复为默认(需页面存在 link[rel=icon] 指向默认图标) */
  15. export function removeFavicon() {
  16. const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
  17. if (link) {
  18. link.remove();
  19. }
  20. }
  21. const DEFAULT_FAVICON = "/favicon.ico";
  22. /** 用 Canvas 绘制红底白字数字徽章,并设为 favicon(未读数 > 0 时使用) */
  23. export function updateFaviconWithBadge(count: number) {
  24. if (count <= 0) {
  25. updateFavicon(DEFAULT_FAVICON);
  26. return;
  27. }
  28. const size = 64;
  29. const canvas = document.createElement("canvas");
  30. canvas.width = size;
  31. canvas.height = size;
  32. const ctx = canvas.getContext("2d");
  33. if (!ctx) return;
  34. const text = count > 99 ? "99+" : String(count);
  35. const fontSize = text.length >= 2 ? 16 : 18;
  36. const radius = text.length >= 2 ? 14 : 12;
  37. const cx = size - radius - 4;
  38. const cy = radius + 4;
  39. ctx.clearRect(0, 0, size, size);
  40. ctx.beginPath();
  41. ctx.arc(cx, cy, radius, 0, Math.PI * 2);
  42. ctx.fillStyle = "#dc2626";
  43. ctx.fill();
  44. ctx.strokeStyle = "#fff";
  45. ctx.lineWidth = 2;
  46. ctx.stroke();
  47. ctx.fillStyle = "#fff";
  48. ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
  49. ctx.textAlign = "center";
  50. ctx.textBaseline = "middle";
  51. ctx.fillText(text, cx, cy);
  52. const dataUrl = canvas.toDataURL("image/png");
  53. updateFavicon(dataUrl);
  54. }
  55. export { DEFAULT_FAVICON };