我想用 SVG 构建一个图标集并将其用于按钮。这些图标根据应用的 CSS 类更改其样式。例如。想象一下,当菜单打开时,汉堡菜单图标会转换为关闭图标。
为了提高可读性,我简化了此处所有示例中的路径。请想象一下,这两条路径将展开,最终显示一个十字,并通过 CSS 过渡进行动画处理。此处代码的预期结果如下所示:
使用 .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>
这是最坏的情况。没有缓存,对于我在列表中使用的图标,我多次使用相同的代码,严重影响了结果。
所以我的问题是:我是否监督某些事情?有更好的办法吗?
最佳答案
对于图标,<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/> 与 <style/> 来应用基于应用于父级 HTML 的类的 CSS 规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76077591/