I have an horizontally responsive SVG path that stretch without keeping its ratio, and it’s working when using preserveAspectRatio="none"
on the tag and vector-effect="non-scaling-stroke"
on the path to keep it intact. But I also need to animate an element along that same path without losing its aspect ratio.
Here is an example and as you can see it’s working if you resize horizontally the wrapper but not for the star that loses its aspect-ratio caused by the global preserveAspectRatio="none"
attribute. I can’t find a workaround for that and have both the path horizontally responsive and keep the star intact and following the path…
.wrapper {
width: 100%;
min-width: 200px;
max-width: 100%;
height: 200px;
resize: horizontal;
overflow: hidden;
border: 1px solid blue;
}
svg {
width: 100%;
height: 100%;
}
.path {
fill: none;
stroke: black;
stroke-width: 2px;
stroke-dasharray: 6 6;
stroke-linecap:round;
}
.star {
fill: red;
}
<div class="wrapper">
<svg viewBox="0 0 400 200" preserveAspectRatio="none">
<defs>
<path id="star" d="M0,24.675L2.679,32.921L11.349,32.921L4.335,38.017L7.014,46.262L0,41.166L-7.014,46.262L-4.335,38.017L-11.349,32.921L-2.679,32.921L0,24.675Z"/>
<path id="path" d="M0,36.608C0,36.608 64.096,15.519 92.956,48.531C121.816,81.542 101.293,129.283 74.824,115.941C48.354,102.599 68.017,24.7 188.557,73.454C309.097,122.207 261.935,170.513 400,175.664" vector-effect="non-scaling-stroke"/>
</defs>
<use href="#path" class="path"/>
<use href="#star" class="star" x="0" y="-36">
<animateMotion dur="10s" repeatCount="indefinite" rotate="auto">
<mpath href="#path"/>
</animateMotion>
</use>
</svg>
</div>
2
Answers
I though I had a great idea by replacing the original svg star with a star-anchor element. So that I could have a second SVG, which contains the actual star that keeps its aspect ratio, follow the star-anchor.
Feel free to have a look. Perhaps its giving you any ideas.
But I don’t think the edges on this approach can be smoothed out in a satisfying manner.
Here is the code example for how to calculate the distorted path in JS.
Your path is using cubic-bezier coordinates. I do not yet fully understand how they work. Therefore my example uses line coordinates. I think that makes it easier to understand anyways.
Unfortunately we can’t change the track of a motion path via
transform
or prevent unproportional scaling for specific elements.A workaround could be to update the motion path on resize:
ResizeObserver
scalePathData()
)We can’t specify a fixed
viewBox
– otherwise the animated element would be scaled/"squeezed" as well.Basically we’re updating scaleX and scaleY values on resize based on a comparison between the original motion path bounding box and the resized SVG aspect ratio.
The above example is based on the SVGPathData interface draft structure parsed via Jarek Foksa’s polyfill.
See also "css-tricks.com:Create a Responsive CSS Motion Path? Sure We Can!"