svelte
svelte copied to clipboard
Transition:draw shows weird behaviour when vector-effect: non-scaling-stroke is applied.
Describe the bug
When applying vector-effect: non-scaling-stroke to a svg path, svelte's transition:draw shows weird behaviour. Instead of starting the animation from the beggining, it splits it into multiple parts, animating each part separately.
I noticed this behaviour while testing animating Rich Harris Pancake library, since it applies non-scaling-stroke on the charts. (https://github.com/Rich-Harris/pancake)
Reproduction
Line in blue doesn't have any vector effect (vector-effect: none).
Line in red has vector-effect: non-scaling-stroke applied to it.
https://svelte.dev/repl/177e5996f77e4870b1579bc91ecc53b3?version=3.44.0
Logs
No response
System Info
System:
OS: Windows 10 10.0.19043
CPU: (12) x64 AMD Ryzen 5 3600 6-Core Processor
Memory: 5.28 GB / 15.95 GB
Binaries:
Node: 14.8.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.4 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
npm: 6.14.7 - C:\Program Files\nodejs\npm.CMD
Browsers:
Chrome: 94.0.4606.81
Edge: Spartan (44.19041.1266.0), Chromium (94.0.992.50)
Internet Explorer: 11.0.19041.1202
npmPackages:
rollup: ^2.47.0 => 2.58.0
svelte: ^3.38.2 => 3.44.0
Severity
annoyance
As a pancake user, I started looking into this myself!
draw relies on node.getTotalLength() to provide the length of the path. However, a non-scaling-stroke scales the path to a new coordinate system.
I found that we can translate between the two coordinate systems by leveraging the fact that getBoundingClientRectangle returns to us the size in DOM units and getBBox returns the size in SVG units:
const { width: domWidth } = node.getBoundingClientRect();
const { width: svgWidth } = node.getBBox();
const scale = domWidth / svgWidth;
len = len * scale;
Note: I've literally never used it or heard of it before... but I noticed that
node.getCTM()actually returns the same scaling factor. That being said, I didn't pay attention in linear algebra, so whengetCTMtells me it returns a matrix... I run away.
It's not perfect for some reason. I'm still trying to figure that out. I went ahead and implemented by own version of the draw function for the REPL that you linked above, and as you can see, there's no weird artifacts (which means that it's good enough for my purposes), but the timing is still slightly off. ~I'll report back if I figure out why that is.~
EDIT: whoops, yep, figured out why that is. My solution seems to work in situations where the viewBox aspect ratio is the same as the DOM aspect ratio. (Updated REPL to demonstrate). Where that isn't the case, the vertical and horizontal scaling factors are different, and therefore, the path length calculation is different.