1

I have pink circle and an eye on the svg path, like the following:

The circle and an eye should rotate over the first grey circle. How can I do that?

It is my inspiration (codepen).

.ball {
  width: 20px;
  height: 20px;
  background-color: green;
  border-radius: 50%;
  offset-path: path('M366.307,583.8c0,322.145-81.9,583.3-182.9,583.3S.5,905.94.5,583.8a1782.186,1782.186,0,0,1,9.4-185.08C34.193,167.219,102.7.5,183.407.5,284.409.5,366.307,261.65,366.307,583.8Z');
  offset-distance: 0%;
  animation: red-ball 5s linear alternate infinite;
  transform: translate(0, -45%);
}

@keyframes red-ball {
  from {
    offset-distance: 0%;
  }
  to {
    offset-distance: 100%;
  }
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1339.88" height="1339.88" viewBox="0 0 1339.88 1339.88">
     <defs>
      <clipPath id="clip-path">
      <rect id="Rectangle_119" data-name="Rectangle 119" width="721.865" height="1173.012" fill="none" stroke="#acb4ae" stroke-width="1"/>
      </clipPath>
      <clipPath id="clip-path-2">
      <rect id="Rectangle_527" data-name="Rectangle 527" width="130.25" height="130.225" fill="none" stroke="#b4b4b4" stroke-width="1"/>
      </clipPath>
    </defs>
    <g id="Sekcja_2" data-name="Sekcja 2" transform="translate(-300.09 -1206.12)">
      <g id="Group_397" data-name="Group 397" transform="translate(300.09 1716.556) rotate(-45)" opacity="0.276">
        <g id="Group_79" data-name="Group 79" clip-path="url(#clip-path)">
        <path id="Path_365" data-name="Path 365" d="M366.307,583.8c0,322.145-81.9,583.3-182.9,583.3S.5,905.94.5,583.8a1782.186,1782.186,0,0,1,9.4-185.08C34.193,167.219,102.7.5,183.407.5,284.409.5,366.307,261.65,366.307,583.8Z" transform="translate(2.711 2.711)" fill="none" stroke="#acb4ae" stroke-miterlimit="10" stroke-width="1"/>
        <path id="Path_366" data-name="Path 366" d="M393.529,583.8c0,322.145-81.891,583.3-182.9,583.3S27.722,905.94,27.722,583.8,109.613.5,210.629.5,393.529,261.65,393.529,583.8Z" transform="translate(150.304 2.711)" fill="none" stroke="#acb4ae" stroke-miterlimit="10" stroke-width="1"/>
        <path id="Path_367" data-name="Path 367" d="M420.752,583.8c0,322.145-81.885,583.3-182.9,583.3S54.945,905.94,54.945,583.8,136.836.5,237.852.5,420.752,261.65,420.752,583.8Z" transform="translate(297.902 2.711)" fill="none" stroke="#acb4ae" stroke-miterlimit="10" stroke-width="1"/>
      </g>
    </g>
    <g id="Group_464" data-name="Group 464">
      <circle id="Ellipse_6" data-name="Ellipse 6" cx="10" cy="10" r="10" transform="translate(596 1997)" fill="#da2e68"/>
      <g id="Group_452" data-name="Group 452" transform="translate(-1031.676 1629.774)">
        <g id="Group_442" data-name="Group 442" transform="translate(1637.676 391)">
          <g id="Group_441" data-name="Group 441" clip-path="url(#clip-path-2)">
            <g id="Ellipse_29" data-name="Ellipse 29" transform="translate(3.439 3.427)" fill="#fff" stroke="#aab5ad" stroke-width="1">
              <circle cx="61.686" cy="61.686" r="61.686" stroke="none"/>
              <circle cx="61.686" cy="61.686" r="61.186" fill="none"/>
            </g>
            <path id="Path_1161" data-name="Path 1161" d="M16.351,0A16.351,16.351,0,1,1,0,16.351,16.351,16.351,0,0,1,16.351,0Z" transform="translate(48.774 48.762)" fill="#fff"/>
            <path id="Union_1" data-name="Union 1" d="M.408,35.239H0a70.5,70.5,0,0,1,122.1,0h.133a70.449,70.449,0,0,1-61.18,35.467C34.975,70.706,12.6,56.29.408,35.239Z" transform="translate(4.062 29.304)" fill="none" stroke="#b4b4b4" stroke-width="1"/>
            <path id="Path_1159" data-name="Path 1159" d="M66.2,94.029a27.783,27.783,0,1,1,10.9-2.2,28.012,28.012,0,0,1-10.9,2.2m0-43.051h0c-1.193,4.678-2.3,7.807-4.446,9.949s-5.272,3.252-9.95,4.444c4.679,1.193,7.808,2.3,9.95,4.447s3.253,5.271,4.446,9.95c1.192-4.678,2.3-7.807,4.444-9.95s5.271-3.253,9.95-4.447c-4.679-1.192-7.808-2.3-9.95-4.444s-3.252-5.272-4.444-9.949" transform="translate(-1.215 -1.212)" fill="#9bd698"/>
          </g>
        </g>
      </g>
      </g>
    </g>
    </svg>
<div class="ball"></div>
<div class="eye"></div>

The green ball should replace the pink and the eye also should rotate. Animation should be infinite, not bouncing like now.

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
Natalia
  • 1,009
  • 2
  • 11
  • 24
  • You might e.g. wrap your svg and circle div in a relative div and give your animated circle an absolute postioning (similar to the linked codepen example). But it's probably easier to animate the svg elements directly instead of HTML DOM elements – this way you don't need to align the HTML elements to your svg. See also ["How move/animate multiple circles along motion path by SVG Animation"](https://stackoverflow.com/questions/73135195/how-move-animate-multiple-circles-along-motion-path-by-svg-animation) – herrstrietzel May 31 '23 at 17:33
  • I added position absolute to svg and does not work. – Natalia Jun 01 '23 at 03:29

1 Answers1

0

You're facing several problems:

  1. your "orbit" motion paths are heavily transformed i.e rotated and translated with x/y offsets: so aligning them in the desired way gets more complicated – you can "de-transform" them
  2. browser support for offset-path is still (2023) rather spotty – safari/webkit seems to be working on it but ... nay! Won't work properly for quite a lot of iphone/ipad users – svg SMIL animations provide a better cross-browser compatibility
  3. aligning HTML DOM elements to a background svg graphic is tricky – it's way easier to animate elements within the svg DOM (especially with regards to responsive layouts).

Convert path transformations to hard coded path data

A common practice:

  1. open your svg in Inkscape select all elements and

  2. ungroup the selection until you see a bounding box for each element.
    See also this SO post: "Removing transforms in SVG files"

  3. Save the new svg again – always keep the original file in case the processed/optimized file is missing crucial data, that could be retained with more fine tuned export parameters.

Be careful: Quite likely, some attributes will get stripped from the markup – especially if you've already manually edited your svg markup e.g adding custom data attributes or classes.
On the other hand – graphic applications tend to generate rather bloated svg code including way to many unnecessary <g> group nestings.

Cleanup

As mentioned before, the previous conversion might strip some attributes. However, this is a good chance to manually optimize your svg markup e.g by assigning more semantic/self-explanatory IDs or class names.

svg{
  height:90vh;
  width: auto;
  border: 1px solid #ccc;
}
<svg viewBox="0 0 1340 1340" xmlns="http://www.w3.org/2000/svg">
  <g class="orbits" fill="none" stroke="#acb4ae" stroke-miterlimit="10">
    <path id="orbit1" stroke="red" d="m676 664c228 228 355 470 283 542-71.4 71.4-314-55.3-542-283a1782 1782 0 01-124-138c-147-181-216-347-159-404 71.4-71.4 314 55.3 542 283z" opacity=".276" />
    <path id="orbit2" d="m799 541c228 228 355 470 283 542-71.4 71.4-314-55.3-542-283s-355-470-283-542c71.4-71.4 314 55.3 542 283z" opacity=".276"/>
    <path id="orbit3" d="m923 417c228 228 355 470 283 542s-314-55.3-542-283c-228-228-355-470-283-542 71.4-71.4 314 55.3 542 283z" opacity=".276" />
  </g>

  <circle id="planet1" cx="0" cy="0" r="10" fill="#da2e68" />
  
  <!-- 1. animation-->
  <animateMotion href="#planet1" dur="5s" begin="-0.5s" repeatCount="indefinite" rotate="auto">
    <mpath href="#orbit1" />
  </animateMotion>
  
  <g id="eye" transform="translate(-371 -880)">
    <circle cx="371" cy="880" r="61.7" fill="#fff" />
    <circle cx="371" cy="880" r="61.2" fill="none" stroke="#aab5ad" />
    <path d="m371 863a16.4 16.4 0 11-16.4 16.4 16.4 16.4 0 0116.4-16.4z" fill="#fff" />
    <path d="m310 879h-.408a70.5 70.5 0 01122 0h.133a70.4 70.4 0 01-61.2 35.5c-26.1 0-48.5-14.4-60.6-35.5z" fill="none" stroke="#b4b4b4" />
    <path d="m371 907a27.8 27.8 0 1110.9-2.2 28 28 0 01-10.9 2.2m0-43.1c-1.19 4.68-2.3 7.81-4.45 9.95s-5.27 3.25-9.95 4.44c4.68 1.19 7.81 2.3 9.95 4.45s3.25 5.27 4.45 9.95c1.19-4.68 2.3-7.81 4.44-9.95s5.27-3.25 9.95-4.45c-4.68-1.19-7.81-2.3-9.95-4.44s-3.25-5.27-4.44-9.95" fill="#9bd698" />
  </g>
  
  <!-- 2. animation-->
  <animateMotion href="#eye" dur="5s" begin="0s" repeatCount="indefinite" rotate="0" >
    <mpath href="#orbit1" />
  </animateMotion>
</svg>

E.g. you could assign names like "oribt1-3" to each motion path and something like "planet1-3" to the animated objects.

Animate with svg SMIL

Now we can connect/bind animated elements and motion paths in a more comprehensible way.

  <animateMotion href="#planet1" dur="5s" begin="-0.5s" repeatCount="indefinite" rotate="auto">
    <mpath href="#orbit1" />
  </animateMotion>

Align elements to motion paths

Animated elements need to be (re)positioned at the svg's origin point since the initial coordinates of an animated element are treated as offset values while moving around the motion path.

For instance: the pink circle has a layout position of cx=10 cy=10 (fortunately, we've converted the transformation inherited from the parent groups before).
Now we can adjust the circle's center to cx="0" and cy="0".

Realigning the "eye" group can be done by checking the groups bounding box via natively supported JS method getBBox() and applying a compensating transformation to center it around the svg origin point:

let bb = eye.getBBox();
let offsetX = bb.x-bb.width/2
let offsetY = bb.y+bb.height/2

applying

transform="translate(-371 -880)"

will reset the initial position of this element and make this element to move perfectly centered on the motion path.

Offset/distance between animated elements

As explained in ccprog's answer here "Offseting animation start point for SVG AnimateMotion"

We can apply negative begin values to set a distance between multiple elements moving around the same motion path.

This would be the SMIL replacement for offset-distance.

Swapping motion paths

Pretty easy: just change the <mpath> reference

svg{
  height:90vh;
  width: auto;
  border: 1px solid #ccc;
}
<svg viewBox="0 0 1340 1340" xmlns="http://www.w3.org/2000/svg">
  <g class="orbits" fill="none" stroke="#acb4ae" stroke-miterlimit="10">
    <path id="orbit1" stroke="red" d="m676 664c228 228 355 470 283 542-71.4 71.4-314-55.3-542-283a1782 1782 0 01-124-138c-147-181-216-347-159-404 71.4-71.4 314 55.3 542 283z" opacity=".276" />
    <path id="orbit2" d="m799 541c228 228 355 470 283 542-71.4 71.4-314-55.3-542-283s-355-470-283-542c71.4-71.4 314 55.3 542 283z" opacity=".276"/>
    <path id="orbit3" d="m923 417c228 228 355 470 283 542s-314-55.3-542-283c-228-228-355-470-283-542 71.4-71.4 314 55.3 542 283z" opacity=".276" />
  </g>

  <circle id="planet1" cx="0" cy="0" r="10" fill="#da2e68" />
  
  <!-- 1. animation-->
  <animateMotion href="#planet1" dur="5s" begin="-0.5s" repeatCount="indefinite" rotate="auto">
    <mpath href="#orbit1" />
  </animateMotion>
  
  <g id="eye" transform="translate(-371 -880)">
    <circle cx="371" cy="880" r="61.7" fill="#fff" />
    <circle cx="371" cy="880" r="61.2" fill="none" stroke="#aab5ad" />
    <path d="m371 863a16.4 16.4 0 11-16.4 16.4 16.4 16.4 0 0116.4-16.4z" fill="#fff" />
    <path d="m310 879h-.408a70.5 70.5 0 01122 0h.133a70.4 70.4 0 01-61.2 35.5c-26.1 0-48.5-14.4-60.6-35.5z" fill="none" stroke="#b4b4b4" />
    <path d="m371 907a27.8 27.8 0 1110.9-2.2 28 28 0 01-10.9 2.2m0-43.1c-1.19 4.68-2.3 7.81-4.45 9.95s-5.27 3.25-9.95 4.44c4.68 1.19 7.81 2.3 9.95 4.45s3.25 5.27 4.45 9.95c1.19-4.68 2.3-7.81 4.44-9.95s5.27-3.25 9.95-4.45c-4.68-1.19-7.81-2.3-9.95-4.44s-3.25-5.27-4.44-9.95" fill="#9bd698" />
  </g>
  <!-- 2. animation-->
  <animateMotion href="#eye" dur="5s" begin="0s" repeatCount="indefinite" rotate="0" >
    <mpath href="#orbit3" />
  </animateMotion>
</svg>

Hopefully, we'll see a more consistent browser support for offset-path – it's really elegant! Currently, we shouldn't really rely on it.

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34