Difficulty Creating an Infinite Anti-Clockwise Carousel using JavaScript - javascript

I'm having quite a bit of trouble with this, and after working tirelessly on it I've decided to just post my problem on here. My boss wants me to figure out a problem with his org-board. The problem arises when the user wants to move from division 6 to division 7--instead of it going anti-clockwise to division 7, it rotates all the way back to division 6 going clockwise. I understand why it's doing this, but I haven't figured out how to make it go anti-clockwise to the next panel without having it rotate all the way back to division 7. I have a CodePen for it, figured it would be easier than posting a ton of code on here: https://codepen.io/jamesaluna/pen/gNRWNJ . If you're not quite sure what I'm trying to exactly do please ask me, thank you!
I tried creating an 'm' variable that calculated the amount of rotations, but that hasn't done much good. I also put the elements in an array and fiddled around with that a bit, using a for/in loop, but I have yet to figure out a solution using that.
var carousel = document.querySelector('.carousel');
var cellCount = 7;
var selectedIndex = 1;
function rotateCarousel() {
var angle = selectedIndex / cellCount * -360;
carousel.style.transform = 'translateZ(-898px) rotateY(' + angle + 'deg)';
}
rotateCarousel();
var sevenButton = document.querySelector('.seven-button');
sevenButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 1;
if (selectedIndex == 7) {
selectedIndex = 1 ;
}
rotateCarousel();
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
var oneButton = document.querySelector('.one-button');
oneButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 2;
rotateCarousel();
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
var twoButton = document.querySelector('.two-button');
twoButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 3;
rotateCarousel();
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
var threeButton = document.querySelector('.three-button');
threeButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 4;
rotateCarousel();
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
var fourButton = document.querySelector('.four-button');
fourButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 5;
rotateCarousel();
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
var fiveButton = document.querySelector('.five-button');
fiveButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 6;
rotateCarousel();
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
var sixButton = document.querySelector('.six-button');
sixButton.addEventListener( 'click', function() {
var m = Math.floor(selectedIndex / cellCount);
selectedIndex = m + 7;
rotateCarousel();
if (selectedIndex == 7) {
selectedIndex = 6 ;
}
document.getElementById("demo").innerHTML = m;
document.getElementById("demo2").innerHTML = selectedIndex;
});
``` CSS
.carousel__cell:nth-child(7n+3) { background: hsla( 360, 100%, 100%, 1); border: 2px solid #B754F7;}
.carousel__cell:nth-child(7n+4) { background: hsla( 360, 100%, 100%, 1);border: 2px solid #FF91FF; }
.carousel__cell:nth-child(7n+5) { background: hsla( 360, 100%, 100%, 1);border: 2px solid #009C00; }
.carousel__cell:nth-child(7n+6) { background: hsla( 360, 100%, 100%, 1);border: 2px solid #999; }
.carousel__cell:nth-child(7n+7) { background: hsla( 360, 100%, 100%, 1);border: 2px solid #FFED8A; }
.carousel__cell:nth-child(7n+1) { background: hsla( 360, 100%, 100%, 1); border: 2px solid #3A43F9; }
.carousel__cell:nth-child(7n+2) { background: hsla( 360, 100%, 100%, 1); border: 2px solid #F4D100; }
.carousel__cell:nth-child(7) { transform: rotateY( 0deg) translateZ(1030px); }
.carousel__cell:nth-child(1) { transform: rotateY( 51.428deg) translateZ(1030px); }
.carousel__cell:nth-child(2) { transform: rotateY(102.856deg) translateZ(1030px); }
.carousel__cell:nth-child(3) { transform: rotateY(154.284deg) translateZ(1030px); }
.carousel__cell:nth-child(4) { transform: rotateY(205.712deg) translateZ(1030px); }
.carousel__cell:nth-child(5) { transform: rotateY(257.14deg) translateZ(1030px); }
.carousel__cell:nth-child(6) { transform: rotateY(308.568deg) translateZ(1030px); }

It has to do with how high your angle of rotation is set and if it is negative or positive. When you get to 6, you have a high angle of rotation, and then to go 'back' to 7, it calculates a heavy rotation value for your angle. It sounds like what you want is to see which angle of rotation is the closest (forward or backwards) and go that direction. That way it won't always go backwards from 6 to 7 and instead go forwards if the ANGLE is closer (rather than the index.
To do that, modify your rotation function with a conditional like so:
function rotateCarousel() {
var angle = selectedIndex / cellCount * -360;
if (angle < -180)
angle = angle % 360;
carousel.style.transform = 'translateZ(-898px) rotateY(' + angle + 'deg)';
}
That way if you're making a rotation that is more than half the full direction, it goes the other way instead. What you desire is to not make a rotation more than 180 deg (esp from 6 to 7, which is 300+ deg) if that makes sense. So we check and go the other direction around the circle instead.

Related

Why the value of end point of element is wrong when using a function

The problem is solved when adding angles individually and then using ttheta(without calling a function to add angles and than using ttheta), but can anyone tell about why using function here is wrong or what problem is this function causing
The issue is solved by using this:
dtransform = window.getComputedStyle(leg1, null).getPropertyValue("transform");
values = dtransform.split('(')[1].split(')')[0].split(',');
dtheta = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
dtransform1 = window.getComputedStyle(leg2, null).getPropertyValue("transform");
values1 = dtransform1.split('(')[1].split(')')[0].split(',');
dtheta1 = Math.round(Math.atan2(values1[1], values1[0]) * (180 / Math.PI));
ttheta = dtheta + dtheta1;
Instead of using function.
What I am trying to achieve is to get value of end points of an element when it is rotated from left and top of browser.
X & Y values are max-distance of end points of shoe
I get right values at some points and wrong at some. I tried to add angle from the parent element but that also don't solve the problem.
This is the related answer from which I had taken help
To check values are right or wrong I added an event to get clientX of mouse click. And values of element positions are taken when Try button is clicked.
Am I doing something wrong, any insights will be really helpful
let leg1 = document.querySelector(".Leg1Shoe")
let leg2 = document.querySelector(".Leg1Part")
let animeAll = document.querySelectorAll(".allClass")
let animePause = false
let ttheta = 0;
function getPos() {
if (!animePause) {
animeAll.forEach(e => {
e.classList.add("AnimatePaused");
})
animePause = true;
} else {
animeAll.forEach(e => {
e.classList.remove("AnimatePaused");
})
animePause = false;
}
let h, w, x, dx, tx, y, dy, ty = "";
leg1.style.outline = "1px solid red"
h = leg1.offsetHeight;
w = leg1.offsetWidth;
x = leg1.getBoundingClientRect().left;
y = leg1.getBoundingClientRect().top;
func2(leg2);
func2(leg1);
dx = (Number(h * (Math.sin(ttheta * (Math.PI / 180)))) + Number(w * (Math.cos(ttheta * (Math.PI / 180))))).toFixed(2);
dy = (Number(w * (Math.sin(ttheta * (Math.PI / 180)))) + Number(h * (Math.cos(ttheta * (Math.PI / 180))))).toFixed(2);
tx = (Number(x) + Number(Math.abs(dx))).toFixed(2);
ty = (Number(y) + Number(Math.abs(dy))).toFixed(2);
console.log("X:" + tx, "Y:" + ty);
}
function func2(e) {
let dtransform, dtheta, values = "";
dtransform = window.getComputedStyle(e, null).getPropertyValue("transform");
if (dtransform != "none") {
values = dtransform.split('(')[1].split(')')[0].split(',');
dtheta = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
} else {
dtheta = 0;
};
ttheta = Number(ttheta) + Number(dtheta);
}
leg1.addEventListener('click', mousePos);
function mousePos(e) {
console.log("X:" + e.clientX, "Y:" + e.clientY)
}
.Leg1Part {
position: relative;
left: 100px;
top: 43px;
width: 20px;
height: 75px;
background-color: green;
transform-origin: top center;
animation: animateLeg1Part 5.0s linear infinite alternate;
}
#keyframes animateLeg1Part {
0% {
transform: rotate(40deg);
}
25% {
transform: rotate(25deg);
}
50% {
transform: rotate(10deg);
}
75% {
transform: rotate(30deg);
}
100% {
transform: rotate(60deg);
}
}
.Leg1Shoe {
position: absolute;
left: 0px;
top: 73px;
width: 40px;
height: 20px;
background-color: blue;
transform-origin: center left;
animation: animateLeg1Shoe 5.0s linear infinite alternate;
}
#keyframes animateLeg1Shoe {
0% {
transform: rotate(15deg);
}
50% {
transform: rotate(-20deg);
}
100% {
transform: rotate(30deg);
}
}
.AnimatePaused {
animation-play-state: paused;
}
<div class="Leg1Part allClass">
<div class="Leg1Shoe allClass"></div>
</div>
<button onclick="getPos()">Try</button>
Thanks for help in advance
This is not a simple answer that can give you a complete solution, but just an outline of the process you can follow.
You get the transformation matrices for the "leg" and the "shoe", as you already do, by calling getPropertyValue("transform") This gives you a string like this: matrix(-0.568718, 0.822533, -0.822533, -0.568718, 0, 0), this is a shortened form of a 3x3 transformation matrix:
| cos(theta) -sin(theta) 0 |
| sin(theta) cos(theta) 0 |
| 0 0 1 |
Parse the string and create a 2d array for this matrix. Note: since you don't have any translation (two zeros in the last column) you can operate on 2x2 matrices.
Multiple the transformation matrices for the "leg" and the "shoe". It's a tedious process, and you can use the mathjs library to help.
Multiply the resulting transformation matrix by the vector of each point's original coordinates. This will give you the coordinates of the point with all rotations applied.
Here are some references:
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix()
https://en.wikipedia.org/wiki/Affine_transformation
http://web.cse.ohio-state.edu/~shen.94/681/Site/Slides_files/transformation_review.pdf
https://en.wikipedia.org/wiki/Matrix_multiplication_algorithm

Zoom with transform:matrix(): how to stick to the left edge (alter zoom center)? [duplicate]

This question already has answers here:
How to scale from left to right only
(2 answers)
How to scale from right to left in CSS?
(2 answers)
Closed 3 years ago.
My client wants to have zoom functionality (I found that this is easy to implement with transform: scale() but after that he wants to have the top-left edge of a node at the same position. Sadly in school instead of math I played to WarCraft III and Space Rangers (iconic Russian game, try it), so now I do not know what kind of formula I need :(
One zoom position:
Another zoom position: as you see at the left we have empty "margin":
Minimum reproducible example is here:
const p = document.querySelector('p');
for (let i = 0; i < 50; i++) {
p.innerText += ' ' + Math.random() + Math.random();
}
document.querySelector('input').onchange = event => {
p.style.setProperty('--zoom', event.target.value);
}
input {
z-index: 1;
position: absolute;
}
p {
transform: matrix(
calc(var(--zoom) / 100),
0,
0,
calc(var(--zoom) / 100),
/* Next two lines is not perfect, what I need to write here? */
calc(var(--zoom) * var(--zoom) / 65 - 140),
calc(var(--zoom) * var(--zoom) / 1000)
);
}
<input type='range' min='10' max='200'></input>
<p></p>
Simple: alter origin with transform-origin: 0 0;
Original origin:
Here for example origin set at 30px 30px:
Read more https://dev.opera.com/articles/understanding-the-css-transforms-matrix/#coordinates
const p = document.querySelector('p');
for (let i = 0; i < 50; i++) {
p.innerText += ' ' + Math.random() + Math.random();
}
document.querySelector('input').onchange = event => {
p.style.setProperty('--zoom', event.target.value);
}
input {
z-index: 1;
position: absolute;
}
p {
transform-origin: 0 0;
transform: matrix(
calc(var(--zoom) / 100),
0,
0,
calc(var(--zoom) / 100),
0,
0
);
}
<input type='range' min='10' max='200'></input>
<p></p>

Modifying CSS position with JS

I'm using document.getElementById("").style.top = var; to randomly change the position of an object every time I click on it, but I can only randomly change the position by pixels and not percentage. How do I randomly generate a number with % ?
document.getElementById("randObject").onclick = function () {
var bColor = "#"+((1<<24)*Math.random()|0).toString(16);
var yAxis = Math.random()*100;
var xAxis = Math.random()*100;
document.getElementById("randObject").style.backgroundColor = bColor;
document.getElementById("randObject").style.top = yAxis;
document.getElementById("randObject").style.left = xAxis;
}
document.getElementById("randObject").style.top = yAxis + "%";
document.getElementById("randObject").style.left = xAxis + "%";
I should point out that your color generator is also not valid 1/16 of the time. You need to left-zero-pad the string:
"#" + ("00000" + ((1<<24) * Math.random() | 0).toString(16)).slice(-6);
Another shortcut you can use to save DOM lookup time is to use the this keyword in your click event. All together it looks like:
Lastly, you might consider using .addEventListener('click', function() { ... }) since that is the current standard, though what you're doing works alright if you reeeally want to support IE6.
Let's check out the demo since this looks like a pretty neat script now:
document.getElementById("randObject").addEventListener('click', function() {
var bColor = "#" + ("00000" + ((1<<24) * Math.random() | 0).toString(16)).slice(-6);
var yAxis = Math.random() * 100;
var xAxis = Math.random() * 100;
this.style.backgroundColor = bColor;
this.style.top = yAxis + "%";
this.style.left = xAxis + "%";
});
:root {
/* change this value to update the size of playing field and #randObject */
--size: 50px;
}
html {
position: absolute;
margin: 0 var(--size) var(--size) 0;
padding: 0;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
body {
margin: 0;
padding: 0;
}
#randObject {
position: absolute;
top: 0;
left: 0;
width: var(--size);
height: var(--size);
background-color: #000000;
border-radius: 50%;
cursor: pointer;
transition-property: top, left, background-color;
transition-duration: 0.4s;
}
<div id="randObject"></div>
I added positioning to your code and I was able to make it to move by percentage.
<img id="randObject" width=100 height=100>
<script>
document.getElementById("randObject").onclick = function () {
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var bColor = "#"+((1<<24)*Math.random()|0).toString(16);
var yAxis = Math.random() + '%';
var xAxis = Math.random() + '%';
document.getElementById("randObject").style.backgroundColor = bColor;
document.getElementById("randObject").style.top = yAxis;
document.getElementById("randObject").style.left = xAxis;
document.getElementById("randObject").style.position = 'absolute';
}
</script>

javascript - dynamic variable name of setInterval()

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div onmouseover="animatedStickers($(this), 17, 5, 4, 2, 3)" style="float: left; background-image: url('http://googledrive.com/host/0B-UH4_eX_YisdlJ4cU9qZ1lwM3c/Tuzki1.png'); background-size: 380px 304px; cursor: pointer; height: 64px; width: 64px; background-position: -6px -6px; color: transparent">Tuzki</div>
<div onmouseover="animatedStickers($(this), 16, 4, 4, 4, 3)" style="float: left; background-image: url('http://googledrive.com/host/0B-UH4_eX_YisdlJ4cU9qZ1lwM3c/Tuzki2.png'); background-size: 304px 304px; cursor: pointer; height: 64px; width: 64px; background-position: -6px -6px; color: transparent">Tuzki</div>
<div onmouseover="animatedStickers($(this), 22, 5, 5, 2, 3)" style="float: left; background-image: url('http://googledrive.com/host/0B-UH4_eX_YisdlJ4cU9qZ1lwM3c/Tuzki3.png'); background-size: 380px 380px; cursor: pointer; height: 64px; width: 64px; background-position: -6px -6px; color: transparent">Tuzki</div>
<script>
var loop = 1;
var stickerInterval = function (element, x, y, last) {
var pos = $(element).css('backgroundPosition').split(' ');
var xPos = parseInt(pos[0].split('px')[0]) - 6 - 6 - 64;
var yPos = parseInt(pos[1].split('px')[0]);
var maxX = ((-6 - 6 - 64) * (x - 1)) - 6;
var maxY = ((-6 - 6 - 64) * last) - 6;
if (loop == y && xPos == maxY) {
// end 1 turn
loop = 1;
xPos = -6;
yPos = -6;
} else if (loop < y && xPos < maxX) {
xPos = -6;
yPos = yPos -6 -6 -64;
loop++;
}
$(element).css('background-position', xPos + 'px ' + yPos + 'px');
};
var animatedStickers = function (element, total, x, y, last, times) {
$(element).removeAttr('onmouseover');
var interval = setInterval(function () { stickerInterval(element, x, y, last) }, 175);
setTimeout(function () {
clearInterval(interval);
loop = 1;
$(element).css('background-position', '-6px -6px');
$(element).attr('onmouseover', 'animatedStickers($(this), ' + total + ', ' + x + ', ' + y + ', ' + last + ', ' + times + ')')
}, 175 * total * times);
};
</script>
I wanna use multiple setInterval() and clear all of them NOT in a time.
<div id="div1" onmouseover="divOver($(this))"></div>
<div id="div2" onmouseover="divOver($(this))"></div>
<script>
var divOver = function (element) {
var id = $(element).attr('id'); // get id
//call setInterval() without the id
var interval = setInterval(function(){ /* do something... */ }, 500);
//clear interval after 1s
setTimeout(function(){ clearInterval(interval) }, 1000);
};
</script>
That code doesn't work fine if I mouseover 2 divs at the same time.
I think: The first I mouseover on div1, function divOver creates a variable name interval. After that (haven't cleared interval yet), I mouseover on div2, function divOver comtinues creating a new variable with the same name interval. So, the first interval can be overridden. Is it right?
To avoid that problem, I think about using setInterval() with id. Something's like this:
var id = $(element).attr('id');
//var interval_ + id = setInterval(function(){ /* do something... */ }, 500);
But that's not javascript syntax. Can you give me any idea to fix this problem?
To answer your question how to maintain a record of different intervals at the same time and being able to start and stop them outside the function scope.
You need to keep an associative arrays with the intervals, such that there can be many intervals at the same time.
<div id="div1" onmouseover="divOver($(this))"></div>
<div id="div2" onmouseover="divOver($(this))"></div>
<script>
var intervals = []
var divOver = function (element) {
var id = element.attr('id'); // get id
//call setInterval() with the id
intervals['i'+id] = setInterval(function(){ /* do something... */ }, 500);
//clear interval after 1s
setTimeout(function(){ clearInterval(intervals['i'+id]) }, 1000);
};
</script>
Though as already mentioned this does most likely not solve your real problem.

HTML5 Canvas & javascript prize wheel

I am having trouble trying to implement a "prize wheel," using canvas. I am using something similar to the canvas "roulette wheel" http://jsfiddle.net/wYhvB/4/ that is floating around StackOverflow. My dilema is when you click spin, in the background I make an API call that returns an id of which prize should actually be chosen, the interface is nothing more than eye candy. I am pushing all the prize descriptions into the first array, how can I add an id into each one of the arcs and stop on a particular one instead of stopping on a specifically random time? I.E. If the API returns "car," I want this wheel to spin a few times and stop on car.
var colors = ["##eaeaea", "##cccccc", "##eaeaea", "##cccccc",
"##eaeaea", "##cccccc", "##eaeaea", "##cccccc"];
// NEED to pre load this data prior
var prize_descriptions = ["car","house","etc..."]; // These are injected on an init call from an api
var current_user_status = {};
var startAngle = 0;
var arc = Math.PI / 4;
var spinTimeout = null;
var spinArcStart = 10;
var spinTime = 0;
var spinTimeTotal = 0;
var current_user_status = null;
var spin_results = null;
var ctx;
function drawSpinnerWheel() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var outsideRadius = 200;
var textRadius = 160;
var insideRadius = 125;
ctx = canvas.getContext("2d");
ctx.clearRect(0,0,500,500);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.font = 'bold 12px Helvetica, Arial';
for(var i = 0; i < 8; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
ctx.save();
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 0;
ctx.shadowColor = "rgb(220,220,220)";
ctx.fillStyle = "black";
ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius,
250 + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var text = prize_descriptions[i];
if (text == undefined)
text = "Not this time! "+i;
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
ctx.restore();
}
//Arrow
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(250 - 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 0, 250 - (outsideRadius - 13));
ctx.lineTo(250 - 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius + 5));
ctx.fill();
}
}
function spin() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
}
function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawSpinnerWheel();
spinTimeout = setTimeout('rotateWheel()', 30);
}
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
ctx.save();
ctx.font = 'bold 30px Helvetica, Arial';
var text = prize_descriptions[index];
ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10);
ctx.restore();
}
function easeOut(t, b, c, d) {
var ts = (t/=d)*t;
var tc = ts*t;
return b+c*(tc + -3*ts + 3*t);
}
drawSpinnerWheel();
$("#spin").bind('click', function(e) {
e.preventDefault();
spin();
});​
I have some experience in creating HTML5 canvas winning wheels, the way that I solved the problem of getting the wheel to stop at at a particular prize determined by a server-side process was to define an array of prizes that correspond to what is shown on the wheel, specifying the start and end angle of each prize in degrees, and then before the wheel is rotated setting a targetAngle to a random value between the start and end angle of the prize in question plus adding a multiple of 360 degrees so that the wheel spins a few times before slowing to a stop at the predetermined prize.
For example if the wheel has 4 prizes then the prizes array is...
var prizes = new Array();
prizes[0] = {"name" : "Prize 1", "startAngle" : 0, "endAngle" : 89};
prizes[1] = {"name" : "Prize 2", "startAngle" : 90, "endAngle" : 179};
prizes[2] = {"name" : "Prize 3", "startAngle" : 180, "endAngle" : 269};
prizes[3] = {"name" : "Prize 4", "startAngle" : 270, "endAngle" : 359};
And the code to set the targetAngle is something like...
targetAngle = Math.floor(prizes[determinedPrize]['startAngle'] + (Math.random() * (prizes[determinedPrize]['endAngle'] - prizes[determinedPrize]['startAngle'])));
targetAngle += (360 * 18);
The spinning function of the wheel then loops until the current angle of the wheel equals the targetAngle.
A working example of my prize wheel and the fully commented source code is available at http://www.dougtesting.net. The pre-determined feature is not enabled in the online example, but can easily be turned on in the source code (winwheel.js) once downloaded.
The easiest way I can think of is to take the current position of the wheel, then calculate the distance from this point to the prize. Add a random number of multiples of the wheel diameter circumference and then you have a distance. The edge of the wheel must travel through this distance to end up at the prize.
Just like you can use linear or cubic interpolation to move an element from 1 position to another in a specified number of steps, you can use the same approach to rotate the wheel from point 0 (start point) to point 1 (end point) from time=0 to time=1
This page Math: Ease In, ease Out a displacement using Hermite curve with time constraint is a good read. It's where I managed to wrap my head around doing basically the same thing - just up/down/left/right, rather than rotationally.
It's a bit choppy while im look at iot just now. Dont know if it's jsfiddle, the missing images or the 25 browser tabs & programs I have running.
Anyway, the point is to use non-linear interpolation to get to a specified distance away in a specified number of steps. It should get there in a specified time, but not with 25 windows open.. :laughs:
Check out the SO link above. It's got some great pictures that really explain quite well.
Here's a fiddle of cubic-spline interpolation for the time.
http://jsfiddle.net/enhzflep/XKzGF/
And here's the full code:
<!DOCTYPE html>
<html>
<head>
<script>
var continuePlaying = true, isPlaying=false;
function byId(a){return document.getElementById(a)}
function myInit()
{
}
window.addEventListener("load",myInit,!1);
function cubicHermite(a,b,d,e,c){var g=a*a,f=g*a;return(2*f-3*g+1)*b+(f-2*g+a)*e+(-2*f+3*g)*d+(f-g)*c}
function interp(a,b,d,e,c){var g,f;f=e/(a/2+b+d/2);g=f*a/2;f*=b;return result=c<=a?cubicHermite(c/a,0,g,0,f/b*a):c<=a+b?g+f*(c-a)/b:cubicHermite((c-a-b)/d,g+f,e,f/b*d,0)}
function linear(a){return a}
function cubic(a){return interp(0.35,0.3,0.35,1,a)}
function getSize(a){return{left:a.offsetLeft,top:a.offsetTop,width:a.clientWidth,height:a.clientHeight}}
function doAnim2(a,b,d,e){var c=a/b;setTimeout(function(){doAnimStep(0,b,c,d,e)},c)}
function doAnimStep(a,b,d,e,c){a<=b?(setTimeout(function(){doAnimStep(a,b,d,e,c)},d),e(a/b),a++):void 0!=c&&null!=c&&c()}
//scroll with cubic interpolation of the current scroll position
function cubicScrollDown(b,callback)
{
var a=byId(b),c=a.scrollHeight-a.clientHeight;
doAnim2(500,c,function(b){a.scrollTop=c*cubic(b)},callback);
}
function cubicScrollUp(b,callback)
{
var a=byId(b),c=a.scrollHeight-a.clientHeight;
doAnim2(500,c,function(b){ a.scrollTop=c*(1-cubic(b)) },callback );
}
//scroll with cubic interpolation of the current scroll position
function linearScrollDown(b, callback)
{
var a=byId(b),c=a.scrollHeight-a.clientHeight;
doAnim2(500,c,function(b){a.scrollTop=c*linear(b)}, callback);
}
function linearScrollUp(b, callback)
{
var a=byId(b),c=a.scrollHeight-a.clientHeight;
doAnim2(1000,c,function(b){ a.scrollTop=c*(1-linear(b)) }, callback );
}
function animFadeOut(elem, callback)
{
doAnim2(500,50,function(raw){elem.style.opacity=1-cubic(raw)},callback);
}
function animFadeIn(elem, callback)
{
doAnim2(500,50,function(raw){elem.style.opacity=cubic(raw)},callback);
}
function cubicBounce(b)
{
cubicScrollDown(b, downCallback);
function downCallback()
{
cubicScrollUp(b, upCallback);
}
function upCallback()
{
if (continuePlaying===true)
setTimeout( function(){cubicBounce(b);}, 0);
else
continuePlaying = true;
}
}
function linearBounce(b)
{
linearScrollDown(b, downCallback);
function downCallback()
{
linearScrollUp(b, upCallback);
}
function upCallback()
{
if (continuePlaying===true)
setTimeout( function(){linearBounce(b);}, 0);
else
continuePlaying = true;
}
}
function fadeOutIn(tgtListIdStr)
{
var tgt = byId(tgtListIdStr);
animFadeOut(tgt,fadedOutCallback);
function fadedOutCallback()
{
animFadeIn(tgt);
}
}
function prependChild(parent, element)
{
if (parent.childNodes)
parent.insertBefore(element, parent.childNodes[0]);
else
parent.appendChild(element)
}
function slideUpRemove(tgtListIdStr)
{
var tgt = byId(tgtListIdStr);
var listItems = tgt.getElementsByTagName('li');
mHeight = listItems[0].clientHeight;
animFadeOut(listItems[0], slideUp);
function slideUp()
{
doAnim2(500, 50, slideUpStep, slideUpDone);
function slideUpStep(raw)
{
listItems[0].style.height = (cubic(1-raw) * mHeight) + 'px';
}
function slideUpDone()
{
dummy = listItems[0];
tgt.appendChild(dummy);
//dummy.removeAttribute('style');
dummy.style.height = null;
dummy.style.opacity = null;
}
}
}
function slideDownInsert(tgtListIdStr)
{
// get the container, it's items and the height of the last LI item.
var tgt = byId(tgtListIdStr);
var listItems = tgt.getElementsByTagName('li');
mHeight = listItems[listItems.length-1].clientHeight;
// create a dummy to take the place of the last item, set it's size and height. make it the first child of the containing list
var dummy = document.createElement('li');
dummy.style.opacity = 0;
dummy.style.height = 0 + 'px';
prependChild(tgt, dummy);
// animate it!
doAnim2(500, 50, slideDownStep,slideDownDone);
function slideDownStep(raw)
{
dummy.style.height = (cubic(raw) * mHeight)+'px';
}
function slideDownDone()
{
// remove the dummy
var newItem = listItems[listItems.length-1];
newItem.style.opacity = 0;
prependChild(tgt, newItem);
tgt.removeChild(dummy);
animFadeIn(newItem, function(){newItem.removeAttribute('style')});
}
}
</script>
<style>
#myListDiv
{
width: 256px;
padding: 6px;
height: 128px;
overflow-y: hidden; /*scroll;*/
border-radius: 6px;
border: solid 1px transparent;
border-color: rgba(0,0,0,0.2) rgba(255,255,255,0.4) rgba(255,255,255,0.4) rgba(0,0,0,0.2);
/* background-image: url(img/rss128.png); */
background-color: rgba(0,0,0,0.1);
}
h4, p
{
margin: 6px 0;
}
ul
{
padding: 0;
list-style: none;
margin: 0;
}
ul#mList li
{
padding: 0 8px;
margin: 0 6px;
display: block;
border: solid 1px #cccccc;
border-bottom-color: #000;
border-color: #ccc transparent #000 transparent;
vertical-align: middle;
background-color: rgba(150,150,150,0.95);
overflow: hidden;
}
.thumb
{
width: 48px;
height: 48px;
float: left;
}
.thumb img
{
height: 48px;
}
#mPanel
{
display: inline-block;
float: left;
padding: 32px;
background-color: hsl(80,50%,20%);
}
</style>
</head>
<body>
<div id='mPanel'>
<div id='myListDiv'>
<ul id='mList'>
<li><div class='thumb'><img src='img/opera.svg'></div><div class='itemData'><h4><a>Item #1</a></h4><p>some assorted text</p></div></li>
<li><div class='thumb'><img src='img/chromeEyes.svg'></div><h4><a>Item #2</a></h4><p>some assorted text</p></li>
<li><div class='thumb'><img src='img/girl.png'></div><h4><a>Item #3</a></h4><p>some assorted text</p></li>
<li><div class='thumb'><img src='img/chuck-norris.jpg'></div><h4><a>Item #1</a></h4><p>some assorted text</p></li>
<li><div class='thumb'><img src='img/redBaron.jpg'></div><h4><a>Item #2</a></h4><p>some assorted text</p></li>
<li><div class='thumb'><img src='img/default.png'></div><h4><a>Item #3</a></h4><p>some assorted text</p></li>
</ul>
</div>
</div>
<button onclick='cubicScrollDown("myListDiv")'>Cubic down</button>
<button onclick='cubicScrollUp("myListDiv")'>Cubic up</button><br>
<button onclick='cubicBounce("myListDiv")'>cubic bounce</button>
<button onclick='linearBounce("myListDiv")'>linear bounce</button><br>
<input type='button' onclick='slideUpRemove("mList")' value='newest'/>
<input type='button' onclick='slideDownInsert("mList")' value='Slide Down'/><br>
<button onclick='continuePlaying=false'>Stop Anim cycle</button>
<input type='button' onclick='fadeOutIn("mList");' value='fadeOutIn'/><br>
</body>
</html>

Categories

Resources