Reputation: 5235
I would like to insert a path on another path ( with progression param ) which I have already applied a transform
attribute.
The problem is that when I get point position from getPointAtLength(//h2 path//)
the x
and y
params are not the same as the position of h2.
So they get positioned out of the box.
let paths = d3.select('svg').select("#h2").selectAll('path.train').data([1,2]);
let branch=d3.select('svg').select("#h2").select('path')
paths.enter().append('path')
.attr('d', d3.symbol().type(d3.symbolTriangle))
.attr('class', 'train')
.attr('fill','black')
.attr('transform',(d,i)=> {
let point = branch.node()
.getPointAtLength(30)
return `translate(${point.x},${point.y}`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.0/d3.min.js"></script>
<svg viewBox="0 0 388.71 412.14">
<g data-id="branch" id="h2" transform="translate(-885 -562) ">
<g class="branch-label" id="h2select" transform="translate(-240 -340)"></g>
<rect fill="rgba(255,255,255,1)" height="11" id="h2" width="12"></rect>
<path d="M1093.763,1595.658 v -82.417 c 0,0 5,-14.987 -18.452,-16.644 -23.452,-1.657 -40.9,2.386 -54.093,-11.537 -13.193,-13.923 -132.873,-159.193 -132.873,-159.193 0,0 -6.456,-10.249 -24.986,-14.661 -18.53,-4.412 -11.029,-16.736 -2.10392,-28.6309 2.68431,-3.5775 12.32475,-15.4715 21.44325,-26.363" data-name="h2" fill="none" id="h2-3" stroke="#efcf2f" stroke-linecap="round" stroke-width="2" transform="translate(208.67 -656.38)" style="opacity: 1;"></path>
</g>
</svg>
What I want is how to position the triangle at 30% of the path without deleting `transform attributes on svg ?
Upvotes: 0
Views: 180
Reputation: 4241
We can "wrap" the path in a g element and give the g element the transform the path had and remove it from the path?
Then your approach should work?
var child = document.getElementById('h2-3');
var parent = child.parentNode;
var i = Array.prototype.indexOf.call(parent.children, child);
console.log(i);
child = parent.removeChild(child);
var g = document.createElementNS('http://www.w3.org/2000/svg','g');
g.setAttribute('transform', child.getAttribute('transform'));
child.removeAttribute('transform');
g.appendChild(child);
if (i < parent.childNodes.length) {
parent.insertBefore(g, parent.childNodes[i]);
} else {
parent.appendChild(g);
}
const len = child.getTotalLength();
//console.log(len)
const point = child.getPointAtLength(0.99*len);
var circle = document.createElementNS('http://www.w3.org/2000/svg','circle')
circle.setAttribute('cx', point.x);
circle.setAttribute('cy', point.y);
circle.setAttribute('r', 10);
circle.setAttribute('fill', 'black');
g.appendChild(circle);
var myInterval;
var currProgress = 0.995;
var direction = -1;
var count = 0;
myInterval = setInterval(myAnimate, 50);
function myAnimate() {
if (currProgress <= 0.0 || currProgress >= 1.0) {
direction *= -1;
}
//console.log(currProgress);
const myPoint = child.getPointAtLength(currProgress * len);
circle.setAttribute("cx", myPoint.x);
circle.setAttribute("cy", myPoint.y);
currProgress += direction * 0.005;
if (count >= 5) {
clearInterval(myInterval);
}
}
/*
let paths = d3.select('svg').select("#h2").selectAll('path.train').data([1,2]);
let branch = d3.select('svg').select("#h2").select('path')
paths.enter().append('path')
.attr('d', d3.symbol().type(d3.symbolTriangle))
.attr('class', 'train')
.attr('fill','black')
.attr('transform',(d,i)=> {
let point = branch.node()
.getPointAtLength(30)
return `translate(${point.x},${point.y}`
})
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.0/d3.min.js"></script>
<svg viewBox="0 0 450 500">
<g data-id="branch" id="h2" transform="translate(-885 -562) ">
<g class="branch-label" id="h2select" transform="translate(-240 -340)"></g>
<rect fill="rgba(255,255,255,1)" height="11" id="h2" width="12"></rect>
<path d="M1093.763,1595.658 v -82.417 c 0,0 5,-14.987 -18.452,-16.644 -23.452,-1.657 -40.9,2.386 -54.093,-11.537 -13.193,-13.923 -132.873,-159.193 -132.873,-159.193 0,0 -6.456,-10.249 -24.986,-14.661 -18.53,-4.412 -11.029,-16.736 -2.10392,-28.6309 2.68431,-3.5775 12.32475,-15.4715 21.44325,-26.363" data-name="h2" fill="none" id="h2-3" stroke="#efcf2f" stroke-linecap="round" stroke-width="2" transform="translate(208.67 -656.38)" style="opacity: 1;"></path>
</g>
</svg>
We can do this with the following JS:
var child = document.getElementById('h2-3');
var parent = child.parentNode;
var i = Array.prototype.indexOf.call(parent.children, child);
console.log(i);
child = parent.removeChild(child);
var g = document.createElementNS('http://www.w3.org/2000/svg','g');
g.setAttribute('transform', child.getAttribute('transform'));
child.removeAttribute('transform');
g.appendChild(child);
if (i < parent.childNodes.length) {
parent.insertBefore(g, parent.childNodes[i]);
} else {
parent.appendChild(g);
}
With this you "wrap" the path in a g element and give the g element the transform the path had and remove it from the path.
Resulting DOM is:
And then we can place a circle at 30% of the path length with the following JS:
const len = child.getTotalLength();
console.log(len)
const point = child.getPointAtLength(0.3*len);
var circle = document.createElementNS('http://www.w3.org/2000/svg','circle')
circle.setAttribute('cx', point.x);
circle.setAttribute('cy', point.y);
circle.setAttribute('r', 10);
circle.setAttribute('fill', 'black');
g.appendChild(circle);
Then we get this as the output (you can play with child.getPointAtLength(0.3*len)
with other values like 0.6*len to see the circle at other positions):
UPDATE
Actually, I noticed some of your path is cut off as it is outside of the svg viewbox so I modified the viewbox like so:
<svg viewBox="0 0 450 500">
Then I also added some animation code, because, why not!
var myInterval;
var currProgress = 0.995;
var direction = -1;
var count = 0;
myInterval = setInterval(myAnimate, 50);
function myAnimate() {
if (currProgress <= 0.0 || currProgress >= 1.0) {
direction *= -1;
}
//console.log(currProgress);
const myPoint = child.getPointAtLength(currProgress * len);
circle.setAttribute("cx", myPoint.x);
circle.setAttribute("cy", myPoint.y);
currProgress += direction * 0.005;
if (count >= 5) {
clearInterval(myInterval);
}
}
Result is:
Upvotes: 1