【问题标题】:How can I use an SVG element’s transformation matrix to calculate destination coordinates?如何使用 SVG 元素的转换矩阵来计算目标坐标?
【发布时间】:2021-04-03 14:08:49
【问题描述】:

我怀疑有什么方法可以使用元素的变换矩阵来计算变换后的坐标,但我不知道该怎么做。

一个示例图最好地说明了这一点:



恐怕我没有走数学路线进入编程。我可以理解转换矩阵在做什么的表面细节,但我的理解有点像一次能读一个音符的音乐,而且速度很慢;我并没有真正理解更高层次的曲调,所以我并不真正理解完整乐句的声音——更不用说旋律了。

同样,我不明白转换矩阵是如何工作的。我曾尝试搜索 grok 转换矩阵的解释,但我发现的所有内容都充满了我不明白的 more 数学术语。我只知道它们的工作方式有点像函数,而且它们是非常灵活的工具,但仅此而已。

SVGMatrix 可用的所有方法中(据说已弃用DOMMatrix,但Firefox Dev. ed.仍在使用SVGMatrix),我不知道.inverse().multiply() 是什么我想要,但不知道如何从该矩阵中哄出一组简单的 xy 坐标。

注意:

  • 我对在这里将屏幕坐标转换为 SVG 坐标不感兴趣。
  • 我只关心 SVG(用户空间)坐标。

【问题讨论】:

    标签: svg dom geometry transformation transformation-matrix


    【解决方案1】:

    您可以使用简单的三角变换:

    const rotatePoint = (point, center, rotateAngle) => {
      const dx = point.x - center.x;
      const dy = point.y - center.y;
      const distance = Math.hypot(dx, dy);
      const currentAngle = Math.atan(dy / dx);
      const nextAngle = currentAngle - rotateAngle;
      const nextDX = distance * Math.cos(nextAngle);
      const nextDY = distance * Math.sin(nextAngle);
      return {x: center.x + nextDX, y: center.y + nextDY};
    };
    

    sn-p 显示蓝色点围绕红色点旋转(逆时针 30 / 90 / 123 度)

    const rotatePoint = (point, center, angle) => {
        const dx = point.x - center.x;
      const dy = point.y - center.y;
      const distance = Math.hypot(dx, dy);
      const current = Math.atan(dy / dx);
      const next = current - angle;
      const nextDX = distance * Math.cos(next);
      const nextDY = distance * Math.sin(next);
      return {x: center.x + nextDX, y: center.y + nextDY};
    };
    
    const center = {x: 150, y: 150};
    const start = {x: 200, y: 30};
    const svg = d3.select('svg');
    
    svg.append('circle')
        .attr('cx', center.x)
      .attr('cy', center.y)
      .attr('r', 5)
      .style('fill', 'red');
    svg.append('circle')
        .attr('cx', start.x)
      .attr('cy', start.y)
      .attr('r', 5)
      .style('fill', 'blue');  
    
    // Rotate 30 deg
    const p30 = rotatePoint(start, center, Math.PI * 30 / 180); 
    svg.append('circle')
        .attr('cx', p30.x)
      .attr('cy', p30.y)
      .attr('r', 5)
      .style('fill', 'green');  
    // Rotate 90 deg
    const p90 = rotatePoint(start, center, Math.PI * 90 / 180); 
    svg.append('circle')
        .attr('cx', p90.x)
      .attr('cy', p90.y)
      .attr('r', 5)
      .style('fill', 'orange');
    // Rotate 123 deg
    const p123 = rotatePoint(start, center, Math.PI * 123 / 180); 
    svg.append('circle')
        .attr('cx', p123.x)
      .attr('cy', p123.y)
      .attr('r', 5)
      .style('fill', 'yellow');  
      
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg width="250" height="200"></svg>

    【讨论】:

    • 这很好。谢谢!我不能将此标记为答案的唯一原因是:我认为我可以“毕达哥拉斯”解决这个问题,但我更喜欢利用现有 SVGMatrixDOMMatrix API 的解决方案。尽管如此,我还是会尝试将其作为临时解决方法。谢谢你。 ??
    • @Zearin 也许你会发现这很有用:stackoverflow.com/questions/14660796/…,但我不确定使用矩阵是否比函数计算更简单,尤其是在涉及性能问题时。
    • 您的方法绝对是最简单的解决方案!自从我上面的原始评论以来,我已经尝试过了,而且效果很好。我要求与矩阵相关的解决方案的唯一原因是,我可以(理论上)在 any 转换的 SVG 元素上使用一些东西,并在需要时计算目标点到。
    • 再次感谢您的帮助。我找不到我想要的解决方案,所以我坚持使用你的解决方案。我会相应地标记我的问题已回答。
    【解决方案2】:

    一旦你有了一个 SVG Circle,

    您可以使用标准 getPointAtLengthgetTotalLength 函数计算圆上的任何 SVG 点。

    请注意圆圈开始绘制的位置(绿点)

    pathLength 转换为度数:

    <style>
      svg{ height:300px }
      text {
        font-size: 3px;
        fill: white;
        text-anchor: middle;
        dominant-baseline: middle;
      }
    </style>
    <svg id="SVG" viewBox="0 0 100 100">
      <circle cx="50%" cy="50%" r="50%" fill="pink"></circle>
      <circle id="CIRCLE" cx="50%" cy="50%" r="40%" 
              fill="none" stroke="black" stroke-dasharray="2"></circle>
      <circle cx="90%" cy="50%" r="5%" fill="green"></circle>
    </svg>
    <script>
      let circle_length = CIRCLE.getTotalLength();
      for (let degree = 0; degree < 360; degree += 45) {
        let { x , y } = CIRCLE.getPointAtLength(  degree * ( circle_length / 360 )  );
        let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
        group.innerHTML = `<circle cx="${x}" cy="${y}" r="3%" fill="blue"/>` +
                          `<text x="${x}" y="${y}">${degree}</text>`;
        SVG.append(group)
      }
    </script>

    【讨论】:

      猜你喜欢
      • 2018-06-28
      • 1970-01-01
      • 2016-11-04
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 2021-12-04
      • 1970-01-01
      • 2022-01-25
      相关资源
      最近更新 更多