Home > front end >  SVG absolute transform list wrong value for text elements
SVG absolute transform list wrong value for text elements

Time:08-24

This question is based on a brilliant answer here.

I am trying to utilize the same concept for svg text element and I don't think it is returning the correct value. I am inheriting this svg and I can't do anything about the way the transform elements are applied. I was hoping javascript can return the absolute x and y value for each of the svg elements. For circle, it does an excellent job, but for text, it fails.

//build gridline with svg tooltip with "title" element
const widthPoints = [];
const heightPoints = [];

for (let i = 0; i <= 720; i  = 10) {
    heightPoints.push(i)
};

for (let i = 0; i <= 1280; i  = 10) {
    widthPoints.push(i)
};

const scaleY = d3.scaleLinear()
    .range([720, 0])
    .domain(d3.extent(heightPoints, d => d))

const scaleX = d3.scaleLinear()
    .range([0, 1280])
    .domain(d3.extent(widthPoints, d => d));

d3.select('svg')
    .append('g')
    .attr('class', 'yAxisLeft')
    .call(d3.axisLeft(scaleY)
        .ticks(heightPoints.length - 1)
        .tickSizeInner([(-(1280))])
    );

const newHeight = JSON.parse(JSON.stringify(heightPoints)).reverse();

d3.selectAll('body > svg > g.yAxisLeft > g.tick')
    .each(
        function(d, i) {
            const selection = d3.select(this);
            selection.selectAll('title')
                .data(function() {
                    return newHeight.filter((a, k) => k === i)
                })
                .join('title')
                .text((d) => {
                    return 'y Coordinate='   `${d}`
                })
        }
    );

d3.select('svg')
    .append('g').attr('class', 'xAxisBottom')
    .call(d3.axisBottom(scaleX)
        .ticks(widthPoints.length - 1)
        .tickSizeInner([(-(720))])
    )
    .style('transform', `translateY(720px)`)

d3.selectAll('body > svg > g.xAxisBottom > g.tick')
    .each(
        function(d, i) {
            const selection = d3.select(this);
            selection.selectAll('title')
                .data([d])
                .join('title')
                .text((d) => {
                    return 'x Coordinate='   `${d}`
                })
        }
    );
 
 // return x,y coordinate of each item as a result of all transform
 
 const svg = document.querySelector('svg');
const matrixViewBox = svg.getCTM();

const circCoord = (element) => {

    const matrixElement = element.getCTM();
    const x = element.cx.baseVal.value; // takes care of unit conversion
    const y = element.cy.baseVal.value;

    const matrixFromElementToVbox = matrixViewBox.inverse().multiply(matrixElement);
    const coord = new DOMPoint(x, y).matrixTransform(matrixFromElementToVbox);

    return coord;

};

const circleVal =[];

document.querySelectorAll('circle')
    .forEach(
        (a) => {
            circleVal.push(circCoord(a));
        }
    );

console.log(circleVal);

const textVal =[];


const textCoord = (element) => {
    const matrixElement = element.getCTM();
    const x = element.x.baseVal.value; // takes care of unit conversion
    const y = element.y.baseVal.value;

    const matrixFromElementToVbox = matrixViewBox.inverse().multiply(matrixElement);
    const coord = new DOMPoint(x, y).matrixTransform(matrixFromElementToVbox);

    return coord;
};

document.querySelectorAll('#textGroup > g > text')
    .forEach(
        (a) => {
            textVal.push(textCoord(a));
        }
    );
    
console.log(textVal);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 720">
<rect  width="1280" height="720" fill="#EFEFEF"></rect>
<g transform="translate(10,10)">
    <g transform="translate(100 100)">
        <circle  r="20" cx="25" cy="25" fill="green"
        transform="translate (100,400) scale(2)" />
    </g>
</g>
<g transform="translate(10,10)">
    <g transform="translate(100 100)">
        <circle  r="20" cx="25" cy="25" fill="red"
        transform="translate (-100,-40) scale(2)" />
    </g>
</g>
  <g id="textGroup" transform="translate(400,300)">
    <g >
      <text x="-247.890625" y="-60" transform="translate(-247.890625,-60)translate(247.890625,60)">P</text>
      <text x="-227.015625" y="-60" transform="translate(-227.015625,-60)translate(227.015625,60)">i</text>
      <text x="-210.09375" y="-60" transform="translate(-210.09375,-60)translate(210.09375,60)">z</text>
      <text x="-193.171875" y="-60" transform="translate(-193.171875,-60)translate(193.171875,60)">z</text>
      <text x="-181.296875" y="-60" transform="translate(-181.296875,-60)translate(181.296875,60)">a</text>     
    </g>
  </g>
</svg>
    <script type="text/javascript" src="prod.js">
    </script>
</body>

</html>

The values returned in textVal don't seem to be accurate. They all seem to be same while each of them has a different x

[
  {"x": 400,"y": 300,"z": 0,"w": 1},
  {"x": 400,"y": 300,"z": 0,"w": 1},
  {"x": 400,"y": 300,"z": 0,"w": 1},
  {"x": 400,"y": 300,"z": 0,"w": 1},
  {"x": 400,"y": 300,"z": 0,"w": 1}
]

CodePudding user response:

Text elements can have multiple x and y values so you want the one at index 0.

Additionally getCTM on the svg root element is never what you want. I assume you meant getScreenCTM instead.

//build gridline with svg tooltip with "title" element
const widthPoints = [];
const heightPoints = [];

for (let i = 0; i <= 720; i  = 10) {
    heightPoints.push(i)
};

for (let i = 0; i <= 1280; i  = 10) {
    widthPoints.push(i)
};

const scaleY = d3.scaleLinear()
    .range([720, 0])
    .domain(d3.extent(heightPoints, d => d))

const scaleX = d3.scaleLinear()
    .range([0, 1280])
    .domain(d3.extent(widthPoints, d => d));

d3.select('svg')
    .append('g')
    .attr('class', 'yAxisLeft')
    .call(d3.axisLeft(scaleY)
        .ticks(heightPoints.length - 1)
        .tickSizeInner([(-(1280))])
    );

const newHeight = JSON.parse(JSON.stringify(heightPoints)).reverse();

d3.selectAll('body > svg > g.yAxisLeft > g.tick')
    .each(
        function(d, i) {
            const selection = d3.select(this);
            selection.selectAll('title')
                .data(function() {
                    return newHeight.filter((a, k) => k === i)
                })
                .join('title')
                .text((d) => {
                    return 'y Coordinate='   `${d}`
                })
        }
    );

d3.select('svg')
    .append('g').attr('class', 'xAxisBottom')
    .call(d3.axisBottom(scaleX)
        .ticks(widthPoints.length - 1)
        .tickSizeInner([(-(720))])
    )
    .style('transform', `translateY(720px)`)

d3.selectAll('body > svg > g.xAxisBottom > g.tick')
    .each(
        function(d, i) {
            const selection = d3.select(this);
            selection.selectAll('title')
                .data([d])
                .join('title')
                .text((d) => {
                    return 'x Coordinate='   `${d}`
                })
        }
    );
 
 // return x,y coordinate of each item as a result of all transform
 
 const svg = document.querySelector('svg');
const matrixViewBox = svg.getScreenCTM();

const circCoord = (element) => {

    const matrixElement = element.getCTM();
    const x = element.cx.baseVal.value; // takes care of unit conversion
    const y = element.cy.baseVal.value;

    const matrixFromElementToVbox = matrixViewBox.inverse().multiply(matrixElement);
    const coord = new DOMPoint(x, y).matrixTransform(matrixFromElementToVbox);

    return coord;

};

const circleVal =[];

document.querySelectorAll('circle')
    .forEach(
        (a) => {
            circleVal.push(circCoord(a));
        }
    );

console.log(circleVal);

const textVal =[];


const textCoord = (element) => {
    const matrixElement = element.getCTM();
    const x = element.x.baseVal[0].value; // takes care of unit conversion
    const y = element.y.baseVal[0].value;

    const matrixFromElementToVbox = matrixViewBox.inverse().multiply(matrixElement);
    const coord = new DOMPoint(x, y).matrixTransform(matrixFromElementToVbox);

    return coord;
};

document.querySelectorAll('#textGroup > g > text')
    .forEach(
        (a) => {
            textVal.push(textCoord(a));
        }
    );
    
console.log(textVal);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 720">
<rect  width="1280" height="720" fill="#EFEFEF"></rect>
<g transform="translate(10,10)">
    <g transform="translate(100 100)">
        <circle  r="20" cx="25" cy="25" fill="green"
        transform="translate (100,400) scale(2)" />
    </g>
</g>
<g transform="translate(10,10)">
    <g transform="translate(100 100)">
        <circle  r="20" cx="25" cy="25" fill="red"
        transform="translate (-100,-40) scale(2)" />
    </g>
</g>
  <g id="textGroup" transform="translate(400,300)">
    <g >
      <text x="-247.890625" y="-60" transform="translate(-247.890625,-60)translate(247.890625,60)">P</text>
      <text x="-227.015625" y="-60" transform="translate(-227.015625,-60)translate(227.015625,60)">i</text>
      <text x="-210.09375" y="-60" transform="translate(-210.09375,-60)translate(210.09375,60)">z</text>
      <text x="-193.171875" y="-60" transform="translate(-193.171875,-60)translate(193.171875,60)">z</text>
      <text x="-181.296875" y="-60" transform="translate(-181.296875,-60)translate(181.296875,60)">a</text>     
    </g>
  </g>
</svg>
    <script type="text/javascript" src="prod.js">
    </script>
</body>

</html>

  • Related