SVG 坐标转换

  • Post author:
  • Post category:其他


此主题将介绍与

getScreenCTM()

方法关联的 SVG 坐标转换。

SVG 坐标和转换这个题目非常广。你可以从 W3C SVG 规范中找到有关该题目的基本信息 – 尤其是

坐标系、转换和单位

本主题将介绍与 SVG 坐标关联的尤为致命问题 – 将屏幕坐标点(从技术上讲为初始视区坐标系)映射到与任何给定 SVG 元素关联的坐标系(从技术上讲为当前用户坐标系)。例如,考虑一个使用标准笛卡尔坐标系的 SVG 圆:

使用标准笛卡尔坐标系的 SVG 圆


图 1

在此示例中,我们关注的是如何相对于圆的坐标系确定鼠标的位置。因此,我们必须将鼠标的屏幕坐标映射到圆的笛卡尔坐标,如下图所示:

将鼠标坐标映射到笛卡尔坐标的 SVG 圆


图 2

在图 2 中,鼠标的屏幕坐标为 (843, 270)。将此点映射到圆的坐标系时,鼠标坐标将变为 (175, 175)。


注意

如果你不熟悉矩阵、矩阵乘法或矩阵求逆,请考虑查看以下一项或多项,然后再继续:

为了以算术方式描述图 2 中的转换,首先了解一些定义会很有帮助。从

W3C SVG 规范

中,我们有:


转换矩阵定义从一个坐标系到另一个坐标系的数学映射。当前转换矩阵 (CTM) 使用 3×3 CTM 矩阵,通过以下等式定义从用户坐标系到视区坐标系的映射:

Hh535760.eq1(zh-cn,VS.85).png

其中,


  • M

    是当前转换矩阵 (CTM)。
  • Hh535760.eq2(zh-cn,VS.85).png
    是表示用户坐标系中的点 (x, y) 的齐次矢量。在图 2 中,此矢量是
    Hh535760.eq3(zh-cn,VS.85).png
    ,它相当于“圆点”(175, 175)。请注意,
    Hh535760.eq2(zh-cn,VS.85).png
    中的“1”只是为了能够进行矩阵运算,实际上可以忽略。
  • Hh535760.eq4(zh-cn,VS.85).png
    是表示视区坐标系中的点 (x’, y’) 的齐次矢量。在图 2 中,此矢量是
    Hh535760.eq5(zh-cn,VS.85).png
    ,它相当于“屏幕点”(843, 270)。

幸运的是,可以在 JavaScript 中通过调用

getScreenCTM()

方法来获取 CTM。例如,如果图 2 中的圆名为

svgCircle

,则

var M = svgCircle.getScreenCTM()

将返回相应的矩阵

M

,以便使用上面的等式将圆点转换为屏幕点。也就是说,如果图 2 的当前转换矩阵

M

是:

Hh535760.eq6(zh-cn,VS.85).png

那么我们按如下所示将圆坐标 (175, 175) 映射到屏幕坐标 (843, 270):

Hh535760.eq7(zh-cn,VS.85).png

在四舍五入后,将产生屏幕坐标 (843, 270)。

上一个示例演示了如何将圆坐标转换为屏幕坐标,但是如何执行相反的操作?也就是说,给定了屏幕坐标,相应的圆坐标是什么?要回答此问题,我们必须求解
Hh535760.eq1(zh-cn,VS.85).png
中的
Hh535760.eq2(zh-cn,VS.85).png
。可按以下方式完成此操作:

Hh535760.eq8(zh-cn,VS.85).png

其中,


  • M

    -1


    是 CTM 矩阵的逆阵,对于上面的示例,它大约是
    Hh535760.eq9(zh-cn,VS.85).png
    。因为允许的所有 SVG 坐标转换都是将二维空间映射到另一个二维空间,所以

    M

    将始终可逆。

  • I

    表示 3×3 单位矩阵(谨记

    M

    -1

    M

    =

    I

    )。

因此,我们可以使用推导出来的等式
Hh535760.eq10(zh-cn,VS.85).png
,将屏幕坐标 (843, 270) 映射到其相应的圆坐标,如下所示:

Hh535760.eq11(zh-cn,VS.85).png

在四舍五入后,将产生圆坐标 (175, 175)。

理解这些数学知识后,在 JavaScript 中实现
Hh535760.eq10(zh-cn,VS.85).png
会相对简单一些:


function coordinateTransform(screenPoint, someSvgObject)
{
  var CTM = someSvgObject.getScreenCTM();
  return screenPoint.matrixTransform( CTM.inverse() );
}

给定屏幕点(

SVGPoint

类型)和一些 SVG 对象(例如圆)后,此函数会将屏幕点映射到与

someSvgObject

关联的坐标系。如果

someSvgObject

表示图 2 的圆,则:


  • var CTM = someSvgObject.getScreenCTM()

    将获取与当前屏幕和圆大小关联的 CTM。

  • CTM.inverse()

    将对 CTM 矩阵求逆,从而向我们提供上面等式中的

    M

    -1



  • screenPoint.matrixTransform( CTM.inverse() )



    M

    -1


    和给定的屏幕坐标
    Hh535760.eq4(zh-cn,VS.85).png
    (即

    screenPoint

    )相乘以产生所需的圆坐标
    Hh535760.eq2(zh-cn,VS.85).png

要查看与图 2 关联的完整示例,请单击

Liquid SVG

。要查看与此示例关联的源代码,请使用浏览器的查看源文件功能。例如,在 Windows Internet Explorer 中,右键单击网页并选择“查看源文件”


。请确保阅读本文档其余部分时有源代码可用。

请注意,随着你更改浏览器窗口的大小,圆大小将相应调整。此

liquid layout

是以下基于百分比的值的结果:


html {
  padding: 0;
  margin: 0;
  height: 100%; /* Required for liquidity. */  
}

body {
  padding: 0;
  margin: 0;
  height: 100%; /* Required for liquidity. */
}


<!-- width="100%" and height="98%" required for liquidity. -->
<svg id="svgElement" width="100%" height="98%" viewbox="0 0 800 800">

移动到代码的另一个方面(

svgCircle

元素及其同级元素)时,将通过以下两个

<g>

元素转换提供标准笛卡尔坐标系:


<g transform="translate(400, 400) scale(1, -1)">

也就是说,

translate(400, 400)

会相对于在

svg

元素上通过

viewbox="0 0 800 800"

施加的 800×800 查看框,将视区坐标系的原点移动到点 (400, 400)。然后,

scale(1, -1)

转换将用户坐标系的 x 轴乘以 1(无更改),将 y 轴乘以 -1,它的效果是围绕 x 轴翻转 y 轴。结果是提供标准笛卡尔坐标系(其原点出现在 800×800 视区的中点)。

该示例的其余(次要)详细信息将通过贯穿

Liquid SVG

的丰富注释进行介绍。要查看如何将此技术用在基于 SVG 的视频游戏中,请参阅“高级 SVG 动画”中的

示例 6



相关主题


HTML5 图形


如何向网页添加 SVG


高级 SVG 动画



Scalable Vector Graphics (SVG)