Spinning an SVG circle using a group element

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

https://i.sstatic.net/Be501.png

My attempt to rotate the white circle using rotateZ(15deg) resulted in this: https://i.sstatic.net/GMp96.png

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

Highlight a pair of words in a phrase using jquery technology

Only one word in my code is highlighted $(document).ready(function() { let firstword = 'web'; let secondword = 'js'; $(".field.ConditionsAccept>.caption:contains('" + secondword + "'):contains('" + firstword ...

Suggestions for arranging nested list items securely?

Hello, I'm new to web design and would greatly appreciate any assistance. I've been learning HTML and CSS through a live project but have run into trouble with positioning nested list items. This is how my web browser renders the page: Here&a ...

Creating a single Vuetify expansion panel: A step-by-step guide

Is there a way to modify the Vuetify expansion panel so that only one panel can be open at a time? Currently, all panels can be closed which is causing issues. I would like the last opened panel to remain open. I also want to prevent closing the currently ...

The horizontal scroll feature is dysfunctional when attempting to allocate cell space based on a percentage

I want to display two sections of text that take up the full width of the screen and resize automatically when the screen size changes. Currently, I am using percentage widths to achieve this. However, when the text inside a section exceeds the available ...

I am looking to implement a feature in my quiz application where a green tick mark appears next to the question number for the correct answer and a red cross mark for the wrong answer

My HTML code here retrieves questions from a database and displays them based on the question number. <h4>{{indexOfelement+1}}</h4>&nbsp;&nbsp; In my CSS, I need to style the questions as follows: How can I achieve this? Each question ...

What is the best way to show the page before loading custom fonts?

My website is time-sensitive and I need it to prioritize displaying the page before loading any custom fonts. Essentially, I want the page to show up initially as if the fonts are not loaded. This way, the custom fonts (if already cached by the browser) w ...

Modification of encapsulated class in Angular is not permitted

Within Angular, a div element with the class "container" is automatically created and inserted into my component's HTML. Is there a way to modify this class to "container-fluid"? I understand that Angular utilizes encapsulation, but I am unsure of how ...

The Enigma of the Empty Gap When Employing "display:table/table-cell"

Struggling to understand why there is a blank space between the image and the menu. Here is the HTML and CSS related to it: <header class="header_nav"> <img src="img/logo.png" class="logo"/> <nav class="nav_bar"> <u ...

Modify the appearance of the PrimeReact navigation bar

I am struggling to change the background color of the navbar in my PrimeReact application. I have tried adding CSS styling in various places, but nothing seems to work. All I want is to customize the background color while keeping the default PrimeReact ...

Styling Page Titles with CSS Content

I'm having trouble including the page title in the footer of all printed pages. I've tried using the following code snippet: @page { @bottom-right { content: attr(title); } } However, this method doesn't seem to be working for me. ...

What could be the reason for my CSS selector with :target not functioning properly?

I tried to implement the instructions I found online for using :target, but it's not working as expected. My goal is to change the background color and font color of #div1 when the first link is clicked, and to change the border of #div2 when the seco ...

What is the best way to reduce the size of an image taken from an image sprite rather than a standalone image?

I have an image sprite and I am looking to make a specific part of it appear smaller using CSS. Any recommendations or suggestions? Thank you in advance. ...

Troubleshooting: Bootstrap interfering with external CSS file and causing errors

Attempting to utilize Bootstrap for the design of my website, I encountered an issue when trying to implement an external CSS file named "mycss.css". Unfortunately, it seems to not be functioning properly at all. Both the file, "mycss.css" and index.php ar ...

Adjusting the background color of the custom input range thumb when the input is disabled

I've customized the input thumb on my range slider, and I'm looking to change its color when it's disabled. I attempted adding a class to the thumb like this: input[type=range]::-webkit-slider-thumb.disabled and also tried adding the disa ...

Why is the feedback section showing a horizontal scrollbar?

I'm puzzled as to why a horizontal scrollbar is appearing in the feedback section. I've examined the elements but can't seem to pinpoint the issue. ...

Animate the leftward disappearance of a div to make it vanish

My goal is to create a user interface where users can navigate through different divs. Here is the HTML code: <article id="realize" class="realizeBox"> <div class="shown"> <div class="heading"> <h2>Realisati ...

Optimizing web content for iOS Safari with min-height media queries

I'm currently working on a Bootstrap-based photo browser that needs to have a responsive 4:3 aspect ratio. To achieve this, I have implemented the following approach: #carousel { width: 320px; height: 240px; ... } Additionally, I utilize media ...

Which is better for cycling through a list of items: CSS or JavaScript?

Currently, I have a navigation bar set up as an unordered list (<ul>), but some list items are not visible. I want to implement functionality where clicking arrows will move the elements left or right by hiding or showing the leftmost or rightmost it ...

Create a responsive layout of inline-block elements that adjusts to resizing

I am currently focusing on developing the website found at: The progress so far includes successfully creating a list of div.project-item elements that adjust the number of columns based on window resizing due to the use of inline-blocks. My next goal is ...

Adjust the button's width as the text changes to create a dynamic animation

I'm trying to create an animation effect where the width of a button changes whenever the text inside it is updated. I've attempted using useRef on the button itself to grab its clientWidth and then applying that value to a CSS variable in order ...