I want to make a Javascript function that will eventually cause a div to animate by updating a calculation as the user scrolls.
My aim is to do this by incrementing a number, initially from 0 up to 20. Once 20 is the value this number needs to be recognised and then the value needs to decrement down from 20 to -20 (For example)
At the moment I have a function that will count up as I scroll down the page and then when I scroll up will count down again but I'm not sure how best to get the values to also update when the numbers reach 20 and -20 as the user scrolls.
let scrollCount = 0;
window.addEventListener("mousewheel", function(e){
if(e.wheelDelta < 0 && scrollCount < 20){
scrollCount++
}
else if(e.wheelDelta > 0 && scrollCount > -20){
scrollCount--
}
let x = scrollCount * window.innerWidth
let y = 30 * window.innerHeight
moveEye(irisLeft, x, y)
moveEye(irisRight, x, y)
console.log(scrollCount)
});
I think using PageOffset would be a better option.
var scrollTop = window.pageYOffset;
This will give you an absolute px value of scroll position. Though it is not in range [-20,20], you can scale it mathematically(min-max scaling).
Scaling Answered Here
Round off scaled values to integers and you're done.
As I said in my comment - this behavior is quire weird as once 20 is reached your div will start trembling/bouncing and will throw your user in a epileptic shock.
let scrollCount = 0;
let direction = 1;
window.addEventListener("mousewheel", function (e) {
if (e.wheelDelta < 0) {
scrollCount += direction
if (scrollCount === 20 || scrollCount === -20) {
direction *= -1
}
}
else if (e.wheelDelta > 0) {
scrollCount -= direction
if (scrollCount === 20 || scrollCount === -20) {
direction *= -1
}
}
let x = scrollCount * window.innerWidth
let y = 30 * window.innerHeight
// moveEye(irisLeft, x, y)
// moveEye(irisRight, x, y)
console.log(scrollCount)
});
Related
I am trying to solve a school assignment in p5 JavaScript. I want something to move over the canvas after one mouseclick. But it only moves a little bit and I have to click several times to get it all the way over. What have I done wrong? Shouldn't the loop make it move all the way? Can post the whole code if needed.
function CanvasPressed()
{
if ( mouseX > 0 && mouseX < 638 && mouseY > 0 && mouseY < 100 )
{
Bird.stop();
Bird.play();
for ( let b = 640; b > 0; b--)
{
x = x - 0.05;
}
}
Alright, so you've got a couple misunderstood things, here:
// purely aesthetic but in javascript functions are usually written as (i think) camelCase
// so: canvasPressed() rather than CanvasPressed(), Class-es start with upper case
function CanvasPressed()
{
// you can check for width & height if you want if ( mouseX > 0 && mouseX < width)
if ( mouseX > 0 && mouseX < 638 && mouseY > 0 && mouseY < height )
{
for ( let b = 640; b > 0; b--) // this, in this case, does the same as for(let i = 0; i < width; i ++)
{
x += 0.05
// 0.05 is very little, only a very small part of a pixel
}
// here it moves 0.05 * 640 (0.05 + 0.05 + 0.05 ... )
}
}
javascript naming conventions thingy if you want
and this is how i would make it move through the canvas:
let mouseWasPressed = false;
let x = 20
function draw() {
background(20);
ellipse(x, height / 2, 40)
if(mouseWasPressed) // don't need {} for 1 line after the if()
x ++; // x = x + 1 shortening in javascript
// }
}
function mousePressed(){
mouseWasPressed = true
}
if you don't want the "animation" you could use your previous method, but change the 0.05 to 1:
for(let i = 0; i <= width; i ++) // you don't have to add parentheses for 1 line
x ++; // x = x + 1 just a shortening in javascript
OR just
x = width // or x += width (x = x + width)
This question already has answers here:
Cross browser JavaScript (not jQuery...) scroll to top animation
(22 answers)
Closed 8 years ago.
I'm trying to make an animated "scroll to top" effect without using jQuery.
In jQuery, I usually use this code:
$('#go-to-top').click(function(){
$('html,body').animate({ scrollTop: 0 }, 400);
return false;
});
How do I animate scrollTop without using jQuery?
HTML:
<button onclick="scrollToTop(1000);"></button>
1# JavaScript (linear):
function scrollToTop (duration) {
// cancel if already on top
if (document.scrollingElement.scrollTop === 0) return;
const totalScrollDistance = document.scrollingElement.scrollTop;
let scrollY = totalScrollDistance, oldTimestamp = null;
function step (newTimestamp) {
if (oldTimestamp !== null) {
// if duration is 0 scrollY will be -Infinity
scrollY -= totalScrollDistance * (newTimestamp - oldTimestamp) / duration;
if (scrollY <= 0) return document.scrollingElement.scrollTop = 0;
document.scrollingElement.scrollTop = scrollY;
}
oldTimestamp = newTimestamp;
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
}
2# JavaScript (ease in and out):
function scrollToTop (duration) {
// cancel if already on top
if (document.scrollingElement.scrollTop === 0) return;
const cosParameter = document.scrollingElement.scrollTop / 2;
let scrollCount = 0, oldTimestamp = null;
function step (newTimestamp) {
if (oldTimestamp !== null) {
// if duration is 0 scrollCount will be Infinity
scrollCount += Math.PI * (newTimestamp - oldTimestamp) / duration;
if (scrollCount >= Math.PI) return document.scrollingElement.scrollTop = 0;
document.scrollingElement.scrollTop = cosParameter + cosParameter * Math.cos(scrollCount);
}
oldTimestamp = newTimestamp;
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
}
/*
Explanation:
- pi is the length/end point of the cosinus intervall (see below)
- newTimestamp indicates the current time when callbacks queued by requestAnimationFrame begin to fire.
(for more information see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
- newTimestamp - oldTimestamp equals the delta time
a * cos (bx + c) + d | c translates along the x axis = 0
= a * cos (bx) + d | d translates along the y axis = 1 -> only positive y values
= a * cos (bx) + 1 | a stretches along the y axis = cosParameter = window.scrollY / 2
= cosParameter + cosParameter * (cos bx) | b stretches along the x axis = scrollCount = Math.PI / (scrollDuration / (newTimestamp - oldTimestamp))
= cosParameter + cosParameter * (cos scrollCount * x)
*/
Note:
Duration in milliseconds (1000ms = 1s)
Second script uses the cos function. Example curve:
3# Simple scrolling library on Github
I am currently writing a tool that allows a user to change a value by pinching their screen. I am using Hammer.js to catch the pinch event and I am using the scale property of the event to determine the factor of which to change the value.
The following code illustrates how I am increasing and decreasing the value based on the pinch:
var value = 10;
var scale = false;
var previous_scale = false;
pinch.on("pinch", function(e){
// Only proceed is it is not the final touch
// The final touch has a scale of (0)
if(e.isFinal===false){
// Only proceed if the previous scale has been defined
if(previous_scale!==false){
// Calculate the difference in scales
scale = e.scale-previous_scale;
previous_scale = e.scale;
if(scale>0){
value = Math.round(value+(1+(scale*100)));
value = value>=4000 ? 4000 : value;
}else{
value = Math.round(value-(1+(Math.abs(scale)*100)));
value = value<=10 ? 10 : value;
}
console.log(value);
}else{
previous_scale = e.scale;
}
}
});
My problem is, I would like the value to do the following:
When value is less than 30, increment by 1 for each scale
When value is less than 60, increment by 5 for each scale
When value is less than 200, increment by 10 for each scale
When value is less than 1000, increment by 50 for each scale
When value is less than 2500, increment by 100 for each scale
When value is less than 5000, increment by 500 for each scale
I have tried to achieve the above by inserting the following:
if(radius>2000){
radius = 500*Math.round(radius/500);
}else if(radius>1000){
radius = 100*Math.round(radius/100);
}else if(radius>300){
radius = 50*Math.round(radius/50);
}else if(radius>50){
radius = 10*Math.round(radius/10);
}
This hasn't worked as it is only rounding the numbers, I actually need to increase the factor as the numbers get larger.
Can anyone advise on the best way to do this?
As the scalevar is already a delta, that seems pretty straightforward:
if( value >= 2500 ) {
value += 500 * scale;
}
else if( value >= 1000 ) {
value += 100 * scale;
}
else if( value >= 200 ) {
value += 50 * scale;
}
else if( value >= 60 ) {
value += 10 * scale;
}
else if( value >= 30 ) {
value += 5 * scale;
}
else {
value += scale;
}
if( value < 0 ) {
value = 0;
}
if( value >= 5000 ) {
value = 5000;
}
(assuming a cap at value = 5000).
It seems that you need the rounded number anyway (correct me if I'm wrong), so as there are a limited number of possible values you can use a linear scale modified by the pinch event to address as an index to the desired values:
var value = 10;
var scale = 10;
var previous_scale = false;
pinch.on("pinch", function(e){
// Only proceed is it is not the final touch
// The final touch has a scale of (0)
if(e.isFinal===false) {
scale = Math.round(scale * e.scale);
if ( scale < 0 ) {
// lower bound
value = 0;
} else if ( scale < 31 ) {
// 0-30 increment of 1 --> 31 values
value = scale;
} else if ( scale < 37 ) {
// 35-60 increment of 5 --> 6 values
value = 30 + (scale - 30) * 5;
} else if ( scale < 51 ) {
// 70-200 increment of 10 --> 14 values
value = 60 + (scale - 36) * 10;
} else if ( scale < 67 ) {
// 250-1000 increment of 50 --> 16 values
value = 200 + (scale - 50) * 50;
} else if ( scale < 82 ) {
// 1100-2500 increment of 100 --> 15 values
value = 1000 + (scale - 66) * 100;
} else if ( scale < 87 ) {
// 3000-5000 increment of 500 --> 5 values
value = 2500 + (scale - 81) * 500;
} else {
// higher limit
value = 5000;
}
console.log(value);
}
});
Please, tell me if this is somewhere near to what you were thinking.
I'm pretty new to reactive programming (and RxJS) and all these operators are heavy to understand.
Anyway, I've successfully written this function that handles scrolling of the document while dragging something. I now wonder if this can be simplified.
Basically, onMouseDown I need to check the position of the mouse every 10ms and I need the updated clientY when the mouse moved, thats why I setup an Rx.Oberservable.interval(10) which I combine with the mouseMove observer. This will scroll the page, whether you move your mouse or not (as intended).
Here's the code:
handleWindowScrollOnDrag() {
var dragTarget = this.getDOMNode()
var scrollTarget = document.body
var wHeight = window.innerHeight
var maxScroll = document.documentElement.scrollHeight - wHeight
// Get the three major events
var mouseup = Rx.Observable.fromEvent(document, 'mouseup');
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(function (md) {
var y = scrollTarget.scrollTop
var multiplier = 1
// Scroll every 10ms until mouseup when we can
var intervalSource = Rx.Observable.interval(10).takeUntil(mouseup);
// Get actual clientY until mouseup
var movement = mousemove.map(function (mm) {
return {
y: mm.clientY
};
}).takeUntil(mouseup);
return Rx.Observable
.combineLatest(movement, intervalSource, function (s1) {
multiplier = 1
if (s1.y < 100 && y >= 0) {
if (s1.y < 75) multiplier = 3;
if (s1.y < 50) multiplier = 5;
if (s1.y < 25) multiplier = 10;
if (s1.y < 15) multiplier = 20;
y -= (1 * multiplier)
}
if (s1.y > wHeight - 100 && y <= (maxScroll)) {
if (s1.y > wHeight - 75) multiplier = 3;
if (s1.y > wHeight - 50) multiplier = 5;
if (s1.y > wHeight - 25) multiplier = 10;
if (s1.y > wHeight - 15) multiplier = 20;
y += (1 * multiplier)
}
return {
y: y
};
});
});
// Update position
this.subscription = mousedrag.subscribe(function (pos) {
document.body.scrollTop = pos.y
});
},
You can remove the extra takeUntils you only need one on the movement stream, since the combineLatest operator will clean up and dispose of all the underlying subscriptions when it completes.
Next, you can remove some extra state by using .scan to manage accumulated state instead of closure variables.
You can also simplify the event body by simply passing the mouse move's y value instead of incurring the overhead of an object allocation in both the map and the combineLatest operators.
Finally, I would change to use .withLatestFrom instead of combineLatest. Since you are already essentially polling at 100 fps with interval combineLatest would emit even faster if the mouse was moving at the same time.
As a side note, while I am not terribly familiar with how DOM scrolling works (and I don't actually know how well your code works in the wild), spamming the page's scroll value faster than the actual render rate of the page seems like overkill. It might be better to lower the interval rate and use something like jQuery.animate to smooth the scrolling in between.
;tldr
handleWindowScrollOnDrag() {
var dragTarget = this.getDOMNode()
var scrollTarget = document.body;
var wHeight = window.innerHeight;
var maxScroll = document.documentElement.scrollHeight - wHeight;
// Get the three major events
var mouseup = Rx.Observable.fromEvent(document, 'mouseup');
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(function (md) {
// Scroll every 10ms until mouseup when we can
var intervalSource = Rx.Observable.interval(10, Rx.Scheduler.requestAnimationFrame);
return intervalSource
.takeUntil(mouseup)
.withLatestFrom(mousemove, function (s1, s2) {
return s2.clientY;
})
.scan(scrollTarget.scrollTop, function(y, delta) {
var multiplier = 1;
if (delta < 100 && y >= 0) {
if (delta < 75) multiplier = 3;
if (delta < 50) multiplier = 5;
if (delta < 25) multiplier = 10;
if (delta < 15) multiplier = 20;
y -= (1 * multiplier);
}
if (delta > wHeight - 100 && y <= (maxScroll)) {
if (delta > wHeight - 75) multiplier = 3;
if (delta > wHeight - 50) multiplier = 5;
if (delta > wHeight - 25) multiplier = 10;
if (delta > wHeight - 15) multiplier = 20;
y += (1 * multiplier);
}
return y;
});
});
// Update position
this.subscription = mousedrag.subscribe(function (pos) {
document.body.scrollTop = pos;
});
},
Edit
A mistake in the original actually allows one to simplify the code even further. You can remove the movement stream all together (and with it an extra call to map) by passing mousemove to withLatestFrom and using that result selector to grab the clientY
Additionally I added where you would likely want to add a scheduler if you were trying to synchronize interval with the render loop of the page, though I would still say you probably want to use 15ms (60fps) rather than 10ms, it again comes down to how the scroll rendering is done.
If setting the scroll position will only be updated at the end of each render loop then it will work fine. However, if it greedily recomputes after every set then it would be better to have an intermediary like React to write to Virtual DOM first rather than flushing all changes directly to the DOM.
The code im using works fine, except for the Z position.
The maximum distance is aquired at an angle of around 53 degrees, instead of the 45 it should be.
I made my own toDegree and toRadian function, because js interprets sin and cos from a Radian points of view.
Any help?
function Bullet(I) {
I.zVelocity = Math.sin(toRadians(turretpitch)) * 5;
}
second part:
bullets.forEach(function (bullet) {
bullet.zVelocity -= 0.05;
bullet.z += bullet.zVelocity;
if (bullet.x > bgImage.width || bullet.x < 0 ||
bullet.y > bgImage.height || bullet.y < 0 ||
bullet.z < 0) {
bullet.explode();
}
}
);