10

我正在开发一个地图,在 Javascript 中使用 SVG 来绘制线条。

我想添加一个功能,您可以在其中搜索道路,如果找到道路,地图上会出现一个圆圈。

我知道我可以在 SVG 中画一个圆圈,但我的问题是,圆圈的大小不应该根据缩放级别而改变。换句话说,圆必须始终具有相同的大小。我地图上的道路有这个功能,我所要做的就是添加

vector-effect="non-scaling-stroke"

到线属性..

一条线看起来像这样。

<line vector-effect="non-scaling-stroke" stroke-width="3" id = 'line1' x1 = '0' y1 = '0' x2 = '0' y2 = '0' style = 'stroke:rgb(255,215,0);'/> 

圆圈看起来像这样。

<circle id = "pointCircle" cx="0" cy="0" r="10" stroke="red" stroke-width="1" fill = "red"/>

是否可以以某种方式将圆圈定义为“非缩放”?

4

4 回答 4

7

我花了一段时间,但我终于把数学弄清了。这个解决方案需要三件事:

  1. 在您的页面中包含此脚本(连同 SVGPan.js 脚本),例如
    <script xlink:href="SVGPanUnscale.js"></script>
  2. 确定您不想缩放的项目(例如,将它们放在具有特殊类或 ID 的组中,或将特定类放在每个元素上),然后告诉脚本如何找到这些项目,例如
    unscaleEach("g.non-scaling > *, circle.non-scaling");
  3. 用于transform="translate(…,…)"将每个元素放置在图表上,而不是 cx="…" cy="…".

仅通过这些步骤,使用 SVGPan 进行缩放和平移不会影响标记元素的缩放(或旋转或倾斜)。

演示: http: //phrogz.net/svg/scale-independent-elements.svg

图书馆

// Copyright 2012 © Gavin Kistner, !@phrogz.net
// License: http://phrogz.net/JS/_ReuseLicense.txt

// Undo the scaling to selected elements inside an SVGPan viewport
function unscaleEach(selector){
  if (!selector) selector = "g.non-scaling > *";
  window.addEventListener('mousewheel',     unzoom, false);
  window.addEventListener('DOMMouseScroll', unzoom, false);
  function unzoom(evt){
    // getRoot is a global function exposed by SVGPan
    var r = getRoot(evt.target.ownerDocument);
    [].forEach.call(r.querySelectorAll(selector), unscale);
  }
}

// Counteract all transforms applied above an element.
// Apply a translation to the element to have it remain at a local position
function unscale(el){
  var svg = el.ownerSVGElement;
  var xf = el.scaleIndependentXForm;
  if (!xf){
    // Keep a single transform matrix in the stack for fighting transformations
    // Be sure to apply this transform after existing transforms (translate)
    xf = el.scaleIndependentXForm = svg.createSVGTransform();
    el.transform.baseVal.appendItem(xf);
  }
  var m = svg.getTransformToElement(el.parentNode);
  m.e = m.f = 0; // Ignore (preserve) any translations done up to this point
  xf.setMatrix(m);
}

演示代码

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <title>Scale-Independent Elements</title>
  <style>
    polyline { fill:none; stroke:#000; vector-effect:non-scaling-stroke; }
    circle, polygon { fill:#ff9; stroke:#f00; opacity:0.5 }
  </style>
  <g id="viewport" transform="translate(500,300)">
    <polyline points="-100,-50 50,75 100,50" />
    <g class="non-scaling">
      <circle  transform="translate(-100,-50)" r="10" />
      <polygon transform="translate(100,50)" points="0,-10 10,0 0,10 -10,0" />
    </g>
    <circle class="non-scaling" transform="translate(50,75)" r="10" />
  </g>
  <script xlink:href="SVGPan.js"></script>
  <script xlink:href="SVGPanUnscale.js"></script>
  <script>
    unscaleEach("g.non-scaling > *, circle.non-scaling");
  </script>
</svg>
于 2012-05-23T05:02:08.393 回答
3

如果您正在寻找一种完全静态的方式来执行此操作,则可以将非缩放笔画与标记结合起来来实现这一点,因为标记可以相对于笔画宽度。

换句话说,您可以将圆圈包裹在 <marker> 元素中,然后在需要它们的地方使用这些标记。

<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000">
    <marker id="Triangle"
      viewBox="0 0 10 10" refX="0" refY="5" 
      markerUnits="strokeWidth"
      markerWidth="4" markerHeight="3"
      orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" />
    </marker>
        <path d="M 100 100 l 200 0" vector-effect="non-scaling-stroke"
        fill="none" stroke="black" stroke-width="10" 
        marker-end="url(#Triangle)"  />
        <path d="M 100 200 l 200 0" 
        fill="none" stroke="black" stroke-width="10" 
        marker-end="url(#Triangle)"  />
</svg>

同样也可以在这里查看和调整。svg 规范并没有完全明确说明在这种情况下应该发生什么(因为标记不在 SVG Tiny 1.2 中,矢量效果不在 SVG 1.1 中)。我目前的想法是它可能会影响标记的大小,但目前似乎没有观众这样做(尝试在支持矢量效果的查看器中,例如 Opera 或 Chrome)。

于 2012-05-07T08:07:35.043 回答
0

看起来在 webkit 中完成了一些工作(可能与这个错误有关:320635)并且当像这样简单地附加时,新的转换不会留下来

transform.baseVal.appendItem

这似乎效果更好。甚至可以在 IE 10 中使用。

编辑:修复了前面多个翻译转换和后面可能的其他转换的更一般情况的代码。但是,所有转换后的第一个矩阵转换必须保留用于未缩放。

translate(1718.07 839.711) translate(0 0) matrix(0.287175 0 0 0.287175 0 0) rotate(45 100 100)

function unscale()
{
    var xf = this.ownerSVGElement.createSVGTransform();
    var m = this.ownerSVGElement.getTransformToElement(this.parentNode);
    m.e = m.f = 0; // Ignore (preserve) any translations done up to this point
    xf.setMatrix(m);

    // Keep a single transform matrix in the stack for fighting transformations
    // Be sure to apply this transform after existing transforms (translate)
    var SVG_TRANSFORM_MATRIX = 1;
    var SVG_TRANSFORM_TRANSLATE = 2;
    var baseVal = this.transform.baseVal;
    if(baseVal.numberOfItems == 0)
        baseVal.appendItem(xf);
    else
    {
        for(var i = 0; i < baseVal.numberOfItems; ++i)
        {
            if(baseVal.getItem(i).type == SVG_TRANSFORM_TRANSLATE && i == baseVal.numberOfItems - 1)
        {
                baseVal.appendItem(xf);
            }

            if(baseVal.getItem(i).type != SVG_TRANSFORM_TRANSLATE)
            {
                if(baseVal.getItem(i).type == SVG_TRANSFORM_MATRIX)
                    baseVal.replaceItem(xf, i);
                else
                    baseVal.insertItemBefore(xf, i);
                break;
            }
        }
    }
}

EDIT2:Chrome 出于某种原因杀死了 getTransformToElement,因此需要手动检索矩阵:

var m = this.parentNode.getScreenCTM().inverse().multiply(this.ownerSVGElement.getScreenCTM());
于 2014-05-27T23:12:23.063 回答
0

在这里这里讨论过

看起来当前的浏览器没有做预期的事情,所以需要对<marker>. transorm: scaleX(5)在等的用户上,需要<marker>transform: translate(...) scaleX(0.2)里面加上一个,如果需要的话,还考虑到模式内可能<pattern>的////xywidthheighttransform-origin

于 2020-04-14T19:25:16.103 回答