I'm working on a wheel of fortune project. Each time someone press "spin" button, we get the prize from an api and calculate the angle based on our target gift and then rotate. In some cases, on iphone, after rotation the result doesn't match our target gift. Therefore I'm looking for a way to double check the result with target and keep rotating if they doesn't match. here is my rotation Javascrip functions:
function startRotation(target){
let giftIndex = parseInt(target.attr('id'))
angle = 360 - ((giftIndex - 1) * (360 / wheelItems.lenght)) - ((360 / wheelItems.lenght) / 3)
angle = angle === 360 ? 0 : angle
rotate(target)}
function rotate(target) {
randomNum = angle + (1800 * ri);
ri++
$(".mainCircle").css('transform', `translate(-50%, -50%) rotate(${randomNum}deg)`)
setTimeout(() => {
winPrize(target)
}, 7500)}
and here is where I add gifts to the wheel:
function createCircle(wheelItems){
let colors = ['#16A085', '#2989B9', '#8E44AD', '#F39C12', '#D35400', '#C0392B', 'cyan', 'brown', 'gray', 'pink', 'maroon', 'gold']
let count = wheelItems.lenght
for (let i = 0; i < count; i++) {
let img = document.createElement('img')
let text = document.createElement('span')
text.innerHTML = wheelItems[i].title
img.src = '/wheel/'+wheelItems[i].title;
img.style.transform = 'skewY('+ Math.abs(90 - (360 / count)) +'deg) rotate('+ 360 / count / 2 + 'deg)'
img.style.width = (40 - ((count - 4) * 3.125)) + '%'
img.style.left = (34 - ((count - 4) * 1.75)) + '%'
img.style.paddingTop = (43 + ((count - 4) * 3.25)) + '%'
text.style.transform = 'skewY('+ Math.abs(90 - (360 / count)) +'deg) rotate('+ 360 / count / 2 + 'deg)'
text.style.width = (50 - ((count - 4) * 3.125)) + '%'
text.style.left = (25 - ((count - 4) * 1.75)) + '%'
text.style.paddingTop = (75 + ((count - 4) * 3.25)) + '%'
$('<li>')
.css({'transform': 'rotate(' + i * (360 / count) + 'deg) skewY(-' + Math.abs(90 - (360 / count)) + 'deg)', 'background' : colors[i]})
.appendTo('ul.mainCircle')
.addClass('item' + i)
.append(img)
.append(text)
}}
I already tried to check the overlap between pin element and the target gift element (<li>) but apparently the <li> elements overlap each other so the result is not always accurate. I also tried to find a way to calculate the angle difference of the wheel after rotation, but I couldn't find one that does not use the css transform rotate value.
I would really appreciate it if you can help me find a way to make sure this problem doesn't happen again.
thanks
Related
So, I've built a quick function to change hue values of a target element in JavaScript, and it works mostly fine now, but I do have some more questions that go beyond the initial post's scope. So I'll open a new question and post them here.
Here's the code:
document.getElementById('left').style.filter = "hue-rotate(" + 20 + "deg)";
document.getElementById('right').style.filter = "hue-rotate(" + 20 + "deg)";
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('click', (e) => {
const filter = e.target.style.filter;
var deg = parseInt(filter.replace(/[^0-9.]/g, ""));
deg += 40;
e.target.style.filter = "hue-rotate(" + deg + "deg)";
if (deg >= 360) {deg -= 360}
console.log(e.target.id + " is " + deg + "deg");
});
});
My main question (1) is that I've coded an if statement to log the current hue value within 360º (the hue-rotate works anyway with values over +360º, but I find it to be clearer this way). However, while the check works perfectly the first time around, it stops working after the function loops once through the 360º (on subsequent loops, it goes beyond 360º).
For clarification, the statement has been positioned after the degree value is already set (and animated) so as to sidestep the quick loop animation that happens when it goes from, say, 340 to 20º (instead of going there directly, it seems to loop back through the whole hue wheel).
Also, (2) the initial hue-rotate states are defined (at the top) within the script because the function does not work otherwise, although both DIVs do have defined CSS values.
That's it! Thanks in advance!
Since the degree value on the element is always set before limiting the degrees to 360, the 2nd time it loops subtracting 360 wont be enough.
style logged value
0 0
360 (-360) 0
720 (-360) 360
etc
To limit the logged value between [0, 360], use the % operator instead
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('click', (e) => {
const filter = e.target.style.filter;
var deg = parseInt(filter.replace(/[^0-9.]/g, ""));
deg += 40;
e.target.style.filter = "hue-rotate(" + deg + "deg)";
deg %= 360; // deg = deg % 360
console.log(e.target.id + " is " + deg + "deg");
});
});
For (2):
To get the style of the element from css, use getComputedStyle
occurence.addEventListener('click', (e) => {
const filter = getComputedStyle(e.target).filter;
...
});
e.target.style.filter = "hue-rotate(" + deg + "deg)";
above line should be below if condition if (deg >= 360) {deg -= 360}
document.getElementById('left').style.filter = "hue-rotate(" + 20 + "deg)";
document.getElementById('right').style.filter = "hue-rotate(" + 20 + "deg)";
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('click', (e) => {
const filter = e.target.style.filter;
console.log(filter);
let deg = parseInt(filter.replace(/[^0-9.]/g, ""));
deg += 40;
if (deg >= 360) {
deg -= 360
}
e.target.style.filter = "hue-rotate(" + deg + "deg)";
console.log(e.target.id + " is " + deg + "deg");
});
});
<div id="left">left</div>
<div id="right">right</div>
currently I'm using fabricjs to draw on a canvas.
I have one large half-circle and a small circle. I want that the user is able to move the small circle on the outer line of the large half circle. But how can I prevent that the circle leave it's path? The Math is not a problem ;)
EDIT: Here I have some code, that shows you, what I want.
deltaLeft = p.left - centerPointX;
deltaTop = p.top - centerPointY;
length = Math.sqrt(deltaLeft * deltaLeft + deltaTop * deltaTop);
console.log(length);
if((length <= centerRadius + 5) && (length >= centerRadius - 5) ){
handleNewX = p.left;
handleNewY = p.top;
}else{
p.left = handleNewX;
p.top = handleNewY;
}
https://jsfiddle.net/g1h2gL88/
The Problem is, it does not feel naturally to move the handle
The problem is the math, if you want to restrict the moment of the of the object on the circle it would calculate the angle of the vector from center to the mouse position. And then your calculate the new position using the angle and the centerRadius. You basically put a straight infinite line starting at the center trough mouse position, and then calculate the intersection of the line with the circle.
deltaLeft = p.left - centerPointX;
deltaTop = p.top - centerPointY;
var radians = Math.atan2(deltaTop, deltaLeft)
p.left = Math.cos(radians) * centerRadius + centerPointX
p.top = Math.sin(radians) * centerRadius + centerPointY
jsfiddle demo
If you really want to have a range centerRadius - 5 to centerRadius + 5 then this can be easily extended to:
var length = Math.sqrt(deltaLeft * deltaLeft + deltaTop * deltaTop);
// change the position only if mouse is outside if the centerRadius +/- 5 range
if (length <= centerRadius - 5 || length >= centerRadius + 5) {
var radians = Math.atan2(deltaTop, deltaLeft)
if (length < centerRadius) {
length = centerRadius - 5;
} else {
length = centerRadius + 5;
}
p.left = Math.cos(radians) * length + centerPointX
p.top = Math.sin(radians) * length + centerPointY
}
fiddle demo
I am currently writing a tracking app that is able to get your currently facing angle and the angle to a destination and point an arrow in the direction you need to travel. (As the crow flies)
I am having trouble taking these 2 angles and making them work together so that the arrow points in the correct direction.
My knowledge of Mathematics is extremely limited so any help would be appreciated.
function deviceOrientationListener(event) {
facing = Math.round(event.alpha);
deg = bearing(lat, lng, getUrlVars()["lat"], getUrlVars()["long"]);
finaldeg = facing - deg;
c.innerHTML = '<img style="-ms-transform: rotate(' + finaldeg + 'deg);-webkit-transform: rotate(' + finaldeg + 'deg);transform: rotate(' + finaldeg + 'deg);" src="images/arrow.png" />';
}
The finaldeg is what i am having trouble with working out.
I did like this with Ionic and the plugin cordova-plugin-device-orientation
to_turn is the variable in which I record the computed angle
degreeStyle is the style applied to the arrow shown on end-user's
screen
last_lat/last_lng is the current position of the user
target_lat/target_lng is the destination
`
this.destinationBearing = this.bearing(
this.last_lat,
this.last_lng,
this.target_lat,
this.target_lng);
this.compass = this.deviceOrientation.watchHeading().subscribe(
(data: DeviceOrientationCompassHeading) => {
this.currentHeading = data.trueHeading;
if (this.destinationBearing > this.currentHeading) {
this.to_turn = this.destinationBearing - this.currentHeading;
} else {
this.to_turn = 360 - (this.currentHeading - this.destinationBearing);
}
this.degreeStyle = 'rotate(' + this.to_turn + 'deg)';
}
);
bearing(startLat, startLng, destLat, destLng){
startLat = this.toRadians(startLat);
startLng = this.toRadians(startLng);
destLat = this.toRadians(destLat);
destLng = this.toRadians(destLng);
let y = Math.sin(destLng - startLng) * Math.cos(destLat);
let x = Math.cos(startLat) * Math.sin(destLat) -
Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
let brng = Math.atan2(y, x);
brng = this.toDegrees(brng);
return (brng + 360) % 360;
}
toRadians(degrees) {
return degrees * Math.PI / 180;
};
toDegrees(radians) {
return radians * 180 / Math.PI;
}
I have a 360 degrees rotating wheel on my player page which acts as a volume button eventually.
I need to limit its functionality to 270 degrees and not 360 (from 7 O'clock to 5 O'clock) so it can have the effect of min to max.
How can i achieve this using the code attached?
The Player Page
$('#metal .indicator-cw').bind('touchmove', function(event){
updateMetal();
});
$("#metal .indicator-cw").mousemove(updateMetal);
function updateMetal(){
var number = $("#metal .result-cw").text();
var Degrees = parseInt(number);
var vol = ((Degrees + 150) % 360) * (1/270);
$musicPlayer.jPlayer("volume", vol);
$("#rotateit").css({'transform':'rotate(' + (Degrees) + 'deg)', '-webkit-transform':'rotate(' + (Degrees) + 'deg)', '-o-transform':'rotate(' + (Degrees) + 'deg)', '-moz-transform':'rotate(' + (Degrees) + 'deg)', '-ms-transform':'rotate(' + (Degrees) + 'deg)' });
if (Degrees > 120 || Degrees < 210) return;
}
//js code for the metal style wheel - above
Add this to stop updating the volume wheel
if (Degrees > 150 && Degrees < 210) return;
You'll also need to calculate the volume a bit differently
//This shifts the degrees from 210-120 to be 0-270 and then scales it to 0-1
var vol = ((Degrees + 150) % 360) * (1/300);
This is all about mathematics. It's a shame that I'v forgotten those I learned in scool.
OK, I'm trying to get the image dimension after rotation (using canvas) with a certain angle in Javascript.
Since I don't have any tools other than MSPaint here, I'll re-use your image:
Say your original rectangle's size is R(ectangle)W(idth) * RH(eight),
in this case RW=200, RH=80;
After rotating a certain angle A, counterclockwise,
where 0deg <= A <= 90deg in degrees (or 0 <= A <= Math.PI/2 in radians),
in this case A=30deg or A=Math.PI/6,
In the new "outer" rectangle, each side is divided by two parts (for the convenience of describing; corresponding to the image).
On the left side, let's say the upper (purple) part is called N(ew)H(eight)U(p), and the lower (red) part is called NHL(ow);
Same rule on the bottom side, we have NW(idth)L(eft) (blue) and NWR(ight) (orange).
So the size (area) of new rectangle would be (NHU + NHL) * (NWL + NWR)
According to the definition of sin and cos:
NWL = RW * Math.cos(A); //where A is in radians
NHL = RW * Math.sin(A);
NHU = RH * Math.cos(A);
NWR = RH * Math.sin(A);
(if you're using A in degrees, replace A to Math.PI*A/180).
So the new "outer" width would be NWL + NWR, and new "outer" height would be NHU + NHL, and now you can calculate everything.
Here's a drop-in function that implements #Passerby's solution + a couple other safeguards:
function imageSizeAfterRotation(size, degrees) {
degrees = degrees % 180;
if (degrees < 0) {
degrees = 180 + degrees;
}
if (degrees >= 90) {
size = [ size[1], size[0] ];
degrees = degrees - 90;
}
if (degrees === 0) {
return size;
}
const radians = degrees * Math.PI / 180;
const width = (size[0] * Math.cos(radians)) + (size[1] * Math.sin(radians));
const height = (size[0] * Math.sin(radians)) + (size[1] * Math.cos(radians));
return [ width, height ];
}
// USAGE:
imageSizeAfterRotation([ 200, 80 ], 30) // [ 213.20508075688775, 169.28203230275508 ]