Вопрос по svg, javascript – Как нарисовать немасштабируемый круг в SVG с помощью Javascript

7

Я разрабатываю карту в Javascript, используя SVG для рисования линий.

Я хотел бы добавить функцию, где вы можете искать дорогу, и если дорога найдена, на карте появляется круг.

Я знаю, что могу нарисовать круг в SVG, но моя проблема в том, что размер круга не должен меняться в зависимости от уровня масштабирования. Другими словами, круг должен всегда иметь одинаковый размер. Дороги на моей карте имеют эту функцию, все, что мне нужно было сделать, это добавить

<code>vector-effect="non-scaling-stroke"
</code>

к строке атрибутов ..

Линия выглядит следующим образом.

<code><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);'/> 
</code>

Круг выглядит так.

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

Можно ли определить круг как «немасштабируемый»? каким-то образом?

Найдите немного иронично, чтобы нарисовать немасштабируемый круг в Scalable Векторная графика Rune FS
@ RuneFS Я полагаю, вы в основном шутите, но примите во внимание и обычную необходимость накладывать 2D-графику в стиле HUD на трехмерную сцену. В целом, метки пользовательского пространства имеют совершенно другие потребности, чем сложный интерактивный контент, который они могут аннотировать. Phrogz
Я масштабирую с помощью SVGPan. Я опробую вашу идею :) Code.google.com / р / svgpan Prechtig
Как ты увеличиваешь масштаб? Если вы масштабируете всю карту, я решил эту проблему путем масштабирования объекта по обратной шкале, т.е. если вы масштабируете карту с коэффициентомs, вы можете масштабировать круг с помощью1/s. Felix Kling

Ваш Ответ

3   ответа
7

но я наконец-то выучил математику. Это решение требует трех вещей:

Включают этот сценарий на вашей странице (вместе со скриптом SVGPan.js), например
<script xlink:href="SVGPanUnscale.js"></script> Определите элементы, которые вы не хотите масштабировать (например, поместите их в группу со специальным классом или идентификатором или поместите определенный класс в каждый элемент), а затем сообщите сценарию, как найти эти элементы, например,
unscaleEach("g.non-scaling > *, circle.non-scaling"); Используйтеtransform="translate(…,…)" разместить каждый элемент на диаграмме,н cx="…" cy="…".

Только с этими шагами масштабирование и панорамирование с использованием SVGPan не повлияет на масштаб (или поворот, или наклон) отмеченных элементов.

Демо:http: //phrogz.net/svg/scale-independent-elements.svБиблиотек
// Copyright 2012 © Gavin Kistner, [email protected]
// 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>
Это не работает в опере 22.0.1471.40 (хотя работал как месяц назад ...) или IE 10. Отлично работает в Opera 12.17. Приходите к веб-разработке, сказали они, только одна платформа, которую они сказали, работает везде, где они сказали ... Eugene
(Должен заметить, мой собственный код, основанный на немасштабировании, работает в Opera 12.17, сама демонстрация не масштабируется) Eugene
2

вы можете комбинировать немасштабирующий штрих с маркерами, чтобы получить это, поскольку маркеры могут быть относительно ширины штриха.

Другими словами, вы можете обернуть круги в элемент <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).

Можете ли вы привести пример того, как будет выглядеть код? Например, если я не хочу круг, который всегда имеет ширину 15 пикселей. Prechtig
0

что в webkit была проделана некоторая работа (возможно, связанная с этой ошибкой: 320635) и новое преобразование не сохраняется, когда просто добавляется таким образом

transform.baseVal.appendItem

Кажется, это работает лучше. Даже работает в IE 10.

EDIT: Исправлен код для более общего случая многократных преобразований перевода впереди и возможных других преобразований после. Первое матричное преобразование после всех преобразований должно быть зарезервировано для немасштабирования.

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());

Похожие вопросы