Установить фокус на любой узел в большом svg-документе
У меня есть большой svg-документ с множеством узлов, краткий пример ниже.
Частью моих требований является наличие меню, в котором вы можете выбрать один из узлов, которые являются шестиугольниками, а затем, чтобы фокус переместился на этот узел или шестиугольник.
Как мне разместить документ на любом узле?
Например, если я нажму кнопку в этом примере, как я могу установить фокус на шестиугольнике, который находится дальше всего справа?
Думаю, я мог бы использовать viewBox, но мне бы хотелось узнать, как этого добиться.
Я мог бы использовать minX и minY, которые являются первыми двумя аргументами viewBox.
Но как мне установить их значения значения?
Могу ли я получить x и y из значений getBBox() узла?
const SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_XLINK = "http://www.w3.org/1999/xlink";
let A = -1200;
let B = 1200;
let hexArray = []
function drawHexagon(r){
let points = "";
for( let i = 1; i <= 6; i++ ){
let a = i * ( Math.PI / 3 );
let x = (r * Math.cos( a - Math.PI/2 )).toFixed(3);
let y = (r * Math.sin( a - Math.PI/2)).toFixed(3);
points += `${x},${y} `;
}
return points;
}
function useHex(theParent,pos){
let use = document.createElementNS(SVG_NS, 'use');
use.setAttributeNS(SVG_XLINK, 'xlink:href', '#theHex');
use.setAttributeNS(null,"x",pos.x);
use.setAttributeNS(null,"y",pos.y);
//use.setAttribute("title",'x value:'+pos.x);
theParent.appendChild(use);
hexArray.push(use);
drawText('x value:'+pos.x,pos)
}
function drawText(val,pos){
let txt = document.createElementNS(SVG_NS, 'text');
txt.setAttributeNS(null,"x",pos.x);
txt.setAttributeNS(null,"y",pos.y);
txt.textContent = val;
txt.setAttributeNS(null,"transform",`translate(0,30) rotate(-75 ${pos.x},${pos.y})`);
textParent.appendChild(txt);
}
function connector(parent,p){
let path = document.createElementNS(SVG_NS, 'path');
let d =`M${p.x},${p.y}C${p.x},125 0,125 0,0`;
path.setAttributeNS(null,"d",d);
parent.appendChild(path);
}
for(let x = A; x <= B; x+=50){
let pos = {x:x,y:250}
useHex(useParent,pos);
connector(connectors,pos);
}
document.querySelector('button').addEventListener("click",()=>{
alert('how do I focus on a node, including those that are off screen');
});
svg {
border: 1px solid;
}
use {
fill: white;
stroke: #000;
}
#itr {
width: 500px;
display: block;
margin: 2em auto;
}
#connectors path {
fill: none;
stroke: black;
}
#tooltip {
position: absolute;
}
text {
dominant-baseline: middle;
text-anchor: end;
}
<button type="button">select node</button>
<svg id="svg" viewBox="-600 -50 1200 550" style="--display:block;">
<defs><polygon id="theHex" points="21.651,-12.500 21.651,12.500 0.000,25.000 -21.651,12.500 -21.651,-12.500 -0.000,-25.000 " ></polygon>
</defs>
<g id="connectors">
</g>
<g id="useParent">
<use xlink:href="#theHex" y="0" />
</g>
<g id="textParent">
</g>
</svg>
Свободный перевод вопроса set focus to any node in large svg document от участника @dagda1.
Ответы (2 шт):
Я добавил атрибуты tabindex к элементам <use>. Это помогает сфокусировать эти шестиугольники.
Когда вы нажимаете кнопку, вы фокусируетесь на элементе, tabindex которого (в данном случае) равен 20. Затем, когда вы нажимаете клавишу табуляции на клавиатуре, вы перемещаете фокус на следующий шестиугольник.
Я установил атрибут viewBox вашего svg в зону значения атрибута x сфокусированного элемента.
const SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_XLINK = "http://www.w3.org/1999/xlink";
let A = -1200;
let B = 1200;
let hexArray = []
function drawHexagon(r){
let points = "";
for( let i = 1; i <= 6; i++ ){
let a = i * ( Math.PI / 3 );
let x = (r * Math.cos( a - Math.PI/2 )).toFixed(3);
let y = (r * Math.sin( a - Math.PI/2)).toFixed(3);
points += `${x},${y} `;
}
return points;
}
function useHex(theParent,pos){
let use = document.createElementNS(SVG_NS, 'use');
use.setAttributeNS(SVG_XLINK, 'xlink:href', '#theHex');
use.setAttributeNS(null,"x",pos.x);
use.setAttributeNS(null,"y",pos.y);
use.setAttributeNS(null,"tabindex",pos.i);
theParent.appendChild(use);
hexArray.push(use);
drawText('x value:'+pos.x,pos)
}
function drawText(val,pos){
let txt = document.createElementNS(SVG_NS, 'text');
txt.setAttributeNS(null,"x",pos.x);
txt.setAttributeNS(null,"y",pos.y);
txt.textContent = val;
txt.setAttributeNS(null,"transform",`translate(0,30) rotate(-75 ${pos.x},${pos.y})`);
textParent.appendChild(txt);
}
function connector(parent,p){
let path = document.createElementNS(SVG_NS, 'path');
let d =`M${p.x},${p.y}C${p.x},125 0,125 0,0`;
path.setAttributeNS(null,"d",d);
parent.appendChild(path);
}
for(let x = A,i=0; x <= B; x+=50,i++){
let pos = {x:x,y:250,i:i}
useHex(useParent,pos);
connector(connectors,pos);
}
let theButton = document.querySelector('[type=button]')
theButton.addEventListener("click",()=>{
let index = theButton.value;
let active = document.querySelector(`[tabindex = '${index}']`)
active.focus()
//alert('how do I focus on a node, including those that are off screen');
});
// event listener for keyup
function checkTabPress(e) {
if (e.keyCode == 9) {
let activeElement = document.activeElement;
let posX = activeElement.getAttribute("x");
svg.setAttributeNS(null, "viewBox", `${posX - 600} -50 1200 550`);
}
}
document.querySelector('body').addEventListener('keyup', checkTabPress);
svg {
border: 1px solid;
}
use {
fill: white;
stroke: #000;
}
#connectors path {
fill: none;
stroke: black;
}
#tooltip {
position: absolute;
}
text {
dominant-baseline: middle;
text-anchor: end;
}
use:focus{fill:red}
<button type="button" value="20">select node</button>
<svg id="svg" viewBox="-600 -50 1200 550" style="--display:block;">
<defs><polygon id="theHex" points="21.651,-12.500 21.651,12.500 0.000,25.000 -21.651,12.500 -21.651,-12.500 -0.000,-25.000 " ></polygon>
</defs>
<g id="connectors">
</g>
<g id="useParent">
<use xlink:href="#theHex" y="0" />
</g>
<g id="textParent">
</g>
</svg>
Если я правильно понимаю, вы хотите увеличить выбранный узел. Как вы писали, это можно сделать с помощью viewBox. Я добавил в ваш код следующий фрагмент:
var svg = document.getElementById('svg')
document.getElementById('useParent').addEventListener('click', evt => {
if(evt.target.nodeName === 'use') {
focusNode(evt.target);
}
});
function focusNode(node) {
var bbox = node.getBBox()
svg.setAttribute('viewBox', `${bbox.x - 250} ${bbox.y - 30} 600 275`)
node.setAttribute('style', 'fill:red;');
//TODO: need to clear the previous selection
}
document.querySelector('button').addEventListener("click",()=>{
focusNode(hexArray[0]);
});
Ширина и высота viewBox уменьшаются вдвое: 600 275 против оригинального 1200 550. В основном это двукратный зум. И он изменяет min-x и min-y viewBox, чтобы шестиугольник оказался примерно в центре SVG.
const SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_XLINK = "http://www.w3.org/1999/xlink";
let A = -1200;
let B = 1200;
let hexArray = []
function drawHexagon(r){
let points = "";
for( let i = 1; i <= 6; i++ ){
let a = i * ( Math.PI / 3 );
let x = (r * Math.cos( a - Math.PI/2 )).toFixed(3);
let y = (r * Math.sin( a - Math.PI/2)).toFixed(3);
points += `${x},${y} `;
}
return points;
}
function useHex(theParent,pos){
let use = document.createElementNS(SVG_NS, 'use');
use.setAttributeNS(SVG_XLINK, 'xlink:href', '#theHex');
use.setAttributeNS(null,"x",pos.x);
use.setAttributeNS(null,"y",pos.y);
//use.setAttribute("title",'x value:'+pos.x);
theParent.appendChild(use);
hexArray.push(use);
drawText('x value:'+pos.x,pos)
}
function drawText(val,pos){
let txt = document.createElementNS(SVG_NS, 'text');
txt.setAttributeNS(null,"x",pos.x);
txt.setAttributeNS(null,"y",pos.y);
txt.textContent = val;
txt.setAttributeNS(null,"transform",`translate(0,30) rotate(-75 ${pos.x},${pos.y})`);
textParent.appendChild(txt);
}
function connector(parent,p){
let path = document.createElementNS(SVG_NS, 'path');
let d =`M${p.x},${p.y}C${p.x},125 0,125 0,0`;
path.setAttributeNS(null,"d",d);
parent.appendChild(path);
}
for(let x = A; x <= B; x+=50){
let pos = {x:x,y:250}
useHex(useParent,pos);
connector(connectors,pos);
}
var svg = document.getElementById('svg')
document.getElementById('useParent').addEventListener('click', evt => {
if(evt.target.nodeName === 'use') {
focusNode(evt.target)
}
});
function focusNode(node) {
var bbox = node.getBBox()
svg.setAttribute('viewBox', `${bbox.x - 250} ${bbox.y - 30} 600 275`)
node.setAttribute('style', 'fill:red;')
}
document.querySelector('button').addEventListener("click",()=>{
focusNode(hexArray[0]);
});
svg {
border: 1px solid;
}
use {
fill: white;
stroke: #000;
}
#itr {
width: 500px;
display: block;
margin: 2em auto;
}
#connectors path {
fill: none;
stroke: black;
}
#tooltip {
position: absolute;
}
text {
dominant-baseline: middle;
text-anchor: end;
}
<button type="button">select 1st node</button>
<svg id="svg" viewBox="-600 -50 1200 550" style="--display:block;">
<defs><polygon id="theHex" points="21.651,-12.500 21.651,12.500 0.000,25.000 -21.651,12.500 -21.651,-12.500 -0.000,-25.000 " ></polygon>
</defs>
<g id="connectors">
</g>
<g id="useParent">
<use xlink:href="#theHex" y="0" />
</g>
<g id="textParent">
</g>
</svg>