如何从DOM翻译到SVG坐标,然后再翻译回来

158 阅读5分钟

使用SVG是相对直接的--直到你想混合DOM和矢量交互。

在这一系列的SVG文章中,我们已经看了什么是SVG为什么你应该考虑它们,以及你如何将它们嵌入你的网页中。我们还看了基本的绘图元素更复杂的路径是如何工作的。

SVG有自己的坐标系统,在viewBox 属性中定义。比如说。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">

这就设置了一个宽度为800单位,高度为600单位,从0,0 。这些单位是用于绘图的任意测量,也可以使用单位的零头。如果你把这个SVG定位在一个800 by600 像素的区域内,每个SVG单位应该直接映射到一个屏幕像素。

然而,矢量图像可以被缩放到任何尺寸--尤其是在响应式设计中。你的SVG可以被缩小到400 by300 ,甚至在10 by1000 的空间里被拉伸到无法辨认。如果你想根据光标的位置来放置更多的元素,那么向这个SVG添加更多的元素就会变得更加困难。

注意:请参阅Sara Soueidan的viewport、viewBox和reserveAspectRatio一文,以了解更多关于SVG坐标的信息。

避免坐标翻译

你也许可以完全避免坐标系之间的转换。

嵌入HTML页面的SVG(而不是图像或CSS背景)成为DOM的一部分,可以用与其他元素类似的方式进行操作。例如,以一个带有单个圆的基本SVG为例。

<svg id="mysvg" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet">
  <circle id="mycircle" cx="400" cy="300" r="50" />
<svg>

你可以对其应用CSS效果。

circle {
  stroke-width: 5;
  stroke: #f00;
  fill: #ff0;
}

circle:hover {
  stroke: #090;
  fill: #fff;
}

你还可以附加事件处理程序来修改属性。

const mycircle = document.getElementById('mycircle');

mycircle.addEventListener('click', (e) => {
  console.log('circle clicked - enlarging');
  mycircle.setAttribute('r', 60);
});

下面的例子在SVG图像中添加了三十个随机的圆,在CSS中应用了悬停效果,并使用JavaScript在点击圆时将半径增加十个单位。

参见CodePen上SitePoint(@SitePoint)的Pen
SVG交互。

SVG到DOM的坐标转换

可能有必要在一个SVG元素上面叠加一个DOM元素--例如,在世界地图上显示的活动国家的菜单或信息框。假设SVG被嵌入到HTML中,元素就成为DOM的一部分,所以getBoundingClientRect()方法可以用来提取位置和尺寸。(打开上面的例子中的控制台,可以看到点击的圆在半径增加后的新属性。)

Element.getBoundingClientRect() 在所有的浏览器中都支持,并返回一个 对象,该对象具有以下像素尺寸的属性。DOMrect

  • .x 和 :元素左边的x坐标,相对于视口原点。.left
  • .right: 元素右边的x坐标,相对于视口原点的坐标
  • .y .top :元素顶部相对于视口原点的y坐标。
  • .bottom:元素底部相对于视口原点的y坐标。
  • .width:该元素的宽度(在IE8及以下版本不支持,但与.right 减去.left 相同)。
  • .height元素的高度(IE8及以下版本不支持,但与.bottom 减去.top 相同)。

所有的坐标都是相对于浏览器视口的,因此会随着页面的滚动而改变。页面上的绝对位置可以通过将window.scrollX 加到.leftwindow.scrollY 加到.top 来计算。

SVG有自己的坐标系统。它是通过viewbox 属性定义的,例如,viewbox="0 0 800 600" ,它设置了一个宽度为800单位,高度为600单位的坐标系,从(0,0)开始。如果你将这个SVG定位在一个800×600像素的区域内,每个SVG单元直接映射到一个屏幕像素。

然而,矢量图像的魅力在于它们可以被缩放到任何尺寸。你的SVG可以在400×300的空间里被缩放,甚至可以在100×1200的空间里被拉伸到无法辨认。如果你不知道该把它们放在哪里,向SVG中添加更多的元素就变得很困难。

(SVG的坐标系统可能很混乱--Sara Soueidan的viewport、viewBox和preserveAspectRatio文章描述了这些选项。)

简单分离的SVG协同效应

你也许可以完全避免坐标系之间的转换。

嵌入到页面中的SVG(而不是图像或CSS背景)成为DOM的一部分,可以用类似于其他元素的方式进行操作。例如,给定一个带有单个圆的基本SVG。

[code language="html"]
<svg id="mysvg" xmlns="www.w3.org/2000/svg" viewBox="0 0 800 600″ preserveAspectRatio="xMidYMid meet">
<圆圈id="mycircle".circle id="mycircle" cx="400″ cy="300″ r="50″ />

[/code]

我们可以应用CSS效果。

[code language="css"]
circle {
strok-width: 5;
stroke:#f00;
fill:#ff0;
}

circle:hover {
stroke:#090;
fill:#fff;
}
[/code]

并附加事件处理程序来修改其属性。

[code language="javascript"]
var mycircle = document.getElementById('mycircle');

mycircle.addEventListener('click', function(e) {
console.log('circle clicked - enlarging');
mycircle.setAttributeNS(null, 'r', 60);
}, false);
[/code]

下面的例子在一个SVG图片上添加了三十个随机的圆圈,在CSS中应用了一个悬停效果,并使用JavaScript在点击圆圈时将半径增加十个单位。

参见CodePen上SitePoint(@SitePoint)的PenSVG交互

SVG到DOM的坐标转换

如果我们想在一个SVG项目上叠加一个DOM元素,例如地图上的菜单或信息框,该怎么办?同样,由于我们的HTML嵌入的SVG元素构成了DOM的一部分,我们可以使用神话般的getBoundingClientRect()方法在一次调用中返回所有尺寸。打开上面的例子中的控制台,可以看到点击的圆在半径增加后的新属性。

Element.getBoundingClientRect() 所有的浏览器都支持该方法,并返回一个DOMrect对象,该对象具有以下像素尺寸的属性。

  • .x 和 - X坐标,相对于视口原点,元素的左侧.left
  • .right - X坐标,相对于视口原点,元素的右侧
  • .y 和 - 相对于视口原点的Y坐标,元素的顶面.top
  • .bottom - 元素底部相对于视口原点的y坐标
  • .width - 元素的宽度(在IE8及以下版本中不支持,但与 减去 )相同.right .left
  • .height - 元素的高度(在IE8及以下版本中不支持,但与 减去 相同).bottom .top

继续阅读:如何从DOM翻译成SVG坐标,再返回SitePoint上。