set focus to any node in large svg document

问题: I have a large svg document with lots of nodes, a brief example is below. Part of my requirements is to be able to have a menu where you can select one of the nodes which...

问题:

I have a large svg document with lots of nodes, a brief example is below.

Part of my requirements is to be able to have a menu where you can select one of the nodes which are hexagons in the example below and then the focus will be on that node or hexagon.

How can I position the document to any node.

For example, if I click the button in the example, how could I put the focus on the hexagon that is the furthest right?

I think I could use the viewBox but I'd like some pointers about how to achieve this.

I could use the minX and minY which are the first 2 arguments of the viewBox.

But how would I set these values?

Would I get the x and y from the getBBox values of the node?

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>


回答1:

I've added tabindex attributes to the <use> elements. This helps making those hexagons focusables.

When you click the button you focus the element whose's tabindex is (in this case) 20. Then when you click the tab key on your keyboard you move the focus to the next hexagon.

I set the viewBox attribute of your svg in vase of the value of the x attribute of the focused element.

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>


回答2:

If I understand correctly you want to zoom into a selected node. As you wrote, you can do it using viewBox. I've added the following snippet to your code:

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]);
});

It halves width and height of the viewBox: 600 275 vs original 1200 550. Basically it's a 2x zoom. And it changes the min-x and min-y of the viewBox to make the hexagon to roughly in the center of the 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>

  • 发表于 2019-01-23 02:49
  • 阅读 ( 172 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除