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>