SVG 坐标转换

此主题将介绍与 getScreenCTM() 方法关联的 SVG 坐标转换。

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

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

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

图 1

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

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

图 2

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

注意

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

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

转换矩阵定义从一个坐标系到另一个坐标系的数学映射。当前转换矩阵 (CTM) 使用 3x3 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 表示 3x3 单位矩阵(谨记 M-1M = 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" 施加的 800x800 查看框,将视区坐标系的原点移动到点 (400, 400)。然后,scale(1, -1) 转换将用户坐标系的 x 轴乘以 1(无更改),将 y 轴乘以 -1,它的效果是围绕 x 轴翻转 y 轴。结果是提供标准笛卡尔坐标系(其原点出现在 800x800 视区的中点)。

该示例的其余(次要)详细信息将通过贯穿 Liquid SVG 的丰富注释进行介绍。要查看如何将此技术用在基于 SVG 的视频游戏中,请参阅“高级 SVG 动画”中的示例 6

相关主题

HTML5 图形 如何向网页添加 SVG 高级 SVG 动画 Scalable Vector Graphics (SVG)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值