In svg, I add transition: all 3s linear
to both <rect>
and <path>
, and then click a button to make the <rect>
and <path>
repeatedly and simultaneously move the same distance between two positions, but during the movement, why doesn't the relative distance between <rect>
and <path>
stay the same?
Browser: Chrome
Demo running with Chrome:
const svg = document.querySelector("#svg");
const path = svg.querySelector("path");
const rect = svg.querySelector("g");
const btn = document.querySelector("#btn");
const paths = [
'M 29 12.6 h 30',
'M 29 32.6 h 30',
]
const hs = [2, 22];
let i = 0;
btn.onclick = () => {
path.setAttribute("d", paths[i]);
rect.setAttribute("transform", `translate(0,${hs[i]})`);
i ^= 1;
}
svg * {
transition: all 3s linear;
}
<svg id="svg" width="200" height="200" viewbox="0 -10 50 50">
<path fill="none" stroke="#ff6699" d="M 29 12.6 h 30"></path>
<g transform="translate(0,2)">
<rect x="29" y="0" width="30" height="10"></rect>
</g>
</svg>
<br>
<button id="btn">Click quickly and repeatedly</button>
Edit: The following answer only applies to the Firefox behavior, where transform
is not animated. Chrome shows a different problem, and it seems that was what you asked about.
transition
is a CSS property, but you try to animate presentation attributes. That seems to work for d
(amazingly, in my opinion), but not for transform
, where there is no transition and the value changes instantaneously.
While the spec tries to give the impression that presentation attributes and CSS properties are freely interchangable, in practice you get some rough edges again and again. I am not going to try to find out if what you tried to do is supposed to work according to spec. In my experience it is just sensible to stay on one side of the fence. So, if you try to animate something with the CSS transition
property, that something should also be written as a CSS property.
Instead of transform="translate(0,2)"
, use style="transform:translate(0,2px)"
- take care to add the px
unit.
const svg = document.querySelector("#svg");
const path = svg.querySelector("path");
const rect = svg.querySelector("g");
const btn = document.querySelector("#btn");
const paths = [
'M 29 12.6 h 30',
'M 29 32.6 h 30',
]
const hs = [2, 22];
let i = 0;
btn.onclick = () => {
path.setAttribute("d", paths[i]);
rect.style.transform = `translate(0,${hs[i]}px)`;
i ^= 1;
}
svg * {
transition: all 3s linear;
}
<svg id="svg" width="200" height="200" viewbox="0 -10 50 50">
<path fill="none" stroke="#ff6699" d="M 29 12.6 h 30"></path>
<g style="transform:translate(0,2px)">
<rect x="29" y="0" width="30" height="10"></rect>
</g>
</svg>
<br>
<button id="btn">Click quickly and repeatedly</button>