My issues

Currently, I have positioned the pathIcon along the 'd' path of the SVG path. However, if I change the size of my SVG, the 'd' attribute must also change.
How can I define a new 'd' that resembles the old SVG path with the new size? I don't know how to define a new 'd' in JavaScript.

document.addEventListener("DOMContentLoaded", function() {
  let svg        = document.querySelector(".svg-path");
  let mPath      = document.getElementById("Path_440");
  let strokePath = document.getElementById("theFill");
  let pathIcon   = document.getElementById("pathIcon");

  const svgRect = svg.getBBox();
  const width   = svgRect.width;
  const height  = svgRect.height;
  svg.setAttribute("viewBox", `0 0 ${width} ${height}`)

  function defineNewOffsetPath()
    {
    /**my issues : defineNewOffsetPath 
      mPath.setAttribute("d", newPath) 
      how to define a path look like old path with new size
    **/
    pathIcon.style.offsetPath = `path('${mPath.getAttribute("d")}')`;
    }

   defineNewOffsetPath();
})
.svg-path {
  overflow: visible;
  width: 100%;
  height: auto;
}

#pathIcon {
  position: absolute;
  inset: 0;
  width: 100px;
  height: 200px;
  background-size: 25px;
  offset-rotate: 0rad;
  transition: 0.2s;
  offset-distance: 0%;
}

#Path_440 {
  stroke-width: 2;
  stroke: #001d36;
}
<div style="height: 175px"></div>
<div id="scrollDiv" style="position: relative">
  <svg class="svg-path" viewBox="0 0 0 0" fill="none">
    <defs>
      <path id="Path_440"
        d="M1293 2S1277 76.47 1197 93.5C1105.55 112.97 888.33 91.07 772.5 100.5 545.5 100.5 302.61 125.94 279 295.5 268 374.5 265.11 419.83 268 503S269.9 645.65 305 741C346.77 854.46 770 838.5 1094.5 832 1366 842.5 1676.02 792 1766 1011 1803.18 1101.5 1766 1457.5 1766 1493.5 1766 1561 1696 1613.5 1618 1627.5 1465 1627.5 1188.11 1632.5 1003.5 1632.5 732.5 1632.5 369.53 1605.69 312 1717.5 271.61 1796 276 1920 276 1982 277.12 2074.28 272.55 2144.17 312 2258 339.86 2338.39 721.15 2324.5 981 2324.5 1297 2324.5 1677.34 2307.5 1739.5 2403.5 1793.57 2487 1772.73 2616.18 1772.73 2765 1772.73 2893 1770.73 2997.5 1652 3032 1612.67 3043.43 1237 3032 893 3032 405.5 3032 2 3030 2 3030"
        stroke="#020878" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-dasharray="20 20" />
    </defs>
    <use href="#Path_440" stroke-width="10" stroke-dasharray="20 10"></use>
    <use id="theFill" href="#Path_440" stroke-dasharray="1991.82, 9259.88" stroke-width="10" stroke="#4cacff"></use>
  </svg>
  <svg id="pathIcon" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <rect width="100" height="200" fill="url(#pattern0)" />
    <defs>
      <pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
        <use xlink:href="#image0_873_8619" transform="scale(0.00353357 0.00176678)" />
      </pattern>
      <image id="image0_873_8619" width="283" height="566" xlink:href="data:image/png;base64,iVBORw0KGgoAAAA" />
    </defs>
  </svg>
</div>

推荐答案

如果你只需要按比例zoom 你的路径数据—这是非常直接的:

  • 解析字符串化的路径数据(来自d属性)
  • 按比例因子zoom 所有与点相关的命令值(您需要排除a个arc命令值,如x—axis—rotation、largeArc和sweep flag)
  • 将路径数据字符串化为d属性

let svg = document.querySelector(".svg-path");
let mPath = document.getElementById("Path_440");
let strokePath = document.getElementById("theFill");
let pathIcon = document.getElementById("pathIcon");

document.addEventListener("DOMContentLoaded", function () {
  // auto adjust viewBox
  let svgRect = svg.getBBox();
  let width = svgRect.width;
  let height = svgRect.height;
  svg.setAttribute("viewBox", `0 0 ${width} ${height}`);

  // update offset path
  defineNewOffsetPath();
});


function defineNewOffsetPath() {
  
  // retrieve the current scale from SVG transformation matrix
  let matrix = svg.getCTM();
  let scale = matrix.a;
  
  // parse path data
  let d = mPath.getAttribute("d");
  let pathData = parsePathData(d);

  //scale pathdata
  pathData = scalePathData(pathData, scale);

  // apply scaled pathdata as stringified d attribute value
  d = pathDataToD(pathData);
  pathIcon.style.offsetPath = `path('${d}')`;
}

// recalculate offset path on resize
window.addEventListener("resize", (e) => {
  defineNewOffsetPath();
});

// just for illustration
resizeObserver()
function resizeObserver() {
  defineNewOffsetPath();
}
new ResizeObserver(resizeObserver).observe(scrollDiv)




/**
* sclae path data proportional
*/
function scalePathData(pathData, scale = 1) {
  let pathDataScaled = [];
  pathData.forEach((com, i) => {
    let { type, values } = com;
    let comT = {
      type: type,
      values: []
    };

    switch (type.toLowerCase()) {
      // lineto shorthands
      case "h":
        comT.values = [values[0] * scale]; // horizontal - x-only
        break;
      case "v":
        comT.values = [values[0] * scale]; // vertical - x-only
        break;

      // arcto
      case "a":
        comT.values = [
          values[0] * scale, // rx: scale
          values[1] * scale, // ry: scale
          values[2], // x-axis-rotation: keep it 
          values[3], // largeArc: dito
          values[4], // sweep: dito
          values[5] * scale, // final x: scale
          values[6] * scale // final y: scale
        ];
        break;

      /**
      * Other point based commands: L, C, S, Q, T
      * scale all values
      */
      default:
        if (values.length) {
          comT.values = values.map((val, i) => {
            return val * scale;
          });
        }
    }
    pathDataScaled.push(comT);
  });
  return pathDataScaled;
}

/**
* parse stringified path data used in d attribute
* to an array of computable command data
*/
function parsePathData(d) {
  d = d
    // remove new lines, tabs an comma with whitespace
    .replace(/[\n\r\t|,]/g, " ")
    // pre trim left and right whitespace
    .trim()
    // add space before minus sign
    .replace(/(\d)-/g, "$1 -")
    // decompose multiple adjacent decimal delimiters like 0.5.5.5 => 0.5 0.5 0.5
    .replace(/(\.)(?=(\d+\.\d+)+)(\d+)/g, "$1$3 ");

  let pathData = [];
  let cmdRegEx = /([mlcqazvhst])([^mlcqazvhst]*)/gi;
  let commands = d.match(cmdRegEx);

  // valid command value lengths
  let comLengths = {
    m: 2,
    a: 7,
    c: 6,
    h: 1,
    l: 2,
    q: 4,
    s: 4,
    t: 2,
    v: 1,
    z: 0
  };
  commands.forEach((com) => {
    let type = com.substring(0, 1);
    let typeRel = type.toLowerCase();
    let isRel = type === typeRel;
    let chunkSize = comLengths[typeRel];

    // split values to array
    let values = com.substring(1, com.length).trim().split(" ").filter(Boolean);

    /**
     * A - Arc commands
     * large arc and sweep flags
     * are boolean and can be concatenated like
     * 11 or 01
     * or be concatenated with the final on path points like
     * 1110 10 => 1 1 10 10
     */
    if (typeRel === "a" && values.length != comLengths.a) {
      let n = 0,
        arcValues = [];
      for (let i = 0; i < values.length; i++) {
        let value = values[i];

        // reset counter
        if (n >= chunkSize) {
          n = 0;
        }
        // if 3. or 4. parameter longer than 1
        if ((n === 3 || n === 4) && value.length > 1) {
          let largeArc = n === 3 ? value.substring(0, 1) : "";
          let sweep = n === 3 ? value.substring(1, 2) : value.substring(0, 1);
          let finalX = n === 3 ? value.substring(2) : value.substring(1);
          let comN = [largeArc, sweep, finalX].filter(Boolean);
          arcValues.push(comN);
          n += comN.length;
        } else {
          // regular
          arcValues.push(value);
          n++;
        }
      }
      values = arcValues.flat().filter(Boolean);
    }

    // string  to number
    values = values.map(Number);

    // if string contains repeated shorthand commands - split them
    let hasMultiple = values.length > chunkSize;
    let chunk = hasMultiple ? values.slice(0, chunkSize) : values;
    let comChunks = [
      {
        type: type,
        values: chunk
      }
    ];

    // has implicit or repeated commands – split into chunks
    if (hasMultiple) {
      let typeImplicit = typeRel === "m" ? (isRel ? "l" : "L") : type;
      for (let i = chunkSize; i < values.length; i += chunkSize) {
        let chunk = values.slice(i, i + chunkSize);
        comChunks.push({
          type: typeImplicit,
          values: chunk
        });
      }
    }
    comChunks.forEach((com) => {
      pathData.push(com);
    });
  });

  /**
   * first M is always absolute/uppercase -
   * unless it adds relative linetos
   * (facilitates d concatenating)
   */
  pathData[0].type = "M";
  return pathData;
}

/**
 * serialize pathData array to
 * d attribute string
 */
function pathDataToD(pathData, decimals = 3) {
  let d = ``;
  pathData.forEach((com) => {
    d += `${com.type}${com.values
      .map((val) => {
        return +val.toFixed(decimals);
      })
      .join(" ")}`;
  });
  return d;
}
html{
  margin:0;
  padding:0;
}


.svg-path {
  overflow: visible;
  width: 100%;
}

#pathIcon {
  position: absolute;
  inset: 0;
  width: 5vw;
  height: 5vw;
  offset-rotate: 0deg;
  offset-distance: 10%;
}


#scrollDiv{
resize:both;
overflow:auto;
border: 1px solid #ccc;
margin:10px;
}
<div id="scrollDiv" style="position: relative">
  <svg class="svg-path" viewBox="0 0 0 0" fill="none">
    <defs>
      <path id="Path_440"
        d="M1293 2 s-16 74.47-96 91.5c-91.45 19.47-308.67-2.43-424.5 7-227 0-469.89 25.44-493.5 195-11 79-13.89 124.33-11 207.5s1.9 142.65 37 238c41.77 113.46 465 97.5 789.5 91 271.5 10.5 581.52-40 671.5 179 37.18 90.5 0 446.5 0 482.5 0 67.5-70 120-148 134-153 0-429.89 5-614.5 5-271 0-633.97-26.81-691.5 85-40.39 78.5-36 202.5-36 264.5 1.12 92.28-3.45 162.17 36 276 27.86 80.39 409.15 66.5 669 66.5 316 0 696.34-17 758.5 79 54.07 83.5 33.23 212.68 33.23 361.5 0 128-2 232.5-120.73 267-39.33 11.43-415 0-759 0-487.5 0-891-2-891-2"
         />
    </defs>
    <use class="stroke" href="#Path_440" stroke="#ccc" stroke-width="10" stroke-dasharray="20 10"></use>
    <use class="stroke" id="theFill" href="#Path_440" stroke-dasharray="925.988 9259.88" stroke-width="10" stroke="#4cacff"></use>
  </svg>
  <svg id="pathIcon" fill="none">
    <rect width="100" height="100" fill="red" fill-opacity="0.5"/>
  </svg>
</div>

Javascript相关问答推荐

zoom svg以适应圆

调用removeEvents不起作用

在我的html表单中的用户输入没有被传送到我的google表单中

显示图—如何在图例项上添加删除线效果?

有条件重定向到移动子域

回溯替代方式

如何利用CSS中的隐藏元素实现平滑扩展和防止网格行间隙

Next.js中的服务器端组件列表筛选

如何找到带有特定文本和测试ID的div?

Next.js无法从外部本地主机获取图像

Reaction即使在重新呈现后也会在方法内部保留局部值

在没有任何悬停或其他触发的情况下连续交换图像

在范围数组中查找公共(包含)范围

无法向甜甜圈图表上的ChartJSImage添加可见标签

不允许在对象文本中注释掉的属性

MAT-TREE更多文本边框对齐问题

通过ng-绑定-html使用插入的HTML中的函数

如何为两条动态路由创建一个页面?

使用Java脚本筛选数组中最接近值最小的所有项

ReactJS Sweep Line:优化SciChartJS性能,重用wasmContext进行多图表渲染