
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">
从处开始设置宽度为800单位,高度为600单位0,0。这些单位是用于绘图目的的任意度量,并且可以使用单位的分数。如果将此SVG放置在800按600像素区域中,则每个SVG单元应直接映射到屏幕像素。
但是,矢量图像可以缩放到任意大小,尤其是在自适应设计中。你可能SVG缩小到400通过300,甚至延伸面目全非在10通过1000空间。如果要根据光标位置放置其他元素,则向该SVG添加更多元素将变得更加困难。
注意:有关SVG坐标的更多信息,请参见Sara Soueidan的视口,vIEwBox和PReserveAspectRatio文章。
避免协调翻译
您也许可以避免完全在坐标系之间转换。
嵌入html页面(而不是图像或CSS背景)中的SVG成为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); });
下面的示例将30个随机圆添加到SVG图像,在CSS中应用悬停效果,并在单击圆时使用javascript将半径增加10个单位。
<!DOCtype HTML> <html> <head> <meta charset="utf-8"> <title>将30个随机圆添加到SVG图像(Web前端之家https://jiangweishan.com/)</title> <style> *, *:before, *:after { Box-sizing: border-box; padding: 0; margin: 0; } html { height: 100%; } body { min-height: 100%; font-family: Lato, sans-serif; font-size: 100%; padding: 0; margin: 10px; color: #444; background-color: #fff; overflow: hidden; } svg { background: radial-gradient(ellipse at center, #fefefe 0%, #cbeeff 100%); } circle { stroke-width: 5; stroke: #f00; fill: #ff0; } circle:hover { stroke: #090; fill: #fff; } </style> </head> <body> <svg id="mysvg" xmlns="HTTP://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1600 800" preserveAspectRatio="xMidYMid meet"></svg> <script> var svg = document.getElementById('mysvg'), NS = svg.getattribute('xmlns'); // add random circles var c, i; for (i = 0; i < 30; i++) { c = document.createElementNS(NS, 'circle'); c.setAttributeNS(null, 'cx', Math.round(Math.random() * 1600)); c.setAttributeNS(Null, 'cy', Math.round(Math.random() * 800)); c.setAttributeNS(null, 'r', 20 + Math.round(Math.random() * 30)); svg.appendChild(c); } // click a circle svg.addeventlistener('click', function(e) { var t = e.target; if (t.nodeName != 'circle') return; t.setAttributeNS(null, 'r', parseFloat(t.getAttributeNS(null, 'r')) + 10 ); console.log(t.getBoundingClientRect()); }, false); </script> </body> </html>
SVG到DOM坐标转换
可能有必要在SVG元素的顶部覆盖DOM元素-例如,在世界地图上显示的活动国家/地区上的菜单或信息框。假设SVG嵌入到HTML中,则元素成为DOM的一部分,因此getBoundingClientRect()方法可用于提取位置和尺寸。(在上面的示例中打开控制台,以显示半径增加后单击的圆的新属性。)
Element.getBoundingClientRect()在所有浏览器中均受支持,并返回一个DOMrect具有以下像素尺寸属性的对象:
.x和.left:元素左侧相对于视口原点的x坐标.right:相对于视口原点的元素右侧的x坐标.y和.top:元素顶侧相对于视口原点的y坐标.bottom:元素相对于视口原点的y坐标.width:元素的宽度(在ie8及以下版本中不支持,但与.rightminus相同.left).height:元素的高度(在IE8及以下版本中不支持,但与.bottom负号相同.top)
所有坐标都相对于浏览器视口,因此将在滚动页面时发生变化。可以通过添加window.scrollX到.left和window.scrollY来计算页面上的绝对位置.top。
DOM到SVG坐标转换
这更具挑战性。假设您要viewBox在单击事件发生的位置上放置一个新的SVG元素。事件处理程序对象提供DOM.clientX和.clientY像素坐标,但是必须将其转换为SVG单位。
很容易想到您可以通过应用乘法因子来计算SVG点的坐标。例如,如果将1000个单位宽度的SVG放置在500px宽度的容器中,则可以将任何DOMx坐标乘以2以获得SVG位置。它行不通!…
不能保证SVG完全适合您的容器。
如果元素尺寸发生变化(可能是响应用户调整浏览器大小的结果),则必须重新计算宽度和高度因子。
SVG或一个或多个元素可以在2D或3D空间中转换。
即使您克服了这些障碍,它也永远不会像您预期的那样有效,并且经常会有误差。
幸运的是,SVG提供了自己的矩阵分解机制来转换坐标。第一步是使用createSVGPoint()方法在SVG上创建一个点,并传入屏幕/事件x和y坐标:
const
svg = document.getElementById('mysvg'),
pt = svg.createSVGPoint();
pt.x = 100;
pt.y = 200;然后,您可以应用从SVG.getScreenCTM()方法的逆函数创建的矩阵变换,该变换将SVG单位映射到屏幕坐标:
const svgP = pt.matrixtransform( svg.getScreenCTM().inverse() );
svgP现在具有.x和.y属性,可提供SVG上的坐标viewBox。
以下代码在SVG画布上单击的点处放置了一个圆圈:
// get SVG element and namespace
const
svg = document.getElementById('mysvg'),
NS = svg.getAttribute('xmlns');
// click event
svg.addEventListener('click', (e) => {
const pt = svg.createSVGPoint();
// pass event coordinates
pt.x = e.clientX;
pt.y = e.clientY;
// transFORM to SVG coordinates
const svgP = pt.matrixTransform( svg.getScreenCTM().inverse() );
// add new SVG element
const circle = document.createElementNS(NS, 'circle');
circle.setAttribute('cx', svgP.x);
circle.setAttribute('cy', svgP.y);
circle.setAttribute('r', 10);
svg.appendChild(circle);
});注意:这些createElementNS()方法与标准DOMcreateElement()方法相同,除了它指定XML名称空间URI之外。换句话说,它作用于SVG文档而不是HTML。
转换为已转换的SVG坐标
还有进一步的复杂化。可以通过平移,缩放,旋转和/或倾斜来变换SVG或单个元素,这会影响所得的SVG坐标。例如,下<g>一层比标准SVG单位大4倍,因此坐标将是包含SVG的坐标的四分之一:
<g id="transFormed" transform="scale(4)"> <rect x="50" y="50" width="100" height="100" /> </g>
将得到的矩形显示为具有400在位置单元的宽度和高度200, 200。
幸运的是,该.getScreenCTM()方法可以应用于任何元素以及SVG本身。结果矩阵考虑了所有转换,因此您可以创建一个简单的svgPoint()转换函数:
const
svg = document.getElementById('mysvg'),
transformed = svg.getElementById('transformed');
console.log( svgPoint(svg, 10, 10) ); // returns x, y
console.log( svgPoint(transformed, 10, 10) ); // = x/4, y/4
// translate page to SVG coordinate
function svgPoint(element, x, y) {
const pt = svg.createSVGPoint();
pt.x = x;
pt.y = y;
return pt.matrixTransform( element.getScreenCTM().inverse() );
}以下演示适用于所有现代浏览器(如果将JavaScript转换为es5,则将在ie11中使用!)。轻击/单击SVG时,将在光标处添加一个圆圈。
<g>单击转换的区域时也会发生这种情况,但是将该元素而不是SVG本身传递给svgPoint()函数以确保计算出正确的坐标:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>轻击/单击SVG时,将在光标处添加一个圆圈(WEB前端之家HTTPS://jiangweishan.com/)</title> <style> *, *:before, *:after { box-sizing: border-box; padding: 0; margin: 0; } html { height: 100%; } body { height: 100%; font-family: Lato, helvetica, sans-serif; font-size: 100%; padding: 0; margin: 0; color: #333; background-color: #fff; overflow: hidden; } #mysvg { display: block; width: auto; height: 100%; margin: 0; background: radial-gradient(ellipse at center, #fefefe 0%, #cbeeff 100%); border: 5px solid #333; touch-action: none; } @media (max-aspect-ratio: 1/1) { #mysvg { width: 100%; height: auto; } } #mysvg rect { fill: #069; } #mysvg circle { stroke-width: 3; stroke: #f00; fill: #ff0; } #mysvg text { fill: #fff; } #output { position: fixed; display: grid; grid-template-columns: auto 3em 3em; gap: 0.2em; bottom: 10px; right: 10px; padding: 0.2em; background-color: rgba(255,255,255,0.9); border-radius: 5px; pointer-events: none; } #output strong, #output span { text-align: right; font-variant-numeric: tabular-nums; } h1 { font-size: 1.1em; grid-column-start: span 3; } #targetID { grid-column-start: span 2; } </style> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" id="mysvg" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMid meet"> <g id="transform1" transform="scale(4)"> <rect x="50" y="50" width="100" height="100" rx="10" /> <text x="100" y="100" text-anchor="middle" dominant-baseline="middle">scale(4)</text> </g> <g id="transform2" transform="scale(2) rotate(10 350 350) skewY(5)"> <rect x="300" y="300" width="100" height="100" rx="10" /> <text x="350" y="350" text-anchor="middle" dominant-baseline="middle">scale(2)</text> </g> </svg> <div id="output"> <h1>DOM to SVG Co-ordinates</h1> <strong>DOM client X,Y:</strong> <span id="clientX"></span> <span id="clientY"></span> <strong>SVG X,Y:</strong> <span id="svgX"></span> <span id="svgY"></span> <strong>target X,Y:</strong> <span id="targetX"></span> <span id="targetY"></span> <strong>add target:</strong> <span id="targetID"></span> <strong>add X,Y:</strong> <span id="addX"></span> <span id="addY"></span> </div> <script> // initialize const svg = document.getElementById('mysvg'), NS = svg.getAttribute('xmlns'), out = {}; 'clientX,clientY,svgX,svgY,targetX,targetY,targetID,addX,addY'.split(',').map(s => { out[s] = { node: document.getElementById(s), value: '-' } }); // events svg.addEventListener('pointermove', getCoordinates); svg.addEventListener('pointerdown', getCoordinates); svg.addEventListener('pointerdown', createCircle); // update co-ordinates function getCoordinates(event) { // DOM co-ordinate out.clientX.value = event.clientX; out.clientY.value = event.clientY; // SVG co-ordinate const svgP = svgPoint(svg, out.clientX.value, out.clientY.value); out.svgX.value = svgP.x; out.svgY.value = svgP.y; // target co-ordinate const svgT = svgPoint(event.target, out.clientX.value, out.clientY.value); out.targetX.value = svgT.x; out.targetY.value = svgT.y; upDateInfo(); }; // add a circle to the target function createCircle(event) { // circle clicked? if (event.target.nodeName === 'circle') return; // add circle to contAIning element const target = event.target.closest('g') || event.target.ownerSVGElement || event.target, svgP = svgPoint(target, event.clientX, event.clientY), cX = Math.round(svgP.x), cY = Math.round(svgP.y), circle = document.createElementNS(NS, 'circle'); circle.setAttribute('cx', cX); circle.setAttribute('cy', cY); circle.setAttribute('r', 30); target.APPendChild(circle); // output information out.targetID.value = target.id || target.nodeName; out.addX.value = cX; out.addY.value = cY; updateInfo(); } // translate page to SVG co-ordinate function svgPoint(element, x, y) { var pt = svg.createSVGPoint(); pt.x = x; pt.y = y; return pt.matrixTransform(element.getScreenCTM().inverse()); } // output values function updateInfo() { for (p in out) { out[p].node.textContent = isNaN(out[p].value) ? out[p].value : Math.round(out[p].value); } } </script> </body> </html>
理想情况下,最好避免DOM与SVG坐标之间的平移,但是,在这种情况下,请使用上述方法来确保该过程在所有页面尺寸上均可靠。








网友评论文明上网理性发言 已有0人参与
发表评论: