I am trying to get results of the spinner. After 6 hours of debugging and all sorts of math attempts I can't seem to find out how to get the value of the spinner with each spin. What DIV does it land on?!
http://codepen.io/anon/pen/OMrOPe
Initially I thought the following algorithm would work.
total_rotations = Get Total Degrees in rotations (including what was done historically.
total_rotations / 360 = _total_rotations_of_a_circle
value_to_subtract = Take the absolute value of _total_rotations_of_a_circle * 360
left_over_value_in_Degree = total_rotations - value_to_subtract
left_over_value_in_Degree/60 = result.
This algorithm only works SOMETIMES. I just am not sure how to do this, any tips would help.
The aoY variable was presented by the original developer, but I don't know how to use that value to find the actual div its pointing to. What math do I need here?
$('#wheel .sec').each(function(){
var t = $(this);
var noY = 0;
var c = 0;
var n = 700;
var interval = setInterval(function () {
c++;
if (c === n) {
clearInterval(interval);
}
var aoY = t.offset().top;
$("#txt").html(aoY);
console.log(aoY);
/*23.7 is the minumum offset number that
each section can get, in a 30 angle degree.
So, if the offset reaches 23.7, then we know
that it has a 30 degree angle and therefore,
exactly aligned with the spin btn*/
if(aoY < 23.89){
console.log('<<<<<<<<');
$('#spin').addClass('spin');
setTimeout(function () {
$('#spin').removeClass('spin');
}, 100);
}
}, 10);
$('#inner-wheel').css({
'transform' : 'rotate(' + totalDegree + 'deg)'
});
noY = t.offset().top;
});
});
The formula RobG proposed is correct:
Math.ceil((( totalDegree + 30 ) % 360) / 60)
Something you also have to take into account is the fact that the offset changes for every consecutive plays. In order to deal with that, you can simply use this formula:
offset = extraDegree MOD 60
You can then replace the number 30 in the function by the offset variable:
Math.ceil((( totalDegree + offset ) % 360) / 60)
See this fiddle
Related
I have a task to make an SVG rotate in IE9.
I found the FakeSmile library which makes it rotate, but after the whole DOM is ready, which is not the behavior I want. I made an attempt to do it manually with JavaScript and ended with this code:
//init an array with values from 0 to 360 for degrees
var degrees = [];
for(var i = 0; i <= 360; i++) {
degress.push(i);
}
// function to rotate it, after it's fetched from the DOM
var rotate = function() {
var deg = degrees.shift();
element.style.msTransform = "rotate(" + deg + "deg)";
degrees.push(deg);
}
setInterval(rotate, 7);
Though it is working, I am worried if any performance hit will occur. Also if there is a better solution. Any suggestions are welcomed.
A creator function and organized objects would be a good start. Remember you shouldn't pollute the global namespace if you can avoid it.
Also de-bounce request and animation. A request every 7 millisecond is two request per frame on a 60fps screen (the most common) and there is no need to calculate and throw away frames the user never sees.
In my example i use requestAnimationFrame because that will synchronize with the screens refreshrate. On every request i check if the handle is already drawing a frame and if it isn't i schedule a frame drawing.
Notice that you can still set JavaScript variables every 7 millisecond. It's just the DOM that's slows.
EDIT 1 - No requestAnimationFrame in IE9
My mistake about requestAnimationFrame, but de-bounce is still a good idea. With de-bounce, several factors can request a change and it will still only render when relevant.
I have replaced requestAnimationFrame with setTimeout(.... 1000/60) for close to 60 fps animation.
function createRotator(element) {
var rotator;
rotator = {
degrees: 0,
element: element,
eventHandle: false,
rotate: function rotate() {
rotator.degrees = (rotator.degrees + 1) % 360;
if (rotator.eventHandle === false)
rotator.eventHandle = setTimeout(function() {
rotator.element.style.transform = "rotate(" + rotator.degrees + "deg)";
rotator.element.style.msTransform = "rotate(" + rotator.degrees + "deg)";
rotator.eventHandle = false;
}, 1000 / 60);
}
};
return rotator;
}
//TEST
var nodes = 0;
var handle;
handle = setInterval(function() {
nodes++;
if (nodes > 10) {
clearInterval(handle);
}
var testNode = document.body.appendChild(document.createElement("p"));
testNode.innerHTML = "Hello dear World!";
testNode.style.width = "115px";
testNode.style.cssFloat = "left";
testNode.style.marginTop = "100px";
var rotator = createRotator(testNode);
setInterval(rotator.rotate, 3);
}, 1000 / 4);
Yeah, with IE9, you're out of luck on CSS animations. My only suggestion would be a memory optimization
//init a variable to store the current angle
let angle = 0;
// function to rotate it
function rotate() {
angle = (++angle)%360;
element.style.msTransform = "rotate(" + angle+ "deg)";
}
setInterval(rotate, 7);
This design change also lets you change the speed of the rotation on the fly without changing the interval length. All you would change is ++angle to angle + w where w is the angular velocity.
What is also unfortunate is that you can't use requestAnimationFrame instead of an interval. Oh well. It's not the end of the world.
EDIT:
It was bugging me that the function was relying so heavily on global variables. So, here is a slightly "better", though heavier, way of doing it.
/** Takes in an element, an angular velocity, and an interval, and makes the element spin in IE9
PARAMS:
element : Element - The element we are spinning
da : Number - The angular velocity in degrees per interval
interval : Number - The number of milliseconds per interval
RETURNS:
Number - The ID of the interval that is created
**/
function makeRotate(element, da, interval){
// Variable to store angle
let a = 0;
// If da isn't provided, make it 1
da = da || 1;
// If interval isn't provided, make it 7
interval = interval || 7;
// Get the ID and make the interval
let id = window.setInterval(() => {
// Increment the angle by the angular velocity, but wrap around 360
a = (a + da)%360;
// Apply the transform to the element
element.style.msTransform = "rotate(" + a + "deg)";
}, interval);
// Return the ID of the interval
return id;
}
const intervalId = makeRotate(element, 1, 7);
Also, I made sure to return the interval id because it is always handy to be able to cancel those suckers! window.clearInterval(intervalId);
So I have a variable containing rotation in degrees, and I have an ideal rotation, and what I want is the percentage of accuracy within 20 degrees in either direction.
var actualRotation = 215
var idealRotation = 225
var accuracy = magicFunction(actualRotation, idealRotation)
In this case, the actualRotation is 10 degrees off from idealRotation, so with a 20 degree threshold in either direction, that's a 50% accuracy. So the value of accuracy would be 0.5.
var accuracy = magicFunction(225, 225) // 1.0
var accuracy = magicFunction(225, 210) // 0.25
var accuracy = magicFunction(245, 225) // 0.0
var accuracy = magicFunction(90, 225) // 0.0
How can I achieve this?
var actualRotation = 215
var idealRotation = 225
var diff = abs(actualRotation - idealRotation);
if (diff > 20)
console.log(0);
else{
accuracy = 1 - (diff/ 20);
console.log(accuracy);
}
Try this (just run code snippet):
function magicFunction(actualRotation , idealRotation ) {
var diff = Math.abs(actualRotation - idealRotation);
var accurrancy = 1 - (diff / 20);
accurrancy = accurrancy < 0 ? 0 : accurrancy;
return accurrancy;
}
console.log("225, 225: ", magicFunction(225, 225));
console.log("225, 210: ", magicFunction(225, 210));
console.log("245, 225: ", magicFunction(245, 225));
console.log("90, 225: ", magicFunction(90, 225));
The previous answers were good, but they don't handle the case where the difference crosses the zero-singularity.
E.g. when the angles are 5 and 355, you expect a difference of 10, but a simple subtraction gives 350. To rectify this, subtract the angle from 360 if it is bigger than 180.
For the above to work, you also need the angles to be in the range [0, 360). However this is a simple modulo calculation, as below.
Code:
function normalize(angle) {
if (angle < 0)
return angle - Math.round((angle - 360) / 360) * 360;
else if (angle >= 360)
return angle - Math.round(angle / 360) * 360;
else
return angle;
}
function difference(angle1, angle2) {
var diff = Math.abs(normalize(angle1) - normalize(angle2));
return diff > 180 ? 360 - diff : diff;
}
function magicFunction(actualRotation, idealRotation, limit) {
var diff = difference(actualRotation, idealRotation);
return diff < limit ? 1.0 - (diff / limit) : 0.0;
}
// tests
console.log(difference(10, 255)); // 115 (instead of the incorrect answer 245)
console.log(magicFunction(5, 355, 20)); // 0.5 (instead of 0 as would be returned originally)
EDIT: a graphical illustration of why the previous method would be insufficient:
I was working on a fun project that implicates creating "imperfect" circles by drawing them with lines and animate their points to generate a pleasing effect.
The points should alternate between moving away and closer to the center of the circle, to illustrate:
I think I was able to accomplish that, the problem is when I try to render it in a canvas half the render jitters like crazy, you can see it in this demo.
You can see how it renders for me in this video. If you pay close attention the bottom right half of the render runs smoothly while the top left just..doesn't.
This is how I create the points:
for (var i = 0; i < q; i++) {
var a = toRad(aDiv * i);
var e = rand(this.e, 1);
var x = Math.cos(a) * (this.r * e) + this.x;
var y = Math.sin(a) * (this.r * e) + this.y;
this.points.push({
x: x,
y: y,
initX: x,
initY: y,
reverseX: false,
reverseY: false,
finalX: x + 5 * Math.cos(a),
finalY: y + 5 * Math.sin(a)
});
}
Each point in the imperfect circle is calculated using an angle and a random distance that it's not particularly relevant (it relies on a few parameters).
I think it's starts to mess up when I assign the final values (finalX,finalY), the animation is supposed to alternate between those and their initial values, but only half of the render accomplishes it.
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I can't figure it out, thanks in advance!
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I Think that your animation function has not care about the elapsed time. Simply the animation occurs very fast. The number of requestAnimationFrame callbacks is usually 60 times per second, So Happens just what is expected to happen.
I made some fixes in this fiddle. This animate function take care about timestamp. Also I made a gradient in the animation to alternate between their final and initial positions smoothly.
ImperfectCircle.prototype.animate = function (timestamp) {
var factor = 4;
var stepTime = 400;
for (var i = 0, l = this.points.length; i < l; i++) {
var point = this.points[i];
var direction = Math.floor(timestamp/stepTime)%2;
var stepProgress = timestamp % stepTime * 100 / stepTime;
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
}
}
Step by Step:
based on comments
// 1. Calculates the steps as int: Math.floor(timestamp/stepTime)
// 2. Modulo to know if even step or odd step: %2
var direction = Math.floor(timestamp/stepTime)%2;
// 1. Calculates the step progress: timestamp % stepTime
// 2. Convert it to a percentage: * 100 / stepTime
var stepProgress = timestamp % stepTime * 100 / stepTime;
// if odd invert the percentage.
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
// recompute position based on step percentage
// factor is for fine adjustment.
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
I have a image which always points in the direction of the mouse.
At some point the angle goes from 180deg to -180deg, how do I get the image to take the short angle instead of doing full circle?
// Find ship angle (Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;).
var mouseAngle = getAngle(FIREFLY.CENTER.X, FIREFLY.CENTER.Y, currentMousePos[0], currentMousePos[1]);
var turnDegrees = mouseAngle - FIREFLY.ANGLE;
var maxDegrees = 5;
console.log(mouseAngle + " " + FIREFLY.ANGLE);
if (turnDegrees > -5 && turnDegrees < 5) {
// Do nothing.
} else if (turnDegrees < 0) {
FIREFLY.ANGLE -= 5;
} else {
FIREFLY.ANGLE += 5;
}
// Set ship direction.
FIREFLY.style.transform = 'rotate(' + (FIREFLY.ANGLE + 90) + 'deg)';
Fiddle
Angles are the same modulo 360 degrees, so to avoid big rotations like 359° you choose -1° and to avoid -359° you choose 1°
You could do something like
var turnDegrees = (mouseAngle - FIREFLY.ANGLE) ;
if(-540<turnDegrees&&turnDegrees <=-180) turnDegrees+=360;
else if(180<turnDegrees&&turnDegrees<=540) turnDegrees-=360;
but it turns out FIREFLY.ANGLE can take large values, so to avoid plenty of other if clauses, a general formula is better. Here is one using modulo
var turnDegrees = mod(mouseAngle - FIREFLY.ANGLE +180, 360) -180;
function mod(x, value){ // Euclidean modulo
return x>=0 ? x%value : value+ x%value;
}
plot test
fiddle updated: http://jsfiddle.net/crl/2rz296tf/31
You could turn off CSS transforms, and do the interpolation yourself.
Try calculating the difference between the angles when the mouse moves and add that to a total angle, that way the angle wont be bound to (-180, 180)
I'm making some simple css3 watch and its working like this (just calculates mins, secs and hours rotation and apply it
var updateWatch = function() {
var seconds = new Date().getSeconds();
var hours = new Date().getHours();
var mins = new Date().getMinutes();
var sdegree = seconds * 6;
var srotate = "rotate(" + sdegree + "deg)";
var hdegree = hours * 30 + (mins / 2);
var hrotate = "rotate(" + hdegree + "deg)";
var mdegree = mins * 6;
var mrotate = "rotate(" + mdegree + "deg)";
$(".jquery-clock-sec").css({"-moz-transform" : srotate, "-webkit-transform" : srotate});
$(".jquery-clock-hour").css({"-moz-transform" : hrotate, "-webkit-transform" : hrotate});
$(".jquery-clock-min").css({"-moz-transform" : mrotate, "-webkit-transform" : mrotate});
}
All animations has some easing.
And all works well but when some marker makes full rotate then 360deg becomes 0deg and then marker makes all circle back. Is there any simple way to avoid it?
It is logical that the marker goes backwards when you change it from 359 deg to 0 deg.
The logical answer would be to avoid truncating the data.
I would get the time (fractionary part), convert it to seconds, convert that to degrees, and use that.
Don't worry if the resulting number is a zillion degrees, it will map to the correct position.
And it will wrap ok when going from a zillion degrees to a zillion + 1, when that happens to make a new rotation.
Just to avoid accuracy problems, as I said before, use only the time excluding the day.