Spinning an SVG circle using a group element

I'm interested in rotating an SVG circle without affecting the rotation of other elements.

My attempt to rotate the white circle using rotateZ(15deg) resulted in this:

This is the progress I've made so far: https://jsfiddle.net/41hrnojs/

<svg viewBox="0 0 1400 900" style="outline:1px solid red;">
            <g>
               <clipPath id="hexagonal-mask">
                  <circle cx="700" cy="100" r="705" ></circle>
               </clipPath>
            </g> 
            <a>
             <image clip-path="url(#hexagonal-mask)" height="100%" width="100%" xlink:href="{{ asset('images/H3z50J2.jpg') }}"  style="transform: translateY(-140px);"/>
            </a>

            <g  style="transform-origin: 701px -5%; transform: rotateZ(15deg)">
                <circle cx="701" cy="0" r="665" stroke="#fff" stroke-width="1px" fill="transparent"  style="transform: translateY(-50px);" ></circle>
               
                <!-- center dot -->
                <g id="g1" >
                    <circle cx="701" cy="615" r="15" fill="#fff">
                        
                    </circle> 
                    <path  stroke="#000" stroke-width="1px" d="M701 630 701 690"></path>
                   
                    <text x="672" y="720" font-family="'Playfair Display', serif" font-size="2em" font-weight="bold" fill="#9d9e9f">2007</text>
                    <text x="640" y="730" font-family="'Playfair Display', serif" font-size="2.85em" font-weight="bold" fill="#000">
                        <tspan x="640" dy="40">Lorem</tspan>
                        <tspan x="640" dy="45">Ipsum</tspan>
                    </text>
                    
                    <animateMotion 
                       xlink:href="#g1"
                       dur="1s"
                       begin="click"
                       fill="freeze"
                       path="M0 100 Q50 80 -399 -135"
                       repeatCount="1">
                        
                    </animateMotion>
                </g>
                
                
                

                <!-- left dot -->
                <g>
                    <!-- <circle cx="305" cy="485" r="15" fill="#fff"></circle> -->
                    <circle cx="302" cy="480" r="15" fill="#fff"></circle>
                    <path stroke="#000" stroke-width="1px" d="M302 495 305 675"></path>
                </g>

                <!-- right dot -->
                <g>
                    <circle cx="1100" cy="480" r="15" fill="#fff"></circle>
                    <path  stroke="#000" stroke-width="1px" d="M1100 495 1100 675"></path>
                </g>
            </g>

        </svg>

My objective is to:

  • Rotate the white circle while clicking on the dots located on the circle
    itself

Answer №1

To avoid rotating everything, I prefer to calculate the position of the dots on the circle and then use those coordinates to draw the line and text.

In this case, I am utilizing javascript with a crucial function in the script that calculates the new position after rotation: rotatePoint(p, c, rot)

It's worth noting that I have removed unnecessary transformations in the SVG.

let theG = document.querySelector("#theG");
//circle's center
let center = { x: 700, y: -40 };
//rotation in radians
let rot = .6;
//function to calculate the new position of a rotated point
function rotatePoint(p, c, rot) {
  // p: the point
  // c: the center of rotation
  // rot: the rotation
  let cos = Math.cos(rot);
  let sin = Math.sin(rot);
  return {
    x: c.x + (p.x - c.x) * cos - (p.y - c.y) * sin,
    y: c.y + (p.x - c.x) * sin + (p.y - c.y) * cos
  };
}


//select all groups with a class of dot
let groups = theG.querySelectorAll(".dot");
let points = [];

groups.forEach((g) => {
  let dot = g.querySelector("circle");
  let p = {};

  p.x = dot.getAttribute("cx");
  p.y = dot.getAttribute("cy");
  
  points.push(p)
});


itr.addEventListener("input",()=>{
  
let rot = itr.value;

groups.forEach((g,i) => {
  
  let dot = g.querySelector("circle");
  let line = g.querySelector("line");
  let t1 = g.querySelectorAll("text")[0];

  let newPoint = rotatePoint(points[i], center, rot);

  dot.setAttribute("cx", newPoint.x);
  dot.setAttribute("cy", newPoint.y);

  line.setAttribute("x1", newPoint.x);
  line.setAttribute("x2", newPoint.x);
  line.setAttribute("y1", newPoint.y);
  line.setAttribute("y2", newPoint.y + 180);

  t1.setAttribute("x", newPoint.x);
  t1.setAttribute("y", newPoint.y + 200);

});
  
});
input{width:90vw;}
p{text-align:center;}
text{text-anchor:middle}

line{stroke:#000; stroke-width:1px; }
<p><input type="range" id="itr" min="-.85" max=".85" value="0" step=".01" /></p>

<svg viewBox="0 0 1400 900" style="outline:1px solid red;" >
   <defs>
    <clipPath id="hexagonal-mask">
      <circle cx="700" cy="-40" r="705"></circle>
    </clipPath>
  </defs>
  <image clip-path="url(#hexagonal-mask)" height="100%" width="100%" xlink:href="https://assets.codepen.io/222579/castell.jpg"></image>
  <circle cx="700" cy="-40" r="655" stroke="#fff" stroke-width="1px" fill="transparent"></circle>
  <g id="theG">

    <g class="dot">
      <circle cx="700" cy="615" r="15" fill="#fff"></circle>
      <line x1="700" y1="615" x2="700" y2="795"></line>
      <text x="700" y="815" font-family="'Playfair Display', serif" font-size="2em" font-weight="bold" fill="#9d9e9f">2007</text>
    </g>

    <g class="dot">
      <circle cx="302" cy="480" r="15" fill="#fff"></circle>
      <line x1="302" y1="480" x2="302" y2="660"></line>
      <text x="302" y="680" font-family="'Playfair Display', serif" font-size="2em" font-weight="bold" fill="#9d9e9f">2006</text>
    </g>

    <g class="dot">
      <circle cx="1100" cy="480" r="15" fill="#fff"></circle>
      <line x1="1100" y1="480" x2="1100" y2="660"></line>
      <text x="1100" y="680" font-family="'Playfair Display', serif" font-size="2em" font-weight="bold" fill="#9d9e9f">2008</text>
    </g>
  </g>
</svg>

Answer №2

Is it possible to achieve this without relying on JavaScript? In the upcoming demo, I will only use JavaScript to alter the rotation value.

Given that SVG transforms utilize degrees rather than radians, please keep that in mind.

The concept at play involves creating a nested SVG element. While rotating the 'G' in one direction, it's essential to rotate the line and text by the same degree but in the opposite direction. The challenge arises when the dots change their positions during rotation, requiring knowledge of their position to serve as an anchor for the line and text.

The solution is to enclose everything within a nested SVG where the positions remain constant, allowing for the rotation of the nested SVG. According to SVG 1.1 specifications, the transform attribute was not permitted within the 'element'. Hence, each nested SVG was placed in a group and rotated accordingly.

itr.addEventListener("input",()=>{
 
let rot = itr.value;
theG.setAttribute("transform",`rotate(${rot} 700 -40)`) 
dot2006.setAttribute("transform",`rotate(${-rot} 302 480)`); 
dot2007.setAttribute("transform",`rotate(${-rot} 700 600)`);
dot2008.setAttribute("transform",`rotate(${-rot} 1100 480)`);  
});
input{width:90vw;}
p{text-align:center;}
text{text-anchor:middle; font-family:'Playfair Display' serif; font-size:2em; font-weight:bold; fill:#9d9e9f;}

line{stroke:#000; stroke-width:1px; }
<p><input type="range" id="itr" min="-45" max="45" value="0" /></p>

<svg viewBox="0 0 1400 900" style="outline:1px solid red;">
  <defs>
    <clipPath id="hexagonal-mask">
      <circle cx="700" cy="-40" r="705"></circle>
    </clipPath>
    
    
     <g id="cl">
      <!--<rect x="-32" y="-15" width="64" height="224" fill="gold"/>-->
      <circle r="15" fill="#fff"></circle>
      <line y2="180"></line>
      </g>
  </defs>
  
  
  
  <image clip-path="url(#hexagonal-mask)" height="100%" width="100%" xlink:href="https://assets.codepen.io/222579/castell.jpg"></image>
  <circle cx="700" cy="-40" r="655" stroke="#fff" stroke-width="1px" fill="transparent"></circle>
  
  
  <g id="theG" transform="rotate(0 700 -40)">
    
    
<g id="dot2007" transform="rotate(0 700 600)">
<!--transform="rotate(-25 668+32 600+15)"-->
<svg x="668" y="600" width="64" height="224" viewBox="-32 -15 64 224" >
      <use xlink:href="#cl"/>
      <text y="200">2007</text>
    </svg>
    </g>   

    
<g id="dot2006" transform="rotate(0 302 480)"> 
<!--transform="rotate(-25 270+32 465+15)"-->
<svg x="270" y="465" width="64" height="224" viewBox="-32 -15 64 224">
      <use xlink:href="#cl"/>
      <text y="200">2006</text>
    </svg>
</g>
    
<g id="dot2008" transform="rotate(0 1100 480)"> 
<!--transform="rotate(-25 1068+32 465+15)"-->
<svg x="1068" y="465" width="64" height="224" viewBox="-32 -15 64 224">
      <use xlink:href="#cl"/>
      <text y="200">2008</text>
    </svg>
    </g>

  
  </g>
</svg>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for aligning the button and search bar in the center:In order

Currently still learning HTML, I'm facing an issue with centering a button and a search bar that are positioned beside each other. Despite trying multiple methods, I haven't been successful in achieving the desired centered layout while keeping t ...

What is the best way to line up two forms side by side?

Looking to create a login view for a webpage with two forms (one for registration and one for login) aligned side by side. Initially attempted using a table, then tried using bootstrap form groups (rows and cols) without success. Finally, aligned them in t ...

Ensure that FlexBox Columns have a height of 100% and vertically align their content

Having scoured through numerous questions and answers, I'm still stuck on why I can't achieve 100% height for certain flexbox columns. Specifically, trying to make the "Genre" column with one line of text match the height of the "Song & The A ...

Webpage refreshing when resizing browser

Hey there, I'm facing an issue where my HTML website restarts whenever the browser size changes. Can someone please help me fix this? You can check out my website here I have uploaded my code files here: Code Files Link ...

When using margin-right and a width of 100% in the WordPress backend, a scrollbar may be triggered

I have been working on developing a configuration page for my WordPress plugin. In order to display the content, I included an <ul> element inside a <div> container and added it to the configuration page. However, I encountered an issue where a ...

Utilize the JavaScript Email Error Box on different components

On my website, I have implemented a login system using LocalStorage and would like to incorporate an error message feature for incorrect entries. Since I already have assistance for handling email errors on another page, I am interested in applying that sa ...

Unable to view numbers on iPhone or ios device

I am facing an issue with displaying a phone number in the contact section on my website. The number shows up on desktop but not on my iPhone, even when I resize the screen to be smaller than my phone's display size. This issue has been tested on both ...

Is there a way to stop TD from going to the next line?

I am using Javascript to dynamically generate a table and I want it to extend beyond the boundaries of the page. When I manually create the table, it works fine and extends off the page, but once I put it into a loop, the <td> elements wrap onto a se ...

What is the best way to make the div inside the table cells expand to fill the available space?

I'm having trouble making the inner divs within table cell divs expand and fit their parent div without overlapping the next cell below it. Check out the sandbox for reference I've outlined the areas in grey where I want the cells to be filled. ...

Can React components be saved in an array?

I am currently working on enhancing the code snippet provided below. This code is intended to iterate through elements of an array using keys to locate images within a lengthy SVG file directly embedded in the document under the identifier "SomelongUglySVG ...

Highlighting a Table Column with a Radio Button

I am struggling with controlling the highlight of a table using only radio buttons. When I try to change the selector to input:radio, it doesn't work as expected. It seems to only work with the selector provided in my code snippet. $("th").on("clic ...

Divide the division inside the box by clicking on it

I am looking to create a unique div with a random background color and a width of 100px. Additionally, I want to have a button that, when clicked, will split the original div into two equal parts, each with its own random background color. With each subs ...

How to Toggle the Submit Button Using jQuery

I have implemented a feature using jQuery to disable the submit button and display a loading image. Here is how I have set it up: The HTML structure for the submit button looks like this: <div id="registerbtn" name="registerbtn"> <input typ ...

Unable to make jQuery animate top function work

I have three rectangles set up like this. I've managed to make them disappear when clicking the close button on the right side of each rectangle, but I'm struggling with getting the remaining rectangles to move upward to fill the empty space. Her ...

What is the process for dynamically populating a select dropdown based on the selection made in another select dropdown?

I need to dynamically populate the second select box based on the option selected in the first select box. Here's what I have tried so far, but it doesn't seem to be working as expected. HTML: <form id="step1"> <p> Creat ...

Can Highchart dynamically adjust color choices based on the quantity of data points available?

I am trying to figure out how to import a specific color palette into my column graph. I want to use different color palettes based on the number of data points in my graph - for 3 or fewer points, I want to use the top row colors, for 4 points I want to u ...

What could be the reason for justify-self: flex-start not positioning the div at the bottom of a column?

I want to display a div in three levels: header, content, and footer. The content can vary in height and the footer should always be at the bottom. Here is how I have implemented it: <div class="news-content-block"> <div class=" ...

What is causing these headers for the children not to center properly?

I am trying to centrally align both vertically and horizontally the <div> element, with its child elements <h1> and <h2> being horizontally centered in relation to the parent <div>. To achieve this, I have created a specific class f ...

The div remains unchanged when the button is clicked

My webpage is filled with several large div elements. There's a button on the page that, when clicked, should switch to the next div. Despite my efforts, the code I've written doesn't seem to be working as expected. :( This is the HTML st ...

Updating the CSS style of an inner DIV using JavaScript

Can you provide guidance on how to modify the background color using JavaScript for the different styles listed below? i) Internal Style sheet and/or ii) External Style sheet I am currently working with the card deck slide show available at https://githu ...