在主题中用自定义图标替换 Discourse 的默认 SVG 图标

您可以单独替换 Discourse 的默认 SVG 图标,或将它们作为一个整体替换为您自己的自定义 SVG,并 在主题或主题组件中 覆盖它们。

步骤 1 - 创建 SVG 精灵图 (Spritesheet)

要开始,您必须创建一个 SVG 精灵图。它可以包含从单个附加的自定义 SVG 图标到数百个完整替换集中的任何内容。

精灵图应保存为 SVG 文件。原则上,您是将原始 SVG 图标文件中的 \u003csvg\u003e 标签内容嵌套到 \u003csymbol\u003e 标签中,并为其指定一个良好的标识符。

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="my-theme-icon-1">
    <!--
      SVG 代码在 \u003csvg\u003e 标签内部,来自源 SVG 图标文件
      这通常是 \u003csvg\u003e 标签之间的所有内容
      (但不包括 SVG 标签本身,它被上面的 \u003csymbol\u003e 替换)
      您可以将任何属性(例如 ViewBox="0 0 0 0")转移到 \u003csymbol\u003e 标签上
    -->
  </symbol>

  <symbol id="my-theme-icon-2">
    <!-- SVG 代码在这里。根据需要添加更多 \u003csymbol\u003e 块。
    -->
  </symbol>
</svg>
  • 确保在精灵图中的每个 symbol 上添加一个自定义 ID。为了您的方便,最好在 ID 前加上您的主题名称前缀,例如 my-theme-icon

  • 要使图标颜色像现有图标一样动态变化,请将 fill 设置为 currentColor,而不是硬编码的颜色(如 #333)。

  • 要正确缩放或居中您的图标,请在 \u003csymbol\u003e 标签上使用 viewBox 属性。有关更多信息,请参阅 How to Scale SVG | CSS-Tricks

  • 注意 SVG 中的样式冲突。例如,SVG 通常定义了像 .st0{fill:#FF0000;} 这样的内联样式。如果您有多个 SVG 使用相同的类,这可能会导致问题(要修复这些问题,请将类编辑为每个图标独有的)。

  • 如果您有很多图标,有方法可以实现自动化。https://www.npmjs.com/package/svg-sprite-generator 是一个简单的命令行工具,用于将 SVG 合并到精灵图中。

示例 - 单个自定义图标精灵图

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="bat-icon" viewBox="6 6 36 36">
    <path
      fill="currentColor"
      d="M24,18.2c0.7,0,0.9,0.2,0.9,0.2l0.4-1.7c0,0,0.4,1.5,0.4,2.8c0.2,1.1,2.2,0.4,3.9,0C31.4,19.1,32,16,32,16h16c0,0-9.4,3.5-7,10c0,0-14.8-2-17,7l0,0c-2.2-9-17-7-17-7c2.4-6.5-7-10-7-10h16c0,0,0.6,3.1,2.3,3.5c1.7,0.4,3.9,1.1,3.9,0c0.2-1.1,0.4-2.8,0.4-2.8l0.4,1.7C23.1,18.4,23.4,18.2,24,18.2L24,18.2L24,18.2z"
    />
  </symbol>
</svg>

步骤 2 - 将精灵图添加到您的主题

构建好精灵图后,您需要将 SVG 文件添加到您的组件/主题中。这可以通过 UI 轻松完成,或者您可以将其硬编码到组件/主题中。

:information_source: 一旦它被上传到任何已安装的组件/主题中,它就可以在您的整个实例中使用 \u003csymbol\u003e 标签中的 ID 来访问。

通过 UI

转到主题/组件设置的“上传”部分,并添加您的 sprite 文件,SCSS 变量名称设置为 icons-sprite

硬编码到主题/组件中

将精灵图文件添加到主题的 /assets 文件夹中。然后更新根文件夹中的 assets.json 文件。
对于名为 my-icons.svg 的 SVG sprite,您的 about.json 应包含以下内容:

"assets": {
  "icons-sprite": "/assets/my-icons.svg"
}

步骤 3(可选)- 覆盖默认图标

现在您的精灵图已设置好,您可以告诉 Discourse 替换图标。以下是如何从 api-initializer 进行操作的方法:

// {theme}/javascripts/discourse/api-initializers/init-theme.gjs

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  api.replaceIcon("bars", "my-theme-icon-bars");
  api.replaceIcon("link", "my-theme-icon-link");
  // 等等。
});

第一个 ID,bars,是 Discourse 中的默认图标 ID,第二个是您的替换图标的 ID。找到我们图标 ID 的最简单方法是在浏览器中检查该图标。

这里的图标名称遵循 d-icon- 前缀。所以在这个例子中它是 d-unliked

我们的大多数图标遵循 https://fontawesome.com/ 的图标名称,但也有例外(这就是为什么在检查器中检查 ID 是最可靠的方法)。您可以在 此处 github 上的 const REPLACEMENTS 中看到所有例外。

就是这样。您现在可以使用自己的自定义图标来设计 Discourse 的样式了!


此文档是版本控制的 - 在 github 上 建议更改。

57 个赞

如何定位特定元素中的特定图标?在我的例子中,我想用另一个 FA 图标替换侧边栏菜单中的文档图标。

我将使用 CSS 隐藏它,并为其添加一个新按钮。

Common / CSS

.sidebar-section-wrapper {
  li[data-list-item-name=docs] {
    display: none !important;
  }
}

在 More > Customize this section 中添加一个新按钮

3 个赞

这不起作用。我尝试了:

<script type="text/discourse-plugin" version="0.8">
    api.replaceIcon("shield-halved", "hat-wizard");
</script>

来自这里,但似乎不起作用。我认为脚本标签方法已损坏,因为这个在预览链接中不起作用。老实说,我不确定。

对我来说是有效的 :woman_shrugging:t2:

你是否将其放入了 head 标签?我也替换了我 header 中的 robot:

你可能需要将图标添加到 admin 的 SVG icon subset 设置中。

1 个赞

是的,head 标签。还有 header 标签,因为指南是这么说的。

完成了。现在可以用了。谢谢!

1 个赞

@NateDhaliwal 请私信我好吗?我需要一些帮助,但在您的个人资料中没有看到聊天选项。谢谢!

1 个赞
/* 对于左侧菜单,我们重新定义类别 Audi */ 中的前缀 span 类元素的背景 */
.navigation-category [data-category-id="6"] .prefix-span {
  background: url("https://raw.githubusercontent.com/tima4502/car-icons/bb0d0fae3e5b66c512a27a130b219ec0ee342ada/audi.svg") center/contain no-repeat !important;

当我点击主页时,方形图标又出现了!你能告诉我我哪里做错了什么吗?在类别页面本身上,它可以正常工作。

你好,有人能澄清主题/组件名称、文件名、SCSS 变量名和符号 ID 之间的关系吗?

我正试图用我们自己的图标替换版主 shield-halved 图标,但说明有点不清楚。

在第 2 步:

  • 通过 UI” 截图显示的文件名是 baticonsprite.svg,SCSS 变量名是 icons-sprite
  • 但在“硬编码到主题”中,它会告诉你将其硬编码到主题/组件中
    • 但如何操作?我在编辑器中看不到 assets.json 文件。如果我导出组件,我会看到一个 about.json,其中显示了我通过 UI 上传的 sprite
    • 但这个例子也显示了一个不同的文件名 /assets/my-icons.svg — 这是指与 baticonsprite.svg 相同的文件吗?
    • 这两种方法是做同一件事的替代方法,你只需要做其中一种另一种,而不是两者都做……?

在第 3 步:

  • 但现在,在 api.replaceIcon() 中,第二个参数没有使用任何前面的 ID,不是 icons-spritebat-iconbaticonsprite.svgmy-icons.svg。相反,我们得到了一个全新的 my-theme-icon-bars……感到困惑。
    • my-theme 前缀是必需的吗?如果是,那么“主题名称”字符串来自哪里?比如它应该是 my-theme-bat-icon 吗?如果它是一个组件而不是一个主题呢?
    • 对于 icon-bars 部分,它应该是:
      • SVG 精灵表 XML 中的符号 ID
      • SVG 文件的文件名
      • 你给它的 SCSS 变量名
      • 以上某个组合(例如 icons-sprite-bat-icon?)

你实际在哪里放置 api.replaceIcon() 调用?将其放入自定义组件的“JS”选项卡中是否可以,该选项卡已包含样板代码:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
   // your code here
});

或者是否有必要创建一个自定义 <script type=”discourse/plugin”> 标签并将其放在 <head> 选项卡中?


抱歉我的困惑。

我尝试了上述几种组合,但无论如何都无法显示我的 sprite……

我的 sprite XML 看起来像:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">


<symbol id="my-logo" viewBox="0 0 94.652 95.261"><defs><linearGradient id="a" y1="47.631" x2="94.652" y2="47.631" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff593d"/><stop offset="1" stop-color="#ff7751"/></linearGradient></defs><title>d_only</title><path d="M47.326,0H0V95.261H47.326c23.67,0,47.326-21.326,47.326-47.624S71,0,47.326,0Zm0,69.274a21.644,21.644,0,1,1,21.65-21.637A21.635,21.635,0,0,1,47.326,69.274Z" fill="url(#a)"/></symbol>

</svg>

文件名是 my-logo.svg,SCSS 变量名也是 my-logo

在自定义组件的“JS”选项卡中,我有:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
    api.replaceIcon("shield-halved", "my-logo")
});

但似乎什么都没有显示。我是否遗漏了某个步骤,或者是我误解了某种魔术字符串插值……?