LocustHorde
LocustHorde

Reputation: 6399

Setting direction of rotation in d3

I have a circle with few clickable points. When I click one of these, I want it to rotate to the right side (zero degree) position. When I click the point on top (90deg), I want the circle to rotate clockwise and when I click the point at the bottom, (270deg), I want it to rotate anti-clockwise. Essentially, take the shortest path to get to right side. The problem is, when I click the bottom point (blue star), the circle takes the longest path to be at the right side. If you play around with it a bit more, you can see that it seems to set a specific direction to each point and no matter where they are, it follows that direction.

How do I control that direction of rotation please? Any pointers towards this is appreciated.

function rotate(angle) {
  d3.select('.container')
    .attr('transform', `rotate(${angle})`);
}
body {
  margin: 0 auto;
  text-align: center;
  padding: 10px;
}

.container {
  transform-origin: 250px 250px;
  transition: all 0.5s ease-in;
}

.star {
  cursor: pointer;
}

.star--red {
  fill: red;
}

.star--green {
  fill: green;
}

.star--blue {
  fill: lightblue;
}

.star--pink {
  fill: salmon;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewbox="0 0 500 500"> 
 <g class="container">
  <ellipse id="main-circle" stroke-width="2" ry="145" rx="145" cy="250" cx="250" stroke="#000" fill="#fff"/> 
  <path id="svg_8" class="star star--red" onclick="rotate(90);" 
        d="m228.500007,135.025599l16.424656,0l5.075345,-15.603354l5.075348,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643555l-13.287804,9.643555l5.075609,-15.603354l-13.287806,-9.643293z"/>
  <path id="svg_9" class="star star--green" onclick="rotate(180);"
        d="m119.362495,245.178355l16.424656,0l5.075345,-15.603354l5.075347,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643556l-13.287805,9.643556l5.07561,-15.603354l-13.287806,-9.643293z"/>
  <path id="svg_10"  class="star star--blue" onclick="rotate(270);"
 d="m228.500007,355.025599l16.424656,0l5.075345,-15.603354l5.075347,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643556l-13.287805,9.643556l5.07561,-15.603354l-13.287806,-9.643293z"/> 
  <path id="svg_11" class="star star--pink" onclick="rotate(0);"
 d="m337.362495,245.178355l16.424656,0l5.075345,-15.603354l5.075347,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643556l-13.287805,9.643556l5.07561,-15.603354l-13.287806,-9.643293z"/>
 </g>
</svg>

Essentially I've copy pasted the codepen code here, but the pen is also available at https://codepen.io/anon/pen/LjMVzM

Upvotes: 2

Views: 1235

Answers (2)

magjac
magjac

Reputation: 937

To rotate counter-clockwise, use a negative angle.

Upvotes: 1

Andrew
Andrew

Reputation: 13853

You can't just get away with tracking only the required rotation, you also need to keep track of the total rotation. For example, If you keep clicking whatever star is on top the required degrees to keep going becomes, [0, 90, 180, 270, 360, 450, ...]. Once keeping track of the total rotation, all you need to do is determine the direction to increment as shown below.

var totAngle = 0;
function rotate(angle) {
  d3.select('.container')
    .attr('transform', () => {
      // Clamp the current rotation to 0-360
      var minAngle = ((totAngle % 360) + 360) % 360;
      // Calculate the required rotation 0/90/180/270
      var rot = (angle - minAngle + 360) % 360;
      // Determine the direction clockwise/counter-clockwise
      totAngle += rot <= 180 ? rot : -90
      return `rotate(${totAngle})`;
    });
}
body {
  margin: 0 auto;
  text-align: center;
  padding: 10px;
}

.container {
  transform-origin: 250px 250px;
  transition: all 0.5s ease-in;
}

.star {
  cursor: pointer;
}

.star--red {
  fill: red;
}

.star--green {
  fill: green;
}

.star--blue {
  fill: lightblue;
}

.star--pink {
  fill: salmon;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewbox="0 0 500 500"> 
 <g class="container">
  <ellipse id="main-circle" stroke-width="2" ry="145" rx="145" cy="250" cx="250" stroke="#000" fill="#fff"/> 
  <path id="svg_8" class="star star--red" onclick="rotate(90);" 
        d="m228.500007,135.025599l16.424656,0l5.075345,-15.603354l5.075348,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643555l-13.287804,9.643555l5.075609,-15.603354l-13.287806,-9.643293z"/>
  <path id="svg_9" class="star star--green" onclick="rotate(180);"  d="m119.362495,245.178355l16.424656,0l5.075345,-15.603354l5.075347,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643556l-13.287805,9.643556l5.07561,-15.603354l-13.287806,-9.643293z"/>
  <path id="svg_10"  class="star star--blue" onclick="rotate(270);"
 d="m228.500007,355.025599l16.424656,0l5.075345,-15.603354l5.075347,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643556l-13.287805,9.643556l5.07561,-15.603354l-13.287806,-9.643293z"/> 
  <path id="svg_11" class="star star--pink" onclick="rotate(0);"
 d="m337.362495,245.178355l16.424656,0l5.075345,-15.603354l5.075347,15.603354l16.424653,0l-13.287801,9.643293l5.075608,15.603354l-13.287807,-9.643556l-13.287805,9.643556l5.07561,-15.603354l-13.287806,-9.643293z"/>
 </g>
</svg>

Upvotes: 2

Related Questions