meanwhile I've read a lot of sites about viewbox, viewports and svg scaling.
I have an svg-box, with variable size, depending on the browser-window.
Inside this, I display some elements like rects, lines texts etc.
These elements, or better the view is "zoomable" by using the mousewheel.
<h1>Editor:</h1>
<div id="content">
<svg id="drawing"
title="Layouteditor"
viewBox="0 0 500 300"
xmlns="http://www.w3.org/2000/svg"
version="2.0"
preserveAspectRatio="xMidYMid meet">
<g class="draggable preview" id="437" pointer-events="fill" transform="translate(150 50)">
<rect fill="none" x="80" y="100" width="41" height="41"></rect>
<line class="CDC300" fill="none" x1="140" y1="120" x2="121" y2="120" />
<text class="CDC300" font-family="sans-serif" font-size="30px" fill="#000000" stroke-width="0.3" text-anchor="middle" x="100" y="131">T</text>
<text class="CDC400" id="20010" text-anchor="end" x="75" y="125">=HTC1+HSP1</text>
<circle class="insertpoint" cx="140" cy="120" r="3" />
<circle class="pin" cx="140" cy="120" r="2" />
</g>
<g id="a123" style="touch-action: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
<line class="hotwater graphic" id="g123" x1="100" y1="120" x2="200" y2="120"></line>
</g>
</svg>
</div>
the scripts:
var svgDrawing = document.getElementById('drawing');
var viewbox = svgDrawing.viewBox.baseVal;
function zoomInOut(evt) {
if (evt.deltaY > 1) {
viewbox.width += 10;
viewbox.height += 10;
} else {
viewbox.width -= 10;
viewbox.height -= 10;
}
}
function zoomFull(evt){
console.log("Zoom 100%");
//here, the zoom should be set to 100% of the drawing
//###################################################
//###################################################
//###################################################
}
$(document).ready(function () {
console.log("document ready");
svgDrawing.setAttribute("height", window.innerHeight - 200);
svgDrawing.addEventListener('wheel', e => zoomInOut(e));
svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})
now, I'd like to add a "fullZoom"-function.
For this, I need to get the outer dimensions of all nested elements, I think. So perhaps like a boundingBox over all elements. And here, you come into the game. I am totally confused meanwhile.
Thank you,
Carsten
I don't know if you realize but your SVG is also changing size (specifically its width as you scroll)? (I added a blue border to the <div/>
and black border to the <svg/>
)
On load:
On scroll-in:
This is because you only fix the height on document load. If you ifx the width too, this problem goes away: https://codepen.io/Alexander9111/pen/poJzWEN
$(document).ready(function () {
console.log("document ready");
svgDrawing.setAttribute("height", window.innerHeight - 200);
svgDrawing.setAttribute("width", 1.5 * (window.innerHeight - 200));
svgDrawing.addEventListener('wheel', e => zoomInOut(e));
svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})
Then we can scroll without "growing" our width.
Is your desired outcome on double-click something like this:
ie any elements within as close to the edge of the svg as allows?
UPDATE
If you want to just return to original zoom, assuming you created the SVG with the original zoom first set up (hard-coded) etc. in the viewport etc.? Then you can simply save this original value and reset it when the user double clicks?
Demo: https://codepen.io/Alexander9111/pen/poJzWEN?editors=1010
JS:
const svgDrawing = document.getElementById('drawing');
// console.log(svgDrawing.currentTranslate);
let viewbox = svgDrawing.viewBox.baseVal;
const original_w = viewbox.width;
const original_h = viewbox.height;
const w_h_ratio = 2;
const g = document.getElementById('437');
console.log(g.transform.baseVal.consolidate().matrix);
var point = svgDrawing.createSVGPoint();
function zoomEvent(evt) {
evt.preventDefault();
if (evt.deltaY > 1) {
zoomInOut(-1, 10);
} else {
zoomInOut(+1, 10);
}
}
function zoomInOut(direction, stepSize) {
if (direction > 0) {
viewbox.width -= w_h_ratio * stepSize;
viewbox.height -= stepSize;
} else {
viewbox.width += w_h_ratio * stepSize;
viewbox.height += stepSize;
}
}
function zoomFull(evt){
evt.preventDefault();
viewbox.width = original_w;
viewbox.height = original_h;
console.log("Zoom 100%");
}
function elementIsInside(el, box){
var result = false;
el_rect = el.getBoundingClientRect();
point.x = el_rect.x;
point.y = el_rect.y;
point.width = el_rect.width;
point.height = el_rect.height;
var invertedSVGMatrix = el.getScreenCTM().inverse();
var p = point.matrixTransform(invertedSVGMatrix);
box_rect = box.getBoundingClientRect();
if (el_rect.left >= box_rect.left &&
el_rect.right <= box_rect.right &&
el_rect.bottom <= box_rect.bottom &&
el_rect.top >= box_rect.top){
result = true;
} else {
result = false;
}
// console.log("result_" + el.tagName, result)
return result;
}
$(document).ready(function () {
console.log("document ready");
svgDrawing.setAttribute("height", window.innerHeight - 200);
svgDrawing.setAttribute("width", w_h_ratio * (window.innerHeight - 200));
svgDrawing.addEventListener('wheel', e => zoomEvent(e));
svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})
UPDATE
I also tried to do the other idea (to get the max zoom that still shows all elements on-screen: https://codepen.io/Alexander9111/pen/NWqWEOw and I believe I have done it.
I added a red circle on the screen to show that we maximized the viewport to the maximum possible. ie:
OR
Wherever we move the elements around, the double-click event will fire the function that loops through the elements and saves the largest bottom and right points of the boundary boxes each element has and then alters the zoom port to that.
JS:
const svgDrawing = document.getElementById('drawing');
// console.log(svgDrawing.currentTranslate);
let viewbox = svgDrawing.viewBox.baseVal;
let original_w = viewbox.width;
let original_h = viewbox.height;
let w_h_ratio = 2;
const g = document.getElementById('437');
console.log(g.transform.baseVal.consolidate().matrix);
// const extra = document.getElementById('example');
// console.log(extra.transform.baseVal.consolidate().matrix);
const b_box = document.getElementById('bounding_box');
const b_box_max = {right: 0, bottom: 0};
var point = svgDrawing.createSVGPoint();
function zoomEvent(evt) {
evt.preventDefault();
if (evt.deltaY > 1) {
zoomInOut(-1, 10);
} else {
zoomInOut(+1, 10);
}
}
function zoomInOut(direction, stepSize) {
if (direction > 0) {
viewbox.width -= w_h_ratio * stepSize;
viewbox.height -= stepSize;
} else {
viewbox.width += w_h_ratio * stepSize;
viewbox.height += stepSize;
}
}
function zoomToSize(width, height) {
viewbox.width = width;
viewbox.height = height;
}
function zoomFull(evt){
evt.preventDefault();
viewbox.width = original_w;
viewbox.height = original_h;
console.log("Zoom 100%");
const elements = [...svgDrawing.childNodes];
console.log("elements", elements);
var inside = true;
for (let el of elements){
if (el.nodeName == "#text"){
//this is not a real node
} else if (elementIsInside(el, svgDrawing)){
inside = true;
} else {
inside = false;
}
}
// var dir = inside ? +1 : -1;
// zoomInOut(dir, (100/(10**i)));
zoomToSize(b_box_max.right,b_box_max.bottom);
}
function elementIsInside(el, box){
var result = false;
el_rect = el.getBoundingClientRect();
point.x = el_rect.right;
point.y = el_rect.bottom;
var invertedSVGMatrix = el.getScreenCTM().inverse();
var p = point.matrixTransform(invertedSVGMatrix);
let trans = el.transform.baseVal;
if (trans.length > 0){
const matrix = trans.consolidate().matrix;
p.x += matrix.e;
p.y += matrix.f;
}
if (p.x > b_box_max.right){
b_box.setAttribute('cx', p.x);
b_box_max.right = p.x;
b_box.setAttribute('cx', p.x);
}
if (p.y > b_box_max.bottom){
b_box.setAttribute('cy', p.y);
b_box_max.bottom = p.y;
b_box.setAttribute('cy', p.y);
}
b_box.setAttribute('r', 5);
console.log(p.x, p.y);
console.log(b_box_max.right,b_box_max.bottom);
box_rect = box.getBoundingClientRect();
if (el_rect.left >= box_rect.left &&
el_rect.right <= box_rect.right &&
el_rect.bottom <= box_rect.bottom &&
el_rect.top >= box_rect.top){
result = true;
} else {
result = false;
}
// console.log("result_" + el.tagName, result)
return result;
}
$(document).ready(function () {
console.log("document ready");
resizeSVG();
svgDrawing.addEventListener('wheel', e => zoomEvent(e));
svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})
$(window).resize(function() {
console.log($(window).width(), $(window).height());
// console.log(window.innerWidth, window.innerHeight);
resizeSVG();
});
function resizeSVG(){
if ((window.innerWidth - 40) / w_h_ratio > (window.innerHeight - 120)){
svgDrawing.setAttribute("height", window.innerHeight - 120);
svgDrawing.setAttribute("width", w_h_ratio * (window.innerHeight - 120));
} else{
svgDrawing.setAttribute("height", (window.innerWidth - 40) / w_h_ratio);
svgDrawing.setAttribute("width", (window.innerWidth - 40));
}
viewbox = svgDrawing.viewBox.baseVal;
original_w = viewbox.width;
original_h = viewbox.height;
w_h_ratio = 2;
}
Most important lines:
function zoomFull(evt){
evt.preventDefault();
viewbox.width = original_w;
viewbox.height = original_h;
console.log("Zoom 100%");
const elements = [...svgDrawing.childNodes];
console.log("elements", elements);
var inside = true;
for (let el of elements){
if (el.nodeName == "#text"){
//this is not a real node
} else if (elementIsInside(el, svgDrawing)){
inside = true;
} else {
inside = false;
}
}
zoomToSize(b_box_max.right,b_box_max.bottom);
}
Loop through all children elements in the svg and call the function elementIsInside()
:
function elementIsInside(el, box){
var result = false;
el_rect = el.getBoundingClientRect();
point.x = el_rect.right;
point.y = el_rect.bottom;
var invertedSVGMatrix = el.getScreenCTM().inverse();
var p = point.matrixTransform(invertedSVGMatrix);
let trans = el.transform.baseVal;
if (trans.length > 0){
const matrix = trans.consolidate().matrix;
p.x += matrix.e;
p.y += matrix.f;
}
if (p.x > b_box_max.right){
b_box.setAttribute('cx', p.x);
b_box_max.right = p.x;
b_box.setAttribute('cx', p.x);
}
if (p.y > b_box_max.bottom){
b_box.setAttribute('cy', p.y);
b_box_max.bottom = p.y;
b_box.setAttribute('cy', p.y);
}
b_box.setAttribute('r', 5);
console.log(p.x, p.y);
console.log(b_box_max.right,b_box_max.bottom);
box_rect = box.getBoundingClientRect();
if (el_rect.left >= box_rect.left &&
el_rect.right <= box_rect.right &&
el_rect.bottom <= box_rect.bottom &&
el_rect.top >= box_rect.top){
result = true;
} else {
result = false;
}
// console.log("result_" + el.tagName, result)
return result;
}
Translate bounding client rect from page coordinates to svg coordinates.
If the element has a transformation (like the group elements often do - such as transform="translate(200, 250)"
, then we also add these (matrix.e and matrix.f) onto our points. If the right is larger than the currently saved maximum right, then replace it, same for the bottom. Move the circle to this point to highlight.
UPDATE
https://codepen.io/Alexander9111/pen/NWqWEOw
You can also add a top left minimum point (shown here in green):
This comes from the following function, making a second point, p2:
function elementIsInside(el, box){
var result = false;
el_rect = el.getBoundingClientRect();
point.x = el_rect.right;
point.y = el_rect.bottom;
var invertedSVGMatrix = el.getScreenCTM().inverse();
var p = point.matrixTransform(invertedSVGMatrix);
point.x = el_rect.left;
point.y = el_rect.top;
var p2 = point.matrixTransform(invertedSVGMatrix);
let trans = el.transform.baseVal;
if (trans.length > 0){
const matrix = trans.consolidate().matrix;
p.x += matrix.e;
p.y += matrix.f;
p2.x += matrix.e;
p2.y += matrix.f;
}
if (p.x > b_box_max.right){
b_box_br.setAttribute('cx', p.x);
b_box_max.right = p.x;
}
if (p.y > b_box_max.bottom){
b_box_br.setAttribute('cy', p.y);
b_box_max.bottom = p.y;
}
b_box_br.setAttribute('r', 5);
if (p2.x < b_box_max.left){
b_box_tl.setAttribute('cx', p2.x);
b_box_max.left = p2.x;
}
if (p2.y < b_box_max.top){
b_box_tl.setAttribute('cy', p2.y);
b_box_max.top = p2.y;
}
b_box_tl.setAttribute('r', 5);
b_box_max.width = b_box_max.right - b_box_max.left;
b_box_max.height = b_box_max.bottom - b_box_max.top;
console.log(p2.x, p2.y, p.x, p.y);
console.log(b_box_max.right,b_box_max.bottom);
box_rect = box.getBoundingClientRect();
if (el_rect.left >= box_rect.left &&
el_rect.right <= box_rect.right &&
el_rect.bottom <= box_rect.bottom &&
el_rect.top >= box_rect.top){
result = true;
} else {
result = false;
}
// console.log("result_" + el.tagName, result)
return result;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.