Search code examples
svgrotationcenteringvector-graphics

SVG: How can I display multiple paths and rotate them around the center?


I am new to SVG.

I am trying to define a "template" with a path in defs and reuse it. I want it the paths to rotate around the center with equal spacing between them enter image description here

This is how far i got. The red dot is the middle.

Here is my code:

    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
    <rect x="0" y="0" width="300" height="300" fill="#0B3E27"/>
    
   <defs>
        <path id="rotor" d="M140,140 
               c0,0,-40,-20,-100,0
               c0,0,50,-10,50,20
               c0,0,0,-5,50,-20"  
            fill="none" 
            stroke-width="2" 
            stroke="white"/>
    </defs>

    <g>
      <use href="#rotor" x="-2" y="0"/>          
      <use href="#rotor" x="54" y="-160" transform="rotate(50)"/> 
      <use href="#rotor" x="-32" y="-305" transform="rotate(100)"/>  
      <use href="#rotor" x="-198" y="-332" transform="rotate(150)"/>    
      <use href="#rotor" x="-326" y="-223" transform="rotate(200)"/>   
      <use href="#rotor" x="-325" y="-55" transform="rotate(250)"/>    
      <use href="#rotor" x="-195" y="53" transform="rotate(300)"/>    
    </g>

    <circle cx="150" cy="150" r="2" fill="red"></circle>
</svg>

How can this be smoothly done in SVG?

Thanks so much in advance,

Simon


Solution

  • You need to specify a "pivot-point" the for the rotation.
    You can either use the transform-origin attribute or CSS property or you can specify this point in the rotate() attribute directly as 2. and 3. arguments like so rotate(50 150 150).

    Besides, you can tweak the position of the rotor shape by adjusting the first coordinates in the the path data d attribute (M150 150)

    M145 150 c0 0-40-20-100 0 0 0 50-10 50 20 0 0 0-5 50-20

    This way you don't need additional x and y offsets for each <use>instance. We can move the entire path this way because it is based on relative commands. If you're uncertain whether your path is fully relative you can convert the path data with a tool like svg-path-editor

    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
        <rect x="0" y="0" width="300" height="300" fill="#0B3E27"/>
        
       <defs>
            <path id="rotor" d="M145 150 c0 0-40-20-100 0 0 0 50-10 50 20 0 0 0-5 50-20"  
                fill="none" 
                stroke-width="2" 
                stroke="white"/>
         <style>
        </defs>
    
        <g >
          <use href="#rotor" />          
          <use href="#rotor"  transform="rotate(50 150 150)" /> 
          <use href="#rotor"  transform="rotate(100 150 150)"/>  
          <use href="#rotor"  transform="rotate(150 150 150)"/>    
          <use href="#rotor"  transform="rotate(200 150 150)"/>   
          <use href="#rotor"  transform="rotate(250 150 150)"/>    
          <use href="#rotor"  transform="rotate(300 150 150)"/>    
        </g>
    
        <circle cx="150" cy="150" r="2" fill="red"></circle>
    </svg>

    Using CSS transform-origin

    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
        <rect x="0" y="0" width="300" height="300" fill="#0B3E27"/>
        
       <defs>
            <path id="rotor" d="M145 150 c0 0-40-20-100 0 0 0 50-10 50 20 0 0 0-5 50-20"  
                fill="none" 
                stroke-width="2" 
                stroke="white"/>
         <style>
          
           use{
             transform-origin: 150px 150px;
           }
           
         </style>
        </defs>
    
        <g >
          <use href="#rotor" />          
          <use href="#rotor"  transform="rotate(50)" /> 
          <use href="#rotor"  transform="rotate(100)"/>  
          <use href="#rotor"  transform="rotate(150)"/>    
          <use href="#rotor"  transform="rotate(200)"/>   
          <use href="#rotor"  transform="rotate(250)"/>    
          <use href="#rotor"  transform="rotate(300)"/>    
        </g>
    
        <circle cx="150" cy="150" r="2" fill="red"></circle>
    </svg>

    Moving more styles to CSS

    The angle calculation is a bit odd – the 7 rotor blades with a 50 degree offset don't match up that well (covering only 350deg).
    You may also move some transformations to an external CSS - combined with CSS vars and calc() – this way you don't need to calculate the rotation angles explicitly.
    Besides, you can easily avoid a background <rect> element by applying a background directly to the outermost SVG element.

    :root {
      --blades: 7;
      --blade: 0;
    }
    
    /* base style for rotor blades */
    .b {
      transform-origin: 150px 150px;
      stroke-width: 2px;
      stroke: #fff;
      transform: rotate(calc(360deg / var(--blades) * var(--blade)));
    }
    
    
    .b1 {
      --blade: 1;
    }
    
    .b2 {
      --blade: 2;
    }
    
    .b3 {
      --blade: 3;
    }
    
    .b4 {
      --blade: 4;
    }
    
    .b5 {
      --blade: 5;
    }
    
    .b6 {
      --blade: 6;
    }
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" style="background:#0B3E27">
      <defs>
        <path id="rotor" d="M145 150 c0 0-40-20-100 0 0 0 50-10 50 20 0 0 0-5 50-20" />
      </defs>
      <!-- rotor blades -->
      <use class="b b0" href="#rotor"/>
      <use class="b b1" href="#rotor"/>
      <use class="b b2" href="#rotor"/>
      <use class="b b3" href="#rotor"/>
      <use class="b b4" href="#rotor"/>
      <use class="b b5" href="#rotor"/>
      <use class="b b6" href="#rotor"/>
    </svg>