как увеличить масштаб структуры svg
У меня есть сложный набор фигур, которые находятся в фрагменте. Они визуализируются с помощью React, но я действительно просто ищу некоторые указатели о том, как я мог бы использовать масштабирование этих фигур.
Мой googling терпит неудачу, и я могу действительно найти примеры графиков.
Как можно масштабировать и выводить сложные структуры?
<svg height="767" width="903">
<g class="vx-group vx-tree" transform="translate(20, 70)">
<g class="vx-group" transform="translate(0, 70)">
<g class="vx-group" transform="translate(0, 0)">
<path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5"
stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
</g>
<g class="vx-group" transform="translate(0, 0)">
<g class="vx-group" transform="translate(451.5, 0)" opacity="1">
<g class="vx-group node__container" transform="translate(0, 0)">
<svg class="" x="0" y="0" style="overflow: visible;">
<polygon
points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30"
class="node__hexagon"></polygon>
</svg>
<g class="vx-group node__business-unit" transform="translate(0, 0)">
<use xlink:href="#icon-BusinessUnit"></use>
</g>
<g class="hierarchy-label__container" transform="translate(0, -40)">
<path class="" d="
M 0.0078125, 5.15625
L 34.64882865137755,25.156249999999996
M -0.9921875, 5.15625
L -34.63320365137754,25.156249999999996
H -65.8515625
a8,8 0 0 1 -8,-8
V -47.15625
a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8
L 73.8515625, 17.156249999999996
a8,8 0 0 1 -8,8
L 34.64882865137755, 25.156249999999996
Z
">
</path>
<svg x="0" y="0" style="overflow: visible;">
<text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle"
style="pointer-events: none;">
<tspan x="0" dy="0em">Finance</tspan>
</text>
</svg>
<svg x="0" y="0" style="overflow: visible;">
<text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle"
style="pointer-events: none;">
<tspan x="0" dy="0.71em">Business Unit</tspan>
</text>
</svg>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
Ответы
Ответ 1
Вы можете просто использовать transform
css для масштабирования svg. Установите источник transform-origin
css, из которого вы хотите "привязать" начало увеличения, и используйте scale(x)
в transform
как в приведенном выше примере, который использует элемент ввода диапазона с минимальным значением до 1
и максимум до 200
для сканирования с 1%
до 200%
:
const slider = document.getElementById("zoomRange");
const zvgZoom = document.getElementById("svgZoom");
const zoomValue = document.getElementById("zoomValue");
slider.oninput = function() {
//console.log('zoom', this.value / 100);
zoomValue.innerText = '${this.value}%';
zvgZoom.style.transform = 'scale(${this.value / 100})';
}
#svgContainer {
background-color: #dedede;
}
#svgZoom {
transform-origin: 0% 0%;
}
<input type="range" min="1" max="200" value="100" class="slider" id="zoomRange">
<span id="zoomValue">100%</span>
<div id="svgContainer">
<svg id="svgZoom" height="767" width="903">
<g class="vx-group vx-tree" transform="translate(20, 70)">
<g class="vx-group" transform="translate(0, 70)">
<g class="vx-group" transform="translate(0, 0)">
<path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5" stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
</g>
<g class="vx-group" transform="translate(0, 0)">
<g class="vx-group" transform="translate(451.5, 0)" opacity="1">
<g class="vx-group node__container" transform="translate(0, 0)">
<svg class="" x="0" y="0" style="overflow: visible;">
<polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30" class="node__hexagon"></polygon>
</svg>
<g class="vx-group node__business-unit" transform="translate(0, 0)">
<use xlink:href="#icon-BusinessUnit"></use>
</g>
<g class="hierarchy-label__container" transform="translate(0, -40)">
<path class="" d="
M 0.0078125, 5.15625
L 34.64882865137755,25.156249999999996
M -0.9921875, 5.15625
L -34.63320365137754,25.156249999999996
H -65.8515625
a8,8 0 0 1 -8,-8
V -47.15625
a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8
L 73.8515625, 17.156249999999996
a8,8 0 0 1 -8,8
L 34.64882865137755, 25.156249999999996
Z
"></path>
<svg x="0" y="0" style="overflow: visible;">
<text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle" style="pointer-events: none;">
<tspan x="0" dy="0em">Finance</tspan>
</text>
</svg>
<svg x="0" y="0" style="overflow: visible;">
<text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle" style="pointer-events: none;">
<tspan x="0" dy="0.71em">Business Unit</tspan>
</text>
</svg>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
Ответ 2
Масштабирование в svg выполняется с помощью viewBox, который сочетает в себе как масштабирование, так и смещение. Есть хорошая статья Как масштабировать SVG. Из следующей статьи:
Если вы рассматриваете документ как холст, поле просмотра является частью холста, который вы хотите видеть зрителем.
Это похоже на экран вашего сотового телефона в приложении камеры, который показывает часть сцены, которая наблюдается с указанным масштабом и смещениями.
Хороший образец, который демонстрирует, что такое viewBox, можно найти здесь.
Небольшая математика и я реализовали увеличение/уменьшение масштаба с помощью мыши. Кроме того, добавлено панорамирование с mousemove и значением шкалы отображения. Пример, демонстрирующий, как можно использовать viewBox:
const svgImage = document.getElementById("svgImage");
const svgContainer = document.getElementById("svgContainer");
var viewBox = {x:0,y:0,w:svgImage.getAttribute("width"),h:svgImage.getAttribute("height")};
svgImage.setAttribute('viewBox', '${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}');
const svgSize = {w:svgImage.getAttribute("width"),h:svgImage.getAttribute("height")};
var isPanning = false;
var startPoint = {x:0,y:0};
var endPoint = {x:0,y:0};;
var scale = 1;
svgContainer.onmousewheel = function(e) {
e.preventDefault();
var w = viewBox.w;
var h = viewBox.h;
var mx = e.x;//mouse x
var my = e.y;
var dw = w*Math.sign(e.deltaY)*0.05;
var dh = h*Math.sign(e.deltaY)*0.05;
var dx = dw*mx/svgSize.w;
var dy = dh*my/svgSize.h;
viewBox = {x:viewBox.x+dx,y:viewBox.y+dy,w:viewBox.w-dw,h:viewBox.h-dh};
scale = svgSize.w/viewBox.w;
zoomValue.innerText = '${Math.round(scale*100)/100}';
svgImage.setAttribute('viewBox', '${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}');
}
svgContainer.onmousedown = function(e){
isPanning = true;
startPoint = {x:e.x,y:e.y};
}
svgContainer.onmousemove = function(e){
if (isPanning){
endPoint = {x:e.x,y:e.y};
var dx = (startPoint.x - endPoint.x)/scale;
var dy = (startPoint.y - endPoint.y)/scale;
var movedViewBox = {x:viewBox.x+dx,y:viewBox.y+dy,w:viewBox.w,h:viewBox.h};
svgImage.setAttribute('viewBox', '${movedViewBox.x} ${movedViewBox.y} ${movedViewBox.w} ${movedViewBox.h}');
}
}
svgContainer.onmouseup = function(e){
if (isPanning){
endPoint = {x:e.x,y:e.y};
var dx = (startPoint.x - endPoint.x)/scale;
var dy = (startPoint.y - endPoint.y)/scale;
viewBox = {x:viewBox.x+dx,y:viewBox.y+dy,w:viewBox.w,h:viewBox.h};
svgImage.setAttribute('viewBox', '${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}');
isPanning = false;
}
}
svgContainer.onmouseleave = function(e){
isPanning = false;
}
<span id="zoomValue">1</span>
<div id="svgContainer">
<svg id="svgImage" height="964" width="767">
<g class="vx-group vx-tree" transform="translate(20, 70)">
<g class="vx-group" transform="translate(0, 70)">
<g class="vx-group" transform="translate(0, 0)">
<path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5" stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
</g>
<g class="vx-group" transform="translate(0, 0)">
<g class="vx-group" transform="translate(451.5, 0)" opacity="1">
<g class="vx-group node__container" transform="translate(0, 0)">
<svg class="" x="0" y="0" style="overflow: visible;">
<polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30" class="node__hexagon"></polygon>
</svg>
<g class="vx-group node__business-unit" transform="translate(0, 0)">
<use xlink:href="#icon-BusinessUnit"></use>
</g>
<g class="hierarchy-label__container" transform="translate(0, -40)">
<path class="" d="
M 0.0078125, 5.15625
L 34.64882865137755,25.156249999999996
M -0.9921875, 5.15625
L -34.63320365137754,25.156249999999996
H -65.8515625
a8,8 0 0 1 -8,-8
V -47.15625
a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8
L 73.8515625, 17.156249999999996
a8,8 0 0 1 -8,8
L 34.64882865137755, 25.156249999999996
Z
"></path>
<svg x="0" y="0" style="overflow: visible;">
<text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle" style="pointer-events: none;">
<tspan x="0" dy="0em">Finance</tspan>
</text>
</svg>
<svg x="0" y="0" style="overflow: visible;">
<text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle" style="pointer-events: none;">
<tspan x="0" dy="0.71em">Business Unit</tspan>
</text>
</svg>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
Ответ 3
Масштабирование и панорамирование являются общими и полезными методами визуализации данных, которые особенно хорошо работают с визуализацией на основе SVG, поскольку векторная графика не страдает от пикселизации, поскольку ее растровая копия будет.
Этот ответ исследует встроенную поддержку D3 как для масштабирования, так и для панорамирования, путем визуализации данных Кика Чжу Чжу с D3.js Cookbook (2013).
Начните с открытия локальной копии следующего файла в вашем веб-браузере:
https://github.com/NickQiZhu/d3-cookbook/blob/master/src/chapter10/zoom.html.
В этом рецепте будет реализовано геометрическое масштабирование и панорамирование с использованием поддержки масштабирования D3. Посмотрим, как это делается в коде:
<script type="text/javascript">
var width = 960, height = 500, r = 50;
var data = [
[width / 2 - r, height / 2 - r],
[width / 2 - r, height / 2 + r],
[width / 2 + r, height / 2 - r],
[width / 2 + r, height / 2 + r]
];
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(
d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoom)
)
.append("g");
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", r)
.attr("transform", function (d) {
return "translate(" + d + ")";
});
function zoom() {
svg.attr("transform", "translate("
+ d3.event.translate
+ ")scale(" + d3.event.scale + ")");
}
</script>
Этот рецепт создает следующий эффект масштабирования и панорамирования:
Оригинал:
Zoom:
Кастрюля:
Вы заметите, что масштабирование и панорамирование прекрасно реагируют как на колесико мыши, так и на многоточечный жест (в отличие от других ответов). Большая часть тяжелого подъема выполняется библиотекой D3, поэтому нам нужен небольшой код.
Ответ 4
Когда мне не удалось найти легкий инструмент для масштабирования (ни один из тестируемых не смог сохранить курсор мыши в качестве центральной точки), я сделал "немного" исследование и получил свое решение. Я нашел самый простой способ использовать viewbox.
Outer svg должен объявить viewBox (или он должен быть создан в начале функции).
<svg id="svgImage" width="900" height="500" viewBox="0 0 900 500">
<style>text { fill: white; }</style>
<g id="par" class="vx-group vx-tree" transform="translate(20, 70)">
<g class="vx-group" transform="translate(0, 70)">
<g class="vx-group" transform="translate(0, 0)">
<path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5" stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
</g>
<g class="vx-group" transform="translate(0, 0)">
<g class="vx-group" transform="translate(451.5, 0)" opacity="1">
<g class="vx-group node__container" transform="translate(0, 0)">
<svg class="" x="0" y="0" style="overflow: visible;">
<polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30" class="node__hexagon"></polygon>
</svg>
<g class="vx-group node__business-unit" transform="translate(0, 0)">
<use xlink:href="#icon-BusinessUnit"></use>
</g>
<g class="hierarchy-label__container" transform="translate(0, -40)">
<path class="" d="
M 0.0078125, 5.15625
L 34.64882865137755,25.156249999999996
M -0.9921875, 5.15625
L -34.63320365137754,25.156249999999996
H -65.8515625
a8,8 0 0 1 -8,-8
V -47.15625
a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8
L 73.8515625, 17.156249999999996
a8,8 0 0 1 -8,8
L 34.64882865137755, 25.156249999999996
Z
"></path>
<svg x="0" y="0" style="overflow: visible;"><text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle" style="pointer-events: none;"><tspan x="0" dy="0em">Finance</tspan></text></svg>
<svg x="0" y="0" style="overflow: visible;"><text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle" style="pointer-events: none;"><tspan x="0" dy="0.71em">Business Unit</tspan></text></svg>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
<script>
const svgImage = document.getElementById("svgImage");
const svgSize = {w:svgImage.clientWidth ,h:svgImage.clientHeight};
var oldScale = 1;
svgImage.onmousewheel = function(e) {
e.preventDefault();
var svgW = svgSize.w,
svgH = svgSize.h,
mX = e.offsetX,
mY = e.offsetY,
delta = (e.wheelDelta) ? -e.wheelDelta : e.detail,
newScale = oldScale + (oldScale*delta/1200); //1200: intensity
var vb = svgImage.getAttribute('viewBox').split(" ");
var newW = svgW * newScale,
newH = svgH * newScale,
newX = vb[0]*1 + (vb[2]*1 - newW) * (mX/svgW),
newY = vb[1]*1 + (vb[3]*1 - newH) * (mY/svgH);
viewBox = { x:Math.round(newX), y:Math.round(newY), w:newW, h:newH };
svgImage.setAttribute('viewBox', '${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}');
oldScale = newScale;
</script>
PS. Очень хорошая серия статей о манипуляциях с системой координат SVG (и не только) написана Сарой Сауидан. Хороший блог, чтобы копаться.