html - <use/> <symbol/> 与 &lt;style/> 来应用基于应用于父级 HTML 的类的 CSS 规则

标签 html css svg

我想用 SVG 构建一个图标集并将其用于按钮。这些图标根据应用的 CSS 类更改其样式。例如。想象一下,当菜单打开时,汉堡菜单图标会转换为关闭图标。

为了提高可读性,我简化了此处所有示例中的路径。请想象一下,这两条路径将展开,最终显示一个十字,并通过 CSS 过渡进行动画处理。此处代码的预期结果如下所示:

Paths expanding with stroke-dasharray

使用 .svg 文件中的图标

当然,我更喜欢使用外部 Sprite SVG 进行缓存,例如

<!-- icons-file.svg -->
<svg>
  <def>
    <symbol id="menu" viewBox="0 0 100 100">
      <path d="M20,40H90"/>
      <path d="M20,60H70"/>
    </symbol>
    <!-- more symbols to come -->
    <style>
      #menu > path {
        /* ... */
        stroke-dasharray: 40 500;
      }
      .active #menu > path:nth-child(1) {
        stroke-dasharray: 80 500;
      }
      .active #menu > path:nth-child(2) {
        stroke-dasharray: 50 500;
        stroke-dashoffset: -10;
      }
    </style>
  </def>
</svg>

并在我的 HTML 中使用它

<a href="#"><svg><use href="icons-file.svg#menu"/></svg></a>

这甚至无法中途工作,因为 <style/> <use/> 忽略标签.

添加 Sprite SVG 内联

幸运的是,我正在开发单页 PWA。当然,兑现更好,但是通过内联 SVG,我(希望)只添加到应用程序的初始加载,而不是每个页面/ View 。

<use/>限制仍然存在,但在这里我可以在 HTML 层中定义我的 SVG 样式:

<html>
  <!-- ... -->
  <body>
    <svg>
      <def>
        <symbol id="menu" viewBox="0 0 100 100"><!-- ... --></symbol>
        <!-- more symbols to come -->
      </def>
    </svg>
    <style>
    #menu > path {
      /* ... */
      stroke-dasharray: 40 500;
    }
    .active #menu > path:nth-child(1) {
      stroke-dasharray: 80 500;
    }
    /* ... */
    </style>

    <a href="#"><svg><use href="#menu"/></svg></a>
  </body>
</html>

现在,图标在默认状态下正确显示(作为汉堡菜单图标)。但无论我在哪里应用 active CSS 类,甚至在 <use/> 上节点,它永远不会受到尊重。据我了解,这是因为 CSS 选择器无法打破 Shadow DOM 的边界。

但是等等, :host 怎么样?选择器?拥有如下规则应该可以解决问题:

:host(.active) #menu > path:nth-child(1) {
  stroke-dasharray: 80 500;
}
/* or maybe */
:host(.active) path:nth-child(1) {
  stroke-dasharray: 80 500;
}

再次,没有运气。 See an example on codepen.说实话,我不知道原因。我猜这是因为 <use/> 的影子 DOM已关闭,但无法找到任何信息(如果确实是这个原因)。 This example on jsfiddle使用自定义元素和开放的 Shadow DOM 可以完美地工作。

CSS property: inherit技巧

有人可能会说我应该做这样的事情:

path {
  stroke-dasharray: inherit;
}

然后我可以设置stroke-dasharray到外部的任何值,例如

<a style="stroke-dasharray: 80 500"><svg><!-- ... --></svg></a>

是的,如果我只有一条路径或者所有路径具有相同的长度、开始和结束样式,这是可能的。事实并非如此。

每个图标和每个实例的整个 SVG

最后,我现在唯一的出路是将图标的 SVG 直接放在 anchor 中:

<a href="#">
  <svg id="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
    <path d="M20,40H80V20"/>
    <path d="M20,60H70V70"/>
    <style>
      path {
        fill: none;
        stroke: black;
        stroke-width: 6px;
        transition: all 0.5s;
      }
      path:nth-child(1) {
        stroke-dasharray: 40 500;
      }
      .active path:nth-child(1) {
        stroke-dasharray: 80 500;
      }
      path:nth-child(2) {
        stroke-dasharray: 10 500;
      }
      .active path:nth-child(2) {
        stroke-dasharray: 50 500;
        stroke-dashoffset: -10;
      }
    </style>
  </svg>
</a>

Here is the result on codepen

这是最坏的情况。没有缓存,对于我在列表中使用的图标,我多次使用相同的代码,严重影响了结果。

所以我的问题是:我是否监督某些事情?有更好的办法吗?

最佳答案

对于图标,<use><symbol>已经派上用场了...

它可以通过所有浏览器支持的原生 JavaScript Web 组件(JSWC)实现更多语义:

  • 创建 <template is="ICON-NAME">对于每个 SVG 图标,包括所有其[状态] CSS/动画样式

  • 您可以在 CodePen 中设计每个 SVG 并将其整个复制到 <template>

  • 定义 native Web 组件 <svg-icon>
    克隆 <template>shadowDOM 中。因此模板中的所有 CSS 都是范围内的!

<template id="ICON-LINUS">
  <svg viewBox="0 0 100 100" >
    <path id="P1" d="M20 40H80V20" />
    <path id="P2" d="M20 60H70V70" />
    <style>
      path { fill: none; stroke: black; stroke-width: 8px;transition: all 0.5s }
      #P1 {  stroke:blue;   stroke-dasharray: 40 500 }
      :host([active]) #P1 { stroke-dasharray: 80 500 }
      #P2 { stroke:red;     stroke-dasharray: 10 500 }
      :host([active]) #P2 { stroke-dasharray: 50 500; stroke-dashoffset: -10 }
      :host([disabled]) { pointer-events:none }
    </style>
  </svg>
</template>

<style>
  svg-icon { cursor:pointer;display:inline-block;width:140px;background:grey }
  svg-icon[active]{ background:green }
</style>

<svg-icon is="LINUS"         ></svg-icon>
<svg-icon is="LINUS" active  ></svg-icon>
<svg-icon is="LINUS"         ></svg-icon>
<svg-icon is="LINUS" disabled></svg-icon>

<script>
  customElements.define("svg-icon", class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode:"open"})
           .append(document.getElementById("ICON-"+this.getAttribute("is"))
                           .content.cloneNode(true));
      this.onclick =
            () => this.toggleAttribute("active",!this.hasAttribute("active"));
    }
  });
</script>

  • GZIP 和 Brotli 喜欢重复,因此将所有模板放在 <head>
    你已经说过你正在开发 PWA,所以确实不需要缓存任何东西;如果没有,请使用我的load-file Web Component加载外部 HTML 模板。
    小细节;我从 d 路径中删除了 ,(逗号),因为这是 HTML 中很少使用的字符。 更频繁使用的空格字符会压缩得更好。

  • 确保使用 <svg-icon>在 DOM 之后<template>定义(或添加异步加载逻辑)

  • connectedCallback中附加shadowDOM (通常在 constructor 中完成)因为我们需要 DOM 信息: is来自svg-icon属性

  • (不是 100% 确定)我认为你的 :host实验失败,因为您尝试将其应用到 <use> 创建的 用户代理(==浏览器!)shadowRoot (如<input><textarea>也创建)。它们更像是锁定的 IFrame。
    这些是与open不同的shadowRoots (或几乎不使用 closed )shadowRoots 我们凡人在用户域(不是用户代理!)中使用 Web 组件创建。

    • 我确实知道 Web 组件只能在 HTML 命名空间中工作,而不能在 SVG 命名空间中工作。
    • 另请注意,Apple 永远不会实现自定义内置元素,因此我们需要坚持 extends HTMLElement (或者不支持Safari)
  • 您可能想要扩展 onclick函数通过shadowDOM(使用 composed:true )发出 CustomEvent 冒泡up,发出 PWA 可以响应的图标名称。

  • https://yqnn.github.io/svg-path-editor/#非常适合编辑单个 d 路径,将它们转换为相对符号。

关于html - <use/> <symbol/> 与 &lt;style/> 来应用基于应用于父级 HTML 的类的 CSS 规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76077591/

相关文章:

javascript - 我使用 Raphael 的动画滞后,为什么?

javascript - 如何将文本放在拉斐尔路径的顶部?

javascript - HREF 链接在 Accordion 内部不起作用

jquery - 将鼠标悬停在无序列表项上时查找当前图标

javascript - 根据屏幕分辨率获取图像 [仅通过 HTML]

javascript - 为输入标签的值添加前缀/后缀

html - 为什么这部分 html 不可见?

css - 将内容拆分为灵活数量的列

html - 每次重复的 SVG 动画延迟

javascript - return false 不会阻止页面在 anchor 点击时重新加载