Reputation: 1645
The effect I'm trying to achieve is upon clicking a button a circle made up of 8 triangles spins around. When it stops the triangle at the top of the circle is selected. Then a fancybox overlay relative to the selected segment is opened (this last part doesn't work yet). The order the segments are selected appears random to the user but it can be preset as I've done in the CSS (currently: 3rd > 6th > 1st > 8th > 2nd > 5th > 7th > 4th).
I've created a codepen to show my current progress on this which you can see by visiting the link below as its quite hard to explain.
http://codepen.io/moy/pen/DrbAL
On clicking the 'Spin' button the circle rotates 3 times (1080 degrees) + an additional 270 degrees so the 3rd list-item is positioned at the top of the div - this works fine.
My 1st question is: On the 2nd click of the button, the circle needs to rotate 3 times again and then a bit more to position the next selected list-item/segment (6) at the top of the div and so on for each of the remaining items. Is there a way to do this without having to add 1080 to the amount the circle has already rotated?? So the value is always 1080deg
+ the additional amount the circle needs to rotate to position the relevant segment at the top of the div. Otherwise the last item's value is going to be something like 10000 degrees! If this has to be done I guess I'll have to live with it and get my maths hat on.
The 2nd question is: Once the circle has finished rotating, how can I display the related fancybox overlay? The ID's are currently the title of each overlay but could easily be overlay-1, overlay-2, etc to match the classes of spin-1, spin-2 set on the spinning wheel after each spin.
Thanks, hope someone can help!
Upvotes: 2
Views: 4411
Reputation: 41143
I found this question very interesting because I worked on a similar project some time ago so this is my input. First, I would suggest you to use the Julian Shapiro's velocity plugin for your animations, which will give you some of the following advantages :
With velocity, spinning your wheel can be done as simple as :
$(".wheel").velocity({
rotateZ : "-" + {degree} + "deg" // "-" minus to spin counterclockwise, remove otherwise
});
The velocity plugin has a full set of API options that you can explore here. Of course, you need an algorithm to calculate the new value of the degree
variable on every click
.
Now, if I understood correctly you have two questions :
I will start answering the second question with the following algorithm :
_target = (_deg - (360 * parseInt(_deg / 360))) / 45;
where _target
is the javascript index
of the selected segment and _deg
the amount of degrees we had spun the wheel from the previous position to the current.
How it works :
Following your example above, the initial state of your wheel is segment No. 1 (Commercial Awareness) and from there you need to move to No. 3 (Team Work), so it means spinning the wheel 3 times (1080 degrees) + 2 more segments of 45 degrees each (90 degrees) = 1170 degrees
rotation.
so, applying that value to the algorithm :
_target = (1170 - (360 * parseInt(1170 / 360))) / 45;
_target = (1170 - (360 * parseInt(3.25))) / 45;
_target = (1170 - (360 * 3)) / 45;
_target = (1170 - (1080)) / 45;
_target = (90) / 45;
_target = 2;
If you consider that in javascript the first element of a group has index=0
, the second is index=1
, the third is index=2
, etc. _target = 2
actually points to the third segment (Team Work). This algorithm works regardless how many degrees you have spun the wheel.
Then firing fancybox is simple:
$(".fancybox").eq(_target).trigger("click");
the code above assumes you have initialized fancybox somewhere in your script like
$(".fancybox").fancybox({ // API options });
Now, for the first question I see 3 possible algorithms that you can set in a separated function to call after button click
:
Returns a patterned order (advances 3 segments each click
) like 1,4,7,2,5,8,3,6 ... then loops. Every segment is shown at least once before the loop so :
function ordSequential() {
// order : 1,4,7,2,5,8,3,6 ... then loops
return _deg = _deg + (45*3) + 1080;
};
it assumes an initial value of _deg = 0
. Notice you can always change the factor 3
to alter the pattern.
So having the amount of degrees we will spin the wheel, we can use velocity to :
1
0.4
the full code
// sequential order
// advances 3 segments each click
var _target, _deg = 0;
function ordSequential() {
// order : 1,4,7,2,5,8,3,6 ... then loops
return _deg = _deg + (45*3) + 1080;
};
jQuery(document).ready(function ($) {
$(".skills-wheel .btn").on("click", function (e) {
// select algorithm sequential, random or preset :
ordSequential();
_target = (_deg - (360 * parseInt(_deg / 360))) / 45;
// start animation
// reset opacity of all segments to 1
$(".fancybox").parent("li").velocity({
opacity: 1
}, {
duration: 100,
complete: function () {
$(".wheel").velocity({
rotateZ: "-" + _deg + "deg"
}, {
duration: 3000,
complete: function (elements) {
// after spinning animation is completed, set opacity of target segment's parent
$(".fancybox").parent("li").eq(_target).velocity({
opacity: 0.4
}, {
duration: 100,
// after opacity is completed, fire targeted segment in fancybox
complete: function () {
$(".fancybox").eq(_target).trigger("click");
} // third animation completed
}); // nested velocity 2
} // second animation completed
}); // nested velocity 1
} // first animation completed
}); // velocity
return false;
}); // click
// initialize fancybox
$(".fancybox").fancybox({
maxWidth: "85%"
});
}); // ready
See JSFIDDLE
Notice the use of nested* velocity functions inside its complete
callback
* EDIT : if you are using Velocity's UI pack, you could use the function
.RunSequence()
to make nested animation sequences much more manageable. See updated JSFIDDLE using this method. Make sure you use the latest version of Velocity.js and UI pack (v1.1.0 as today [Oct 8, 2014]) http://cdnjs.com/libraries/velocity
Returns a preset order as in your example above
3rd > 6th > 1st > 8th > 2nd > 5th > 7th > 4th
The code is basically the same but the algorithm a little bit more complex. First we need to preset the order within an array along with other support variables:
var presetPos = [3, 6, 1, 8, 2, 5, 7, 4],
presetInit = true,
spin_count = 0;
Notice the presetPos
array contains the actual segment number and not the javascript index
. Also notice the use of the flag presetInit
, which tells if the wheel is in its initial state (we'll switch it to false
after the first spin)
The sauce of the preset algorithm is to calculate the difference (in degrees) between the current
and the next
segment in the array so we will know how many degrees the wheel need to spin :
function ordPreset() {
// initial state?
if (presetInit) {
presetInit = false;
return _deg = _deg + ((presetPos[spin_count] - 1) * 45) + 1080;
} else {
var _current = presetPos[spin_count];
var _next = presetPos[spin_count > 6 ? 0 : spin_count + 1];
var _diff = _next - _current;
spin_count = spin_count > 6 ? 0 : ++spin_count;
return _deg = _deg + (_diff * 45) + 1080;
};
};
See JSFIDDLE
Returns a random order in the 0-7 range.
The random algorithm is pretty simple. See this for reference.
function ordRandom() {
return _deg = _deg + ((Math.floor(Math.random() * (8 - 1 + 1)) + 0) * 45) + 1080;
};
However the logic inside the click
method is a little bit more complex. In your ideal world the selected segment should be completely random but never select a segment that has been used before. Once all 8
have been selected that would be the end (the button will stop spinning the wheel).
The are several factors to consider : First, we need an array to tell what segments have never been used.
var _index = [0,1,2,3,4,5,6,7];
Notice this array is about the index
of each segment.
Additionally we need a flag that let us loop (using while
) through the random selection until we find one index
that has never been used.
var _repeatRandom = true;
Then, it's important to remove the random selected index
from the array using the .splice()
method to make sure we won't use it again.
the code for the click event :
$(".skills-wheel .btn").on("click", function (e) {
// select algorithm sequential, random or preset :
// if random order, don't repeat the same
_repeatRandom = _index.length == 0 ? false : true;
// loop until find one that has never been used
while (_repeatRandom) {
ordRandom();
_target = (_deg - (360 * parseInt(_deg / 360))) / 45;
var _inArray = $.inArray(_target, _index);
if (_inArray > -1) {
// target is in the array
_repeatRandom = false; // break while loop
_index.splice(_inArray, 1); // remove segment from array so it won't be repeated
// start animation
// reset opacity of all segments to 1
$(".fancybox").parent("li").velocity({
opacity : 1
}, {
duration : 100,
complete : function () {
$(".wheel").velocity({
rotateZ : "-" + _deg + "deg"
}, {
// addtional settings and callback
duration : 3000,
complete : function (elements) {
// after spinning animation is completed, set opacity of target segment's parent
$(".fancybox").parent("li").eq(_target).velocity({
opacity : 0.4
}, {
duration : 100,
// after opacity is completed, fire targeted segment in fancybox
complete : function () {
$(".fancybox").eq(_target).trigger("click");
} // third animation completed
}); // nested velocity 2
} // second animation completed
}); // nested velocity 1
} // first animation completed
}); // velocity
}; // if
}; // while
return false;
}); // click
See JSFIDDLE
LAST NOTES :
transform
effects (.wheel.spin-1
, .wheel.spin-2
, .wheel.spin-3
, etc.)duration
of the animations, etc.) you may want to adjust to your preferences and needs.Upvotes: 7