Сохранить размер дочерних элементов при масштабировании родительского элемента - PullRequest
11 голосов
/ 16 января 2012

У меня есть страница XHTML с SVG, встроенным в нее как элемент <svg>. Мне нужно реализовать масштабирование, но есть требование, чтобы внутренние <text> элементы не масштабировались. Очевидное решение -

<svg ...>
  <!-- Root <g> to programmatically `setAttribute("transform", ...)` -->
  <g transform="scale(0.5)">
    <!-- Various inner elements here -->
    <!-- Here is a text element.
         Additional <g> is to apply text position which
         *must* depend on the scaling factor set above -->
    <g transform="translate(100 50)">
      <!-- Apply the opposite scaling factor.
           Must be done for every `<text>` element on each change of the scaling factor... -->
      <text x="0" y="0" transform="scale(2)">Hello, World!</text>
    </g>
  </g>
</svg>

Есть ли лучшее решение, чем это? Может быть, есть способ «сбросить» результирующий масштабный коэффициент на внутренних <text> элементах? Код должен работать в Firefox и Chrome.

UPD . Существует также возможность размещать текстовые элементы вне масштабируемого элемента и вручную управлять позициями текстовых элементов. Это позволяет избежать визуальных сбоев, появляющихся в тексте из-за масштабирования. В настоящее время я использую этот метод.

Ответы [ 3 ]

5 голосов
/ 17 января 2012

Существует transform="ref(svg)", который определен в SVG Tiny 1.2 . Насколько мне известно, это не реализовано ни в одном браузере, кроме Opera (Presto).

<svg xmlns="http://www.w3.org/2000/svg" font-size="24" text-rendering="geometricPrecision">
  <!-- Root <g> to programmatically `setAttribute("transform", ...)` -->
  <text x="0" y="1em" stroke="gray" fill="none">Hello, World!</text>
  <g transform="scale(0.5) rotate(25)">
    <!-- Various inner elements here -->
    <!-- Here is a text element.
         Additional <g> is to apply text position which
         *must* depend on the scaling factor set above -->
    <g transform="translate(100 50)">
      <!-- Apply the opposite scaling factor.
           Must be done for every `<text>` element on each change of the scaling factor... -->
      <text x="0" y="1em" transform="ref(svg)">Hello, World!</text>
    </g>
  </g>
</svg>

В приведенном выше примере текст должен отображаться там, где находится серый контур (в верхнем левом углу). К элементу, который имеет transform="ref(svg)", нельзя применять поворот или масштабирование, как если бы он был прямым потомком корневого элемента svg.

4 голосов
/ 23 мая 2012

Если допустимо (а) использовать JavaScript и (б) указать положение каждого немасштабирующего элемента с помощью transform="translate(…,…)" либо на самом элементе, либо в группе переноса (как вы сделали), то вы можете использовать мой код unscaling:
http://phrogz.net/svg/libraries/SVGPanUnscale.js

Демо: http://jsfiddle.net/uPSc4/

Как вы можете видеть в демонстрации, он отменяет все преобразования, кроме перевода (включая масштабирование, поворот и наклон), сохраняя элементы в том месте, где они должны быть.

См. Также эту демонстрацию, которая позволяет масштабировать и панорамировать графику с помощью SVGPan , сохраняя при этом некоторые элементы разметки в неизменном виде:
http://phrogz.net/svg/scale-independent-elements.svg

Вот весь код, который вам нужен:

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

// Removes 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);
}

Вот демонстрационный код, если JSFiddle не работает:

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:x="http://www.w3.org/1999/xlink"
     font-size="36">
  <g transform="matrix(1 0 0 1 0 0)">
    <g transform="translate(100 50)"><text x="0" y="1em">A!</text></g>
    <g transform="translate(200 150)"><text x="0" y="1em">B!</text></g>
    <g transform="translate(150 75)"><text x="0" y="1em">C!</text></g>
  </g>
  <script x:href="http://phrogz.net/svg/libraries/SVGPanUnscale.js"></script>
  <script>
    var root = document.querySelector('g'),
        text = document.querySelectorAll('text'),
        xf   = root.transform.baseVal.getItem(0);

    // Keep scaling, spinning, and sliding the outer graphic
    // but "unscale" each text element afterwards.    
    setInterval(function(){
      var m = xf.matrix.scale(0.99).translate(10,0).rotate(1);
      xf.setMatrix(m);
      [].forEach.call(text,unscale);
    },100);​
  </script>
</svg>​
1 голос
/ 07 мая 2014

Попробуйте это

HTML

<svg>  
  <g transform="scale(0.5)">   
    <g transform="translate(100 50)">      
      <text x="0" y="0">Hello, World!</text>
    </g>
  </g>
</svg>

Найдите обратную матрицу и добавьте только необходимые преобразования.

var svg = document.getElementsByTagName('svg')[0]; 
var text = document.querySelectorAll('text')[0];  
var matrix = svg.createSVGTransformFromMatrix(text.getCTM().inverse().translate(100,50));
text.transform.baseVal.initialize(matrix);

JsFiddle - http://jsfiddle.net/uPSc4/10/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...