Number slider with different min max - javascript

I am trying to make an app where user slides the slider to select a compass direction, but we all know that sliders usually starts at 0 (left) and ends at N (right).
I am trying to replicate this:
Bottomline, I want the slider to start at 270(West) and the center should be 360 and 0(0 degree North) and next should be 0-90(North to East) then 90-180(East to South) and then 180-269(South to West)
Sorry for bad english, not native speaker. If my explanation is not understandable, please refer to the image.

If you start the slider on 270 degrees, then the center would be 90 degrees, because your compass is not centered on the slider range.
You can use negative values to determine the applicable direction with some math:
$('#slider-degrees').change(() => {
let selection = parseInt($('#slider-degrees').val());
let degrees = (selection < 0) ? (360 + selection) : selection;
$('#label-degrees').html(degrees);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="slider-degrees" type="range" min="-90" max="269" value="-90">
<span id="label-degrees"></span>

Related

How to move object along SVG path on scroll and speed up specific sections

I have a project in which I have a long SVG path. The SVG path is centered on the page and has a stroke "none" so that it is invisible. Then I have an image of a rocket. The idea is that this rocket should follow the path on scroll. Meaning for each pixel scrolled, it moves one pixel into the direction of the line.
This so far works great. I got the idea for my code from this stackoverflow question.
My problem now is the specific path I created for this. The path has sections in which the path turns sideways or even up (for a looping). The rocket should still follow in these sections, which it does as you can see on the live example here. But when moving through the looping the rocket leaves the view, because it is still moving at the same speed like the scrolling, but just away from the users scroll direction.
Now I would like to speed up the rocket when it is moving upwards through the looping so it doesn't leave out of view. Let's look at my code.
The Project is made in sveltekit, but that is not important right now because for my problem only the JS should be relevant. So for that matter here is the HTML:
<div style="position:absolute; top:0; right:0; left:0; z-index:1000; pointer-events: none;" id="route">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 966 4692" id="svgRoute">
<path id="path" d="M652 0V239.5V317.5C538.333 325.5 312.9 402.4 320.5 646C328.1 889.6 474.667 970.5 547 980.5C573.667 983 632 988.7 652 991.5C677 995 806 993 798 1118.5C791.6 1218.9 628 1275.67 547 1291.5C372.5 1294.67 30.1999 1323.2 56.9999 1412C90.4999 1523 171.5 1565.5 293.5 1607C415.5 1648.5 707 1657 786.5 1931C850.1 2150.2 436.667 2111 222 2064C64.5001 2047.5 -163.9 2094.8 182.5 2416C528.9 2737.2 800.5 2670.5 893 2597C984.5 2452 1061.5 2189 637.5 2297C213.5 2405 390.167 2773.67 531.5 2944.5C665.833 3062 924 3334.1 882 3482.5C829.5 3668 442 3885.5 420 3990C398 4094.5 764.5 4537 774 4691" stroke-width="5" stroke="red" fill="none"/>
<image id="c" x="-20" y="-70" width="40" height="70" xlink:href="data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAAF4AAACqCAYAAAAk0fhhAAAFP0lEQVR4nO2dzWtcZRSH36tjGKdNOkOSFpqk+RAtqVGEtGahWLQQF0JMKBhQqKjVnVu7dK3gqiC4qGDAhQtpCC4kEOgsKooWQWuDYD9i81EH25RGrUlTx3/gnIvvzXWeKfk9y9+de8+dh7M4vPcrqdfrodlIkiTPk0qskP7f96HVtzESDyHxEBIPIfEQEg+RkGOVNzaOjR/LrcbM9JRb3tvQCCfqeAiJh5B4CImHkHiIQiOKxE4vn53+JLfakxN2PjM95Y4uSZL87wtr6ngIiYeQeAiJh5B4iFzXavKaXqpnvndrPLKv1cx79naa+WZxl5lPTrzq1ohd38niUB0PIfEQEg8h8RASDyHxENHjZNpdXnmNjW++9pZb/4Xxl8z8nbft1TBvzCwUS26NFyeOm3meY6Y6HkLiISQeQuIhJB4i10t/vV3tZp5levFY/u2Gmb9/8rSZe9NO/0CvW8P7H3mijoeQeAiJh5B4CImHaMgNTR4rK9fMfOixx6OP5U07zYo6HkLiISQeQuIhJB4CnWo8zv/4g7tt38PxE08zoo6HkHgIiYeQeAiJh5B4iIaMk+X2DjP/4suZ6GN598ffvLZs5qWW5uyt5jyrbYDEQ0g8hMRDSDxEQ6aa7p4uM+/YvcfMdxY23WO1ljbMvFqzb7su3vnHzO3KjUMdDyHxEBIPIfEQEg+BXvq7urBg5qWif1qd5fvN/OLyX2Y+tKsl/sQagDoeQuIhJB5C4iEkHiLXqWZ17U6ehzO5e/ummb/7rH1laj0pmvmvi7+7NRrxP9TxEBIPIfEQEg8h8RASD+GOk1m+t7ejtbK1s9kCV2prZj7QZ5/TqQ/tdx+EEMJPv9Riy5uuvNehh6COx5B4CImHkHgIiYeIXiTr6Xva3bZnb/+WTua/0NkR9/akv9ftG6CycGTUfq/m3Kz7PkoXdTyExENIPITEQ0g8RMFbkxl+cszcoVa7t96ElCdjky9H/X5u1v+snToeQuIhJB5C4iEkHiJ6raa1Yj9Ith3o7rK/N5IFdTyExENIPITEQ0g8hMRDoE/9tZXtm42KD6Ts05ayMScOHhox86WV67nVUMdDSDyExENIPITEQ7hTTblcNvNHB/zXiJ+tnonKb7Vwt3W3baxG7/PUc8/nVl8dDyHxEBIPIfEQEg8RvVazdMn/cEr17Ndm3ldfMvPjz9hvf2wZtCeqEEJ4sP8hM799+aKZb8zb7z746Cv/XQa3KvvNfMfUSXefWNTxEBIPIfEQEg8h8RDuI9/BeUw8C68fHDTzU684O4ymPMR2YMDOL1yy89nLZvzGp36Jj7+b9zdGcGT0mB6pbzYkHkLiISQeQuIhJB7CXSRLG4U80p5yi2F1wf6gSgghVA7E7ZPnxcX2nkOmkycG7XE5DXU8hMRDSDyExENIPEShXrcHkZRXJqKsXbAv5TWC61e/NfM5J09DHQ8h8RASDyHxEBIP4a7VeNNOGt4kdONP+zNxIdyNrrG54N+IFIN/TvFkcaWOh5B4CImHkHgIiYdAH6n/5vPdZj5y1P7AbgghrO7vNfPKz/Y+Xo0Q8ns8PgvqeAiJh5B4CImHkHgIiYdIsizwuAfzLxeaRQ53D5s/fq9vp1tj5Kj9OThvbDxx5Q8zry6ec2uMDw6Zf2R6/rz5ey2S3UNIPITEQ0g8hMRDNGSRzJ8SzpnjwIlgTzshhBA+KDkb4qaXw93DKXdsrfubckIdDyHxEBIPIfEQEg+R61qNW8RZw/HWaqqL9rSThfTpxcabhPJ0pY6HkHgIiYeQeAiJh5B4iH8BrZ8Mhx0ulsYAAAAASUVORK5CYII="/>
</svg>
</div>
The div is id "route" and will be called as such multiple times throughout the JS. The <path> here is the line stretching through the whole page. The <image> is the rocket which I placed directly inside the SVG. The only relevant css is inside the style tag for the div.
Now for the JavaScript/jQuery
jQuery(document).ready(function(){
jQuery(document).scroll(function() {
positionCar();
});
// init the line car position
positionCar();
function positionCar() {
var scrollY = jQuery('#route').scrollTop() || window.pageYOffset;
var maxScrollY = jQuery('#route').prop('scrollHeight');
var path = document.getElementById("path");
// Calculate distance along the path the car should be for the current scroll amount
var pathLen = path.getTotalLength();
var dist = pathLen * scrollY / maxScrollY;
var pos = path.getPointAtLength(dist);
// Calculate position a little ahead of the car (or behind if we are at the end), so we can calculate car angle
var angle = calculateAngle(path, pathLen, dist, pos)
if (angle < 0) {
var pathLen = path.getTotalLength();
var dist = pathLen * scrollY / maxScrollY;
var pos = path.getPointAtLength(dist);
var angle = calculateAngle(path, pathLen, dist, pos)
}
// Position the car at "pos" totated by "angle" (if angle is upwards position further for more speed)
var car = document.getElementById("c");
car.setAttribute("transform", "translate(" + (pos.x) + "," + (pos.y) + ") rotate(" + (rad2deg(angle) + 90) + ")");
}
function calculateAngle(path, pathLen, dist, pos) {
if (dist + 1 <= pathLen) {
var posAhead = path.getPointAtLength(dist + 1);
return Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
} else {
var posBehind = path.getPointAtLength(dist - 1);
return Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
}
}
function rad2deg(rad) {
return 180 * rad / Math.PI;
}
});
So jQuery checks if document ready and binds a scroll handler which then calls the positionCar() function everytime a scroll happens to place the rocket accordingly. The function basically calculates the position off the car on the path line by scroll position and path length. Then it uses the calculateAngle function to check for the required angle ahead of the path.
The angle is used so the rocket follows the path an turns in the direction of the path. Then the rocket (here called car) is positioned at that position in that specific angle.
The if condition inside the positionCar() function for now does nothing different. It checks if the angle is below zero indicating the path going upwards (for example the looping). My idea now is to implement more speed inside this condition because it tells when the rocket is moving back up instead of down.
How would I accomplish this. I tried playing with the calculations but it just resulted in jumping around. By changing the dist variable I managed to get the rocket faster during upwards movement. This just resulted in the rocket jumping back as soon as the path moves back down. How would I need t implement faster rocket speed only during the upwards movement?
For playing around with it I created a REPL with the full code here: https://svelte.dev/repl/d2e449585ac14ec8aef1fd310fb14548?version=3.46.4

Collision detection on a static element with dynamically generated skewed and rotated elements on a 2D plane

I am developing a wheel spinner (think Wheel of Fortune) with a static selector.
The circle segments are generated by having a square element which is then skewed by X degrees depending on the amount of segments. Another square element with a completely rounded left and top side. The overlap is then displayed to make the segment.
Once the wheel is spun, I need the clicker to detect which segment is selected.
The question is, how do I determine the co-ordinates of the segment to determine if the clicker is inside a segment.
Edit to add code segment:
Segment Generation
<div className="Segment-outer" style={{transform: "skewX(" + this.props.skewDegrees
+ "deg)"}}>
<div className="Segment-inner" style={{transform: "skewX(-"
+ this.props.skewDegrees + "deg)"}}>
</div>
</div>
Segments are held in the following div which is then rotated as shown.
<div className="Spinwheel-holder" style={{transform: "translate(-50%, -50%)
rotate(" + this.state.rotateDegrees + "deg)"}}>
Output (after Skewing and CSS) is this for five segments. Each segment gets thinner as more are added:
Known Variables:
Top, Left, Right, Bottom, x, y, height, width
Further Edit:
At the moment, I am using this for the collision detection, which works but is not entirely accurate.
collision = !(
((segmentRect.y + segmentRect.height) < (this.state.clickerLocation.y)) ||
(segmentRect.y > (this.state.clickerLocation.y + this.state.clickerLocation.height)) ||
((segmentRect.x + segmentRect.width) < this.state.clickerLocation.x) ||
(segmentRect.x > (this.state.clickerLocation.x + this.state.clickerLocation.width))
)

How to normalize the compass input?

I want to rotate the compass image according to the degrees I receive. The input degrees are between 0 and 359 so when I rotate -1 degree from 0 , the image rotates one complete round and sets on 359:
function setCompass(degrees){
$("#compass").stop().transition({rotate: -1*degrees +'deg'});
}
I tried to solve the problem by going back to -1 instead of 359 so I changed the code to the following:
function setCompass(degrees){
if (degrees>180){degrees=degrees-360}
$("#compass").stop().transition({rotate: -1*degrees +'deg'});
}
It solved the lag on 0 and 360 but the problem was shifted to 180. Now when I rotate the device from 180 to 181, it rotates a complete negative round to go back to -179 ! Please suggest me how to modify the calculations so I get smooth changes on every degree?
You could use some modulo formula to find out whether to turn clockwise or anti-clockwise. Persist the current angle with jQuery's data method (so you could have more than one such compass if you wanted to), and allow the angle that you pass as CSS value to be outside the 0-359 range.
Here is how it could work:
function setCompass(degrees){
var curr = $("#compass").data("rotation") || 0;
var next = curr + ((degrees - curr)%360 + 180*3) % 360 - 180;
$("#compass").data("rotation", next).stop().transition({rotate: -next + 'deg'});
}
// Demo with randomly generated angles between 0 and 359
setInterval(function() {
setCompass(Math.floor(Math.random()*360));
}, 1000);
<img id="compass" src="https://i.stack.imgur.com/zdHVn.png" width="100px" height="100px">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.transit/0.9.12/jquery.transit.min.js"></script>

device orientation values jumping around?

is it normal that the alpha, beta, and gamma value of deviceorientation event aren't continuous? ( except from passing from -90 to 90, or 0 to 360 )
take a look at this picture : https://i.stack.imgur.com/JZfTc.png
when i roll my phone, at some point, the alpha ( yaw ) and and beta ( pitch ) value will "jump" to another value, while the gamma ( roll ) value will gradually go from -90 to 90
here's the project i'm currently working on to further demonstrate this : https://codepen.io/anon/pen/ReRORG open this up on your phone, then roll your phone to the left slowly and at some point the colors will change abruptly
as you can see i'm displaying the alpha,beta and gamma values untouched here :
$("#yaw")[0].textContent = "yaw :" + event.alpha;
$("#pitch")[0].textContent = "pitch :" + event.beta;
$("#roll")[0].textContent = "roll :" + event.gamma;
why is that? is there a way to workaround this?

Restrict Javascript Rotation Angle (With Propeller.js)

I am trying to figure out how to use propeller.js to restrict the rotation angle of an image. As of right now, if I spin my image twice, my output would read 720. I would like to make it so the element’s output will reset to 0 when spun over 360. In addition, I would like it to not output negative angles if I spin counterclockwise. For example going from 10 to 280.
Here is a codepen of where I am currently at: http://codepen.io/anon/pen/YyZaXK
$('img').propeller({inertia: 1, speed: 10,onRotate: function(){ console.log(this.angle); }});
Any suggestions?
Thanks!
Figured it out!
Using a Modulo made this work the way I wanted!
Here’s what I added:
(degrees + 360) % 360
Which resulted in:
$('img').propeller({inertia: 1, speed: 10,onRotate: function(){ console.log((this.angle + 360) % 360); }});
Looked in the Propeller.js source, found another solution. You can set zero angle if 360 is reached:
onRotate: function(){
if(this.angle===360)
{
this.virtualAngle = 0;
}
console.log(this.angle);
}
You could also make use of the normalizeAngle() method of Propeller itself:
onRotate: function () {
console.log(this.normalizeAngle(this.angle));
}
That also avoids negative angles.

Categories