使用 icomoon 从 svg Figma 图标生成字体时自动填充颜色

nar*_*yam 7 fonts svg icomoon

我想使用icomoon 应用程序将 svg 转换为HTML/CSS 中的图标字体元素

<svg width="325" height="350" viewBox="0 0 325 350" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M205.43 59.38C205.43 26.5853 232.015 0 264.81 0C297.605 0 324.19 26.5853 324.19 59.38C324.19 92.1747 297.605 118.76 264.81 118.76C246.525 118.76 230.17 110.495 219.277 97.4975L116.536 157.274C117.985 162.41 118.76 167.83 118.76 173.43C118.76 178.348 118.162 183.127 117.035 187.697L222.002 248.758C232.806 237.522 247.991 230.53 264.81 230.53C297.605 230.53 324.19 257.115 324.19 289.91C324.19 322.705 297.605 349.29 264.81 349.29C232.015 349.29 205.43 322.705 205.43 289.91C205.43 280.791 207.486 272.152 211.159 264.431L109.497 205.292C98.9566 221.836 80.4498 232.81 59.38 232.81C26.5853 232.81 0 206.225 0 173.43C0 140.635 26.5853 114.05 59.38 114.05C79.7735 114.05 97.7659 124.331 108.457 139.992L209.556 81.1716C206.893 74.4248 205.43 67.0733 205.43 59.38ZM264.81 19C242.509 19 224.43 37.0787 224.43 59.38C224.43 81.6813 242.509 99.76 264.81 99.76C287.111 99.76 305.19 81.6813 305.19 59.38C305.19 37.0787 287.111 19 264.81 19ZM59.38 133.05C37.0787 133.05 19 151.129 19 173.43C19 195.731 37.0787 213.81 59.38 213.81C81.6813 213.81 99.76 195.731 99.76 173.43C99.76 151.129 81.6813 133.05 59.38 133.05ZM224.43 289.91C224.43 267.609 242.509 249.53 264.81 249.53C287.111 249.53 305.19 267.609 305.19 289.91C305.19 312.211 287.111 330.29 264.81 330.29C242.509 330.29 224.43 312.211 224.43 289.91Z" fill="#0D0F13"/>
</svg>
Run Code Online (Sandbox Code Playgroud)

但每次我生成图标字体文件时,三个圆圈之一在 icomoon 预览中以及在 HTML 中使用时都会被填充为黑色:

<span class="icon icon-share">
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

her*_*zel 10

此渲染问题是由不理想的路径方向引起的。

\n

路径方向

\n

您的图标是一个复合路径 \xe2\x80\x93 它包含多个子路径(切出的“孔”)。

\n

通常,您可以通过切换路径方向来定义哪些形状应被解释为实体或切口:
\n例如 如果外部形状的路径命令具有顺时针路径方向 \xe2\x80\x93,则内部形状的路径命令应使用逆时针方向。

\n

无论您使用顺时针还是逆时针或相反,都没有关系。内部(切出)路径只需使用相反的方向。
\n本文中还解释了:填充规则的缠绕顺序

\n

Svg:填充规则=“evenodd”

\n

原始 svg 文件渲染得很好,因为它已应用于fill-rule="evenodd"路径元素。

\n

显然,icomoon 字体生成器希望您的图标具有上述交替方向。
\n换句话说,将 svg 转换为字体文件时不能使用填充规则。

\n

修复路径方向

\n

这是一个用于修复内部路径的帮助程序脚本。

\n

\r\n
\r\n
const svg = document.querySelector(\'svg\');\nconst path = svg.querySelector(\'path\');\n\nfunction fixPath(path) {\n  // get pathData array\n  let pathData = path.getPathData({\n    normalize: true\n  });\n  // split sub paths\n  let pathDataSubArr = splitSubpaths(pathData);\n  let fixedPathData = fixInnerPathDirections(path, pathDataSubArr);\n  path.setPathData(fixedPathData);\n}\n\n\n/**\n * helpers\n */\nfunction fixInnerPathDirections(path, pathDataSubArr) {\n  let svg = path.closest(\'svg\');\n  let fixedPathData = [];\n  let subPathEls = [];\n  let bbO = path.getBBox();\n  let [xO, yO, wO, hO, rO, bO] = [bbO.x, bbO.y, bbO.width, bbO.height, (bbO.x + bbO.width), (bbO.y + bbO\n    .height)];\n  let outerPathData = pathDataSubArr[0];\n  let outerPathTmp = document.createElementNS(\'http://www.w3.org/2000/svg\', \'path\');\n  outerPathTmp.setPathData(outerPathData);\n  outerPathTmp.classList.add(\'outer\');\n  let outerClockwise = isClockwise(outerPathTmp)\n\n  pathDataSubArr.forEach(function(pathDataSub, i) {\n    // create temporary subpath elements for checking positions\n    let subPath = document.createElementNS(\'http://www.w3.org/2000/svg\', \'path\');\n    subPath.setPathData(pathDataSub);\n    subPath.setAttribute(\'stroke\', \'red\');\n    subPath.setAttribute(\'stroke-width\', \'1\');\n    subPath.setAttribute(\'fill\', \'none\');\n    svg.appendChild(subPath);\n    subPathEls.push(subPath);\n    let bb = subPath.getBBox();\n    let [x, y, w, h, r, b] = [bb.x, bb.y, bb.width, bb.height, (bb.x + bb.width), (bb.y + bb\n      .height)];\n    // remove temporary subpaths\n    subPath.remove();\n\n    if (i > 0) {\n      // is subpath within outer path\n      if (x > xO && y > yO && r < rO && b < bO) {\n        let isClockwiseInner = isClockwise(subPath);\n        // if subpath has same direction as outer path: reverse direction\n        if (isClockwiseInner == outerClockwise) {\n          pathDataSub = reversePathData(pathDataSub);\n        }\n      }\n    }\n    fixedPathData = fixedPathData.concat(pathDataSub);\n  })\n  return fixedPathData;\n}\n\n\nfunction reversePathData(pathData) {\n  let M = pathData[0];\n  let newPathData = [M];\n  // split subpaths\n  let subPathDataArr = splitSubpaths(pathData);\n  subPathDataArr.forEach(function(subPathData, s) {\n    let subPathDataL = subPathData.length;\n    let closed = subPathData[subPathDataL - 1][\'type\'] == \'Z\' ? true : false;\n    let subM = subPathData[0][\'values\'];\n\n    // insert Lineto if last path segment has created by z\n    let lastCom = closed ? subPathData[subPathDataL - 2] : subPathData[subPathDataL - 1];\n    let lastComL = lastCom[\'values\'].length;\n    let lastXY = [lastCom[\'values\'][lastComL - 2], lastCom[\'values\'][lastComL - 1]];\n    let diff = Math.abs(subM[0] - lastXY[0]);\n    if (diff > 1 && closed) {\n      subPathData.pop();\n      subPathData.push({\n        \'type\': \'L\',\n        \'values\': [subM[0], subM[1]]\n      });\n      subPathData.push({\n        \'type\': \'Z\',\n        \'values\': []\n      });\n    }\n    subPathData.forEach(function(com, i) {\n      // reverse index\n      let subpathDataL = subPathData.length;\n      let indexR = subpathDataL - 1 - i;\n      let comR = subPathData[indexR];\n      let comF = subPathData[i];\n      let [typeR, valuesR] = [comR[\'type\'], comR[\'values\']];\n      let [typeF, valuesF] = [comF[\'type\'], comF[\'values\']];\n      if (typeF == \'M\' && s > 0) {\n        newPathData.push(comF);\n      } else if (typeR != \'M\' && typeR != \'Z\') {\n        indexR--;\n        let prevCom = i > 0 ? subPathData[indexR] : subPathData[subpathDataL - 1 - i];\n        let prevVals = prevCom ? (prevCom[\'values\'] ? prevCom[\'values\'] : [0, 0]) : [];\n        prevVals = prevCom[\'values\'];\n        let prevValsL = prevVals.length;\n        let newCoords = [];\n\n        if (typeR == \'C\') {\n          newCoords = [\n            valuesR[2], valuesR[3],\n            valuesR[0], valuesR[1],\n            prevVals[prevValsL - 2], prevVals[prevValsL - 1]\n          ];\n          if (!closed) {\n            let nextVals = i < subpathDataL - 1 ? subPathData[i + 1][\'values\'] : lastXY;\n            let lastCX = (i < subpathDataL - 2) ? nextVals[prevValsL - 2] : subM[0];\n            let lastCY = (i < subpathDataL - 2) ? nextVals[prevValsL - 1] : subM[1];\n            newCoords[4] = lastCX;\n            newCoords[5] = lastCY;\n          }\n        } else {\n          newCoords = [prevVals[prevValsL - 2], prevVals[prevValsL - 1]];\n        }\n        newPathData.push({\n          \'type\': typeR,\n          \'values\': newCoords\n        });\n      }\n    })\n    //use last coordinates as M values if path isn\'t closed\n    if (!closed) {\n      newPathData[0][\'values\'] = lastXY;\n    }\n    if (closed) {\n      newPathData.push({\n        \'type\': \'Z\',\n        \'values\': []\n      });\n    }\n  });\n  return newPathData;\n}\n\n/**\n * check path direction of polygon\n * based on answer:\n * @mpen: https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1180256#answer-46613578\n */\nfunction isClockwise(path, divisions = 50) {\n  var length = path.getTotalLength();\n  let p1 = path.getPointAtLength(0);\n  let A = [p1.x, p1.y];\n  let p2 = path.getPointAtLength(length * 0.25);\n  let B = [p2.x, p2.y];\n  let p3 = path.getPointAtLength(length * 0.75);\n  let C = [p3.x, p3.y];\n  let poly = [A, B, C];\n  let end = poly.length - 1;\n  let sum = poly[end][0] * poly[0][1] - poly[0][0] * poly[end][1];\n  for (let i = 0; i < end; ++i) {\n    const n = i + 1;\n    sum += poly[i][0] * poly[n][1] - poly[n][0] * poly[i][1];\n  }\n  let cw = sum > 0 ? true : false;\n  return cw;\n}\n\n\nfunction splitSubpaths(pathData) {\n  let pathDataL = pathData.length;\n  let subPathArr = [];\n  let subPathMindex = [];\n  pathData.forEach(function(com, i) {\n    let [type, values] = [com[\'type\'], com[\'values\']];\n    if (type == \'M\') {\n      subPathMindex.push(i);\n    }\n  });\n  //split segments after M command\n  subPathMindex.forEach(function(index, i) {\n    let n = subPathMindex[i + 1];\n    let thisSeg = pathData.slice(index, n);\n    subPathArr.push(thisSeg)\n  })\n  return subPathArr;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill@1.0.3/path-data-polyfill.min.js"></script>\n\n\n<p><button type="button" onclick="fixPath(path)">Fix path directions</button></p>\n<svg width="325" height="350" viewBox="0 0 325 350" fill="none" xmlns="http://www.w3.org/2000/svg">\n        <path d="\n        M205.43 59.38C205.43 26.5853 232.015 0 264.81 0C297.605 0 324.19 26.5853 324.19 59.38C324.19 92.1747 297.605 118.76 264.81 118.76C246.525 118.76 230.17 110.495 219.277 97.4975L116.536 157.274C117.985 162.41 118.76 167.83 118.76 173.43C118.76 178.348 118.162 183.127 117.035 187.697L222.002 248.758C232.806 237.522 247.991 230.53 264.81 230.53C297.605 230.53 324.19 257.115 324.19 289.91C324.19 322.705 297.605 349.29 264.81 349.29C232.015 349.29 205.43 322.705 205.43 289.91C205.43 280.791 207.486 272.152 211.159 264.431L109.497 205.292C98.9566 221.836 80.4498 232.81 59.38 232.81C26.5853 232.81 0 206.225 0 173.43C0 140.635 26.5853 114.05 59.38 114.05C79.7735 114.05 97.7659 124.331 108.457 139.992L209.556 81.1716C206.893 74.4248 205.43 67.0733 205.43 59.38Z\n        \n        M264.81 19C242.509 19 224.43 37.0787 224.43 59.38C224.43 81.6813 242.509 99.76 264.81 99.76C287.111 99.76 305.19 81.6813 305.19 59.38C305.19 37.0787 287.111 19 264.81 19Z\n        \n        M59.38 133.05C37.0787 133.05 19 151.129 19 173.43C19 195.731 37.0787 213.81 59.38 213.81C81.6813 213.81 99.76 195.731 99.76 173.43C99.76 151.129 81.6813 133.05 59.38 133.05Z\n        \n        \n        M224.43 289.91C224.43 267.609 242.509 249.53 264.81 249.53C287.111 249.53 305.19 267.609 305.19 289.91C305.19 312.211 287.111 330.29 264.81 330.29C242.509 330.29 224.43 312.211 224.43 289.91Z\n        \n        " fill="#0D0F13" />\n    </svg>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

(检查开发工具中的元素以获取新路径)

\n

怎么运行的

\n
    \n
  1. 解析路径命令(使用Jarek Foksa 的 path-data-polyfill
    \n1.2 以标准化方式检索路径数据 ( path.getPathData({normalize: true}): \n所有命令将仅使用绝对、、、命令
    转换为缩减集。MLCZ
  2. \n
  3. 将路径数据拆分为子路径(每个子路径以新M命令开头)
  4. \n
  5. 检查子路径是否与外部形状相交
  6. \n
  7. 检查外部子路径和内部子路径之间的方向(使用isClockwise(path)助手)
  8. \n
  9. 如果需要,反转子路径方向 ( reversePathData(pathdata))
  10. \n
  11. 连接子路径并覆盖原始d属性
  12. \n
\n

Codepen 示例

\n

您还可以使用此codepen 助手:修复 svg 复合路径方向
\n此脚本将保留原始命令类型 \xe2\x80\x93 A,因此Q命令不会转换为立方 b\xc3\xa9ziers。

\n

HTML/CSS 示例

\n

\r\n
\r\n
@font-face {\n    font-family: \'icomoon\';\n    src: url(\'data:font/woff2;charset=utf-8;base64,d09GMgABAAAAAAKsAA0AAAAABpgAAAJWAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCXhEICoIwgicLDgABNgIkAxgEIAWDGwdFG5gFyJ4FdpMzjFBuZhHDYhE2Lyuvefjx8LVWvt89OwuoLkDsUKWigS2BIwnAKmwRyqeiD1z8VXT0WrS2WSR7w9M/YpFmHip+uESqS2jQkv/fzbMtejpWgbRBCusLh5mNJZxQLwq8+J+4Gbcs0ayoyySyNm4KAw7HxADTzsMwkLHPpyWQZQUKD0Zv7Xy3GOQpg6RIkmGRRZTNaBEChl5xBP7+oo23DrQwAJIQKkKBhJyGYq1CepJu3KLi+GdF1DGd6Klcj0blY/2GbQYFAcSpCPnkiQjsIBSYSGQ1aKhJkjB1VP8HxD9n+u8YsSw0AIRMlEQGIiEBAIICrg7RJwElaKGKPeAyoAAEQihyzqva7VbQSyfP/JuzIacHG9Ph+enDBacfK4dHbovJUxU8A6u/WJQ7Xqm9+nF6qDkbiclwo2VkBgb1ANjxPsxY2H3AZ4bRsplS0Zw5z18lt1pWnMqpZVxycbNKnuXD1oYHbk6mZGvfmfmdMLq4AgJQAH3+SAIPQNz+esn3KSVZ/Z/wwqyM0of8OfegJoEw3uUoQP4sfyGUBSC2AvhN/opAaTcRSAAA6HgjQKjqIZBUzUUgq1qPQKFqBwIVTacQKFXdF6BhvJcsirrQIQIJGQtkZClQIBuxigFprNRxvmfDSncH5TTVNTU14OCqn9JsAJswRX01GR14mZYGrQlL5G2Yjq6yW8/JxgFnVkarTN86GzQ19OgVMQ1MR0YPk6eXNaYXISRh2uCeHNrBzTrX/NNhDoTEti+JJEWWoyj3Hxm3Sk58/FjZQZy3Ai5jAAAA\') format(\'woff2\');\n    font-weight: normal;\n    font-style: normal;\n    font-display: swap;\n}\n\n.icon{\n  font-family: \'icomoon\';\n  font-size:5vw\n}\n\n.icon-share:before {\n  content: "\\e900";\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<span class="icon icon-share">
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

替代方案:paper.js:

\n

如果您的应用程序已经使用 paper.js,您还可以使用其reorient()方法来修复路径方向。请参阅相关帖子“Paper.js 重新定向:SVG 子路径丢失”

\n