通常情况下,整个域名只使用一个图标。但有些时候,你需要根据具体情况使用不同的图标。一个网站可能会改变favicon以配合正在浏览的内容。或者一个网站可能允许用户个性化他们的主题颜色,而这些偏好会反映在图标上。也许你已经看到了那些试图提醒用户某些事件的图标。
从技术上讲,多个图标可以通过手工管理--克里斯向我们展示了他如何使用两个不同的图标进行开发和生产。但是当你达到几十个或几百个变化的规模时,就需要动态地生成它们了。
这是我在最近的一个WordPress项目中遇到的情况,该项目是一个学院和大学的目录。(我以前为同一个项目写过关于查询附近地点的文章。)当查看一个学校的简介时,我们希望favicon使用学校的颜色,而不是我们默认的蓝色,以便给它带来额外的感觉。
由于目录中有200多所学校,而且在不断攀升,我们需要进行动态调整。幸运的是,我们已经有了自定义的元字段来存储每所学校的视觉识别数据。这包括学校的颜色,这些颜色在样式表中被使用。我们只需要一种方法,将这些自定义元颜色应用到动态图标上。
在这篇文章中,我将向你介绍我们的方法和一些需要注意的事项。通过查看不同的学校,你可以看到行动的结果。
![]()
每个favicon在标签中的颜色都是根据所选择的学校而不同的。
SVG是关键
由于浏览器对SVG favicons的支持有所改善,实现动态favicons比过去要容易得多。与PNG(或陈旧的ICO格式)相比,SVG依靠标记来定义矢量图形。这使得它们轻巧、可扩展,而且最重要的是,可以接受各种乐趣。
第一步是用SVG格式创建你的favicon。之后再通过一个SVG优化器来消除杂乱无章的东西也无妨。这就是我们在学校目录中使用的东西。
与WordPress挂钩
接下来,我们要在HTML头部添加favicon链接的标记。如何做到这一点,完全取决于你。在WordPress中,可以把它添加到子主题的标题模板中,或者通过echo'd的 wp_head()动作。
function ca_favicon() {
if ( is_singular( 'school' ) ) {
$post_id = get_the_ID();
$color = get_post_meta( $post_id, 'color', true );
if ( isset( $color ) ) {
$color = ltrim( $color, '#' ); // remove the hash
echo '<link rel="icon" href="' . plugins_url( 'images/favicon.php?color=' . $color, __FILE__ ) . '" type="image/svg+xml" sizes="any">';
}
}
}
add_filter( 'wp_head' , 'ca_favicon' );
在这里,我们要检查帖子类型是school ,并抓取学校的color 元数据,我们之前已经用 [get_post_meta()](https://developer.wordpress.org/reference/functions/get_post_meta/).如果我们确实有一个颜色,我们通过查询字符串把它传到favicon.php 。
从PHP到SVG
在一个favicon.php 文件中,我们首先将内容类型设置为SVG。接下来,我们保存传入的颜色值,如果没有的话,则使用默认的颜色。
然后,我们使用PHP的heredoc语法(对模板很有用)echo 大块的、多行的SVG标记。在使用这种语法时,诸如$color 等变量会被扩展。
最后,我们对SVG标记做了一些修改。首先,为变色元素分配了类。其次,在SVG元素内部添加了一个样式元素,声明了适当的CSS规则,并在echo-ing the$color 。
如果不使用<style> 元素,我们可以在默认颜色出现的地方用$color 代替它,如果它不在太多地方使用的话。
<?php
header( 'Content-Type: image/svg+xml' );
$color = $_GET[ 'color' ] ?? '065281';
$color = sanitize_hex_color_no_hash( $color );
echo <<<EOL
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000">
<style type="text/css">
<![CDATA[
.primary {
fill: #$color;
}
.shield {
stroke: #$color;
}
]]>
</style>
<defs>
<path id="a" d="M0 34L318 0l316 34v417.196a97 97 0 01-14.433 50.909C483.553 722.702 382.697 833 317 833S150.447 722.702 14.433 502.105A97 97 0 010 451.196V34z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<g transform="translate(183 65)">
<mask id="b" fill="#fff">
<use xlink:href="#a"/>
</mask>
<use fill="#FFF" xlink:href="#a"/>
<path class="primary" mask="url(#b)" d="M317-37h317v871H317z"/>
<path class="primary" mask="url(#b)" d="M0 480l317 30 317-30v157l-317-90L0 517z"/>
<path fill="#FFF" mask="url(#b)" d="M317 510l317-30v37l-317 30z"/>
</g>
<g fill-rule="nonzero">
<path class="primary" d="M358.2 455.2c11.9 0 22.633-.992 32.2-2.975 9.567-1.983 18.375-4.9 26.425-8.75 8.05-3.85 15.458-8.458 22.225-13.825 6.767-5.367 13.3-11.433 19.6-18.2l-34.3-34.65c-9.567 8.867-19.192 15.867-28.875 21-9.683 5.133-21.525 7.7-35.525 7.7-10.5 0-20.125-2.042-28.875-6.125s-16.217-9.625-22.4-16.625-11.025-15.167-14.525-24.5-5.25-19.25-5.25-29.75v-.7c0-10.5 1.75-20.358 5.25-29.575 3.5-9.217 8.4-17.325 14.7-24.325 6.3-7 13.825-12.483 22.575-16.45 8.75-3.967 18.258-5.95 28.525-5.95 12.367 0 23.508 2.45 33.425 7.35 9.917 4.9 19.658 11.667 29.225 20.3l34.3-39.55a144.285 144.285 0 00-18.2-15.4c-6.533-4.667-13.65-8.633-21.35-11.9-7.7-3.267-16.275-5.833-25.725-7.7-9.45-1.867-19.892-2.8-31.325-2.8-18.9 0-36.167 3.325-51.8 9.975-15.633 6.65-29.05 15.75-40.25 27.3s-19.95 24.967-26.25 40.25c-6.3 15.283-9.45 31.675-9.45 49.175v.7c0 17.5 3.15 33.95 9.45 49.35 6.3 15.4 15.05 28.758 26.25 40.075 11.2 11.317 24.5 20.242 39.9 26.775 15.4 6.533 32.083 9.8 50.05 9.8z"/>
<path fill="#FFF" d="M582.35 451l22.4-54.95h103.6l22.4 54.95h56.35l-105-246.75h-49.7L527.4 451h54.95zM689.1 348.45H624L656.55 269l32.55 79.45z"/>
</g>
<path class="shield" stroke-width="30" d="M183 99l318-34 316 34v417.196a97 97 0 01-14.433 50.909C666.553 787.702 565.697 898 500 898S333.447 787.702 197.433 567.105A97 97 0 01183 516.196V99h0z"/>
</g>
</svg>
EOL;
?>
这样,你的网站就有一个动态的favicon了。
安全考虑
当然,盲目的echo-ing URL参数会使你受到黑客攻击。为了缓解这些问题,我们应该对我们所有的输入进行消毒。
在这种情况下,我们只对符合3位数或6位数十六进制颜色格式的值感兴趣。我们可以包括一个像WordPress自己的函数 [sanitize_hex_color_no_hash()](https://developer.wordpress.org/reference/functions/sanitize_hex_color_no_hash/)来确保只有颜色被传递进来。
function sanitize_hex_color( $color ) {
if ( '' === $color ) {
return '';
}
// 3 or 6 hex digits, or the empty string.
if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
return $color;
}
}
function sanitize_hex_color_no_hash( $color ) {
$color = ltrim( $color, '#' );
if ( '' === $color ) {
return '';
}
return sanitize_hex_color( '#' . $color ) ? $color : null;
}
你要根据你想传入的值来添加你自己的检查。
缓存以提高性能
浏览器会对SVG进行缓存,但默认情况下,PHP文件会失去这一优势。这意味着每次加载favicon时,你的服务器都会受到冲击。
为了减少服务器的压力和提高性能,你必须明确地缓存这个文件。你可以配置你的服务器,通过你的CDN设置一个页面规则,或者像这样在favicon.php 的最上面添加一个缓存控制头。
header( 'Cache-Control: public, max-age=604800' ); // 604,800 seconds or 1 week
在我们的测试中,在没有缓存的情况下,我们的1.5 KB SVG文件在第一次加载时需要300毫秒的时间来处理,在随后的加载中需要100毫秒。这是很糟糕的。但是,有了缓存,我们在第一次加载时,从CDN那里的处理时间降到了25毫秒,在以后的加载中,从浏览器缓存中的处理时间降到了1毫秒--和普通的SVG一样好。
浏览器支持
如果我们完成了这些,这就不是网络开发了。我们还是要谈谈浏览器支持。
如前所述,现代浏览器对SVG图标的支持是坚实的,并且在当前版本的Chrome、Firefox和Edge中得到完全支持。

SVG图标已经到来......除了Safari。
有一点需要注意的是,Firefox需要在favicon声明中加入属性type="image/svg+xml" ,这样才能发挥作用。其他浏览器则比较宽容,但这只是一个好的做法。你应该包括sizes="any" ,同时你也应该这样做。
<link rel="icon" href="path/to/favicon.svg" type="image/svg+xml" sizes="any">
Safari浏览器
Safari目前还不支持SVG图标,除了用于钉住标签的遮罩图标功能之外。在我的实验中,这是有问题的,而且不一致。它不能很好地处理复杂的形状或颜色,并且在整个领域中缓存了相同的图标。最终,我们决定不费吹灰之力,只使用默认的蓝色填充的后备方案,直到Safari改进支持。
回退
尽管SVG favicon的支持很可靠,但它仍然不是100%。所以一定要添加回退功能。当SVG图标不被支持时,我们可以用rel="alternative icon" 属性来设置一个额外的favicon。
<link rel="icon" href="path/to/favicon.svg" type="image/svg+xml" sizes="any">
<link rel="alternate icon" href="path/to/favicon.png" type="image/png">
为了使网站更加无懈可击,你还可以在你的根目录下放置永恒的favicon.ico 。