Reputation: 179
First off, sorry for the topic name. I can't really explain what I want to make precisely in one sentence.
Then! What I want to make is a circle with a square on its stroke, within which there will be 6 "hoverable" squares.
Here is the test I've made :
var s = Snap(500, 500);
var thecircle = s.circle(250,250,100).attr({fill:'none',stroke:'red','stroke-width':'2'});
var pointer = s.rect(240,340,20,20);
var william = s.g(thecircle, pointer);
var hover1 = s.rect(240,290,20,20).attr({'value':'0'}).addClass('hovering');
var hover2 = hover1.clone().transform('r60,250,250').attr({'value':'60'}).addClass('hovering');
var hover3 = hover1.clone().transform('r120,250,250').attr({'value':'120'}).addClass('hovering');
var hover4 = hover1.clone().transform('r180,250,250').attr({'value':'180'}).addClass('hovering');
var hover5 = hover1.clone().transform('r240,250,250').attr({'value':'240'}).addClass('hovering');
var hover6 = hover1.clone().transform('r300,250,250').attr({'value':'300'}).addClass('hovering');
var $ = jQuery;
$('.hovering').mouseenter(function(){
var rotate = $(this).attr('value');
william.animate({transform:'r'+rotate+',250,250'},300,mina.ease);
});
And here is a fiddle so that you can check how the animation looks : JSfiddle
So now, see what happens when you hover the 6th square (300 degrees) and then hover the 2nd one (60 degrees). The pointer will travel all the way in front of the 5th, 4th and 3rd before reaching the 2nd (a travel of 240 degrees).
I would like for my pointer to go the fastest route to its destination, in this example, it would be 420 degrees. But I have no idea on how I could make it behave that way as I'm not really good at maths...
Upvotes: 2
Views: 498
Reputation: 101820
You just need to work out which of the two possible directions has the shortest distance. Then you add the diff that's smallest to an accumulated rotation value.
Demo
var s = Snap(500, 500);
var thecircle = s.circle(250,250,100).attr({fill:'none',stroke:'red','stroke-width':'2'});
var pointer = s.rect(240,340,20,20);
var william = s.g(thecircle, pointer);
var hover1 = s.rect(240,290,20,20).attr({'value':'0'}).addClass('hovering');
var hover2 = hover1.clone().transform('r60,250,250').attr({'value':'60'}).addClass('hovering');
var hover3 = hover1.clone().transform('r120,250,250').attr({'value':'120'}).addClass('hovering');
var hover4 = hover1.clone().transform('r180,250,250').attr({'value':'180'}).addClass('hovering');
var hover5 = hover1.clone().transform('r240,250,250').attr({'value':'240'}).addClass('hovering');
var hover6 = hover1.clone().transform('r300,250,250').attr({'value':'300'}).addClass('hovering');
var $ = jQuery;
// What rotate currently is
var lastRotate = 0;
// Actual transform rotate accumulates up or down depending
// on which direction we have been going in.
var accumulatedRotation = 0;
$('.hovering').mouseenter(function(){
// Make sure 'rotate' is a number not a string
var rotate = parseInt($(this).attr('value'), 10);
// rotateAlt is the alternative version of rotate (either >360 or <0)
var rotateAlt = (lastRotate < 180) ? (rotate - 360) : (360 + rotate);
// Work out the diff value for each alt
var diffA = rotate - lastRotate;
var diffB = rotateAlt - lastRotate;
// Add the smaller diff to the accumulated rotation
if (Math.abs(diffA) < Math.abs(diffB))
accumulatedRotation += diffA;
else
accumulatedRotation += diffB;
william.animate({transform:'r'+accumulatedRotation+',250,250'},300,mina.ease);
// Remember the last value of 'rotate'
lastRotate = rotate;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Upvotes: 3
Reputation: 9654
UPDATED
To fix this, First we need to create a variable prevRotate
-initially set to 0
- to record the value of the pointer's previous location, then we need to take care of four special cases(1) as well as the normal case like below:
When the pointer was previously on the bottom square ( rotate
value = 0
), and the mouse hovers a square with rotation value more than 180
, we need to move the pointer counter-clockwise by -1 * (360 - rotate)
. then using a callback function we set the rotation degree immediately to original rotate
value using the .animate()
function with animation time of 0
.
When the pointer was originally on a square with rotate
value higher than 180
, and the hover over the bottom square which has rotate value of 0
, animate the pointer to 360
CW direction and then with the use of our callback function we reset it to 0
.
When pointer was previously on the square that has rotate
value of 300
, and the mouses hovers the square with rotate
value of 60
, we need to draw the short route by rotating from 300
to 420
(300 + (360-300) + 60
), then using the callback function to reset the rotate value to 60
.
When the pointer was previously on square with rotate
value of 60
and we need to animate it to square with rotate
of 300
, we need to animate from 60
to -60
and then immediately set the rotation to 300
using the callback function again.
Finally we need to update the value of prevRotate
for further comparisons.
var $ = jQuery,
prevRotate = 0; // The variable used to store the previous location of the pointer.
$('.hovering').mouseenter(function(){
var rotate = $(this).attr('value');
// Special case 1, moving the pointer CCW following the short route.
if( prevRotate == 0){
var tempR = rotate;
rotate = rotate > 180 ? -1 * (360 - rotate) : rotate;
william.animate({transform:'r' +rotate+ ',250,250'},300,mina.ease, function(){
myCB(tempR);
});
}else{
// Normal case.
william.animate({transform:'r'+ rotate +',250,250'},300,mina.ease);
}
if(rotate == 0){
// Special Case 2, animating to 360 instead of 0, then to 0 immediately.
if(prevRotate > 180){
william.animate({transform:'r360,250,250'},300,mina.ease,function(){
myCB(0);
});
}
}else if(rotate == 60 && prevRotate == 300){
// Special Case 3, animating from 300 to 60 following the short route.
william.animate({transform:'r420,250,250'},300,mina.ease, function(){
myCB(60);
});
}else if(rotate == 300 && prevRotate == 60){
// Special Case 4, animating from 60 to 30 following short route.
william.animate({transform:'r-60,250,250'},300,mina.ease, function(){
myCB(300);
});
}
// Update the value of the pointer's previous location
prevRotate = $(this).attr('value');
});
// The Callback function to reset rotate values.
function myCB(theAngle){
william.animate({transform:'r' +theAngle+ ',250,250'},0);
}
(1) Note that you can merge special cases 3 and 4 code blocks into one like this:
if((rotate == 60 || rotate == 300) && prevRotate == 360 - rotate){
// Define the destination angle depending on value of rotate
var newR = (rotate == 60) ? 420 : -60;
william.animate({transform:'r' +newR+ ',250,250'},300,mina.ease,function(){
myCB(prevRotate);
});
}
Upvotes: 2