I would like to trigger an event when the window scroll velocity goes above a certain value. I found some code that will help measure the velocity but I can't work out why the if statement is not triggered when the speed goes above 150. Any help would be greatly appreciated.
const checkScrollSpeed = (function(settings){
settings = settings || {};
let lastPos, newPos, timer, delta,
delay = settings.delay || 50;
function clear() {
lastPos = null;
delta = 0;
}
clear();
return function(){
newPos = window.scrollY;
if ( lastPos != null ){ // && newPos < maxScroll
delta = newPos - lastPos;
}
lastPos = newPos;
clearTimeout(timer);
timer = setTimeout(clear, delay);
return delta;
};
})();
const container = document.querySelector('#container');
window.addEventListener('scroll', function() {
console.log(checkScrollSpeed());
if (checkScrollSpeed() > 150) {
console.log('150+')
container.classList.add('red');
}
});
#container {
width: 75%;
height: 170vh;
background-color: yellow;
}
#container.red {
background-color: red !important;
}
<div id="container"></div>
You are calling checkScrollSpeed in succession and when it's called the second time delta is 0. Either remove the console.log or assign delta to some variable and use that for logging and the condition.
I think it's because you have a little delay between the first time you call checkScrollSpeed() in your console.log and then in your if statement. You can try to call it only once and save the value for your console.log and if statement. It works for me with this solution:
const checkScrollSpeed = (function(settings) {
settings = settings || {};
let lastPos, newPos, timer, delta,
delay = settings.delay || 50;
function clear() {
lastPos = null;
delta = 0;
}
clear();
return function() {
newPos = window.scrollY;
if (lastPos != null) { // && newPos < maxScroll
delta = newPos - lastPos;
}
lastPos = newPos;
clearTimeout(timer);
timer = setTimeout(clear, delay);
return delta;
};
})();
const container = document.querySelector('#container');
window.addEventListener('scroll', function() {
var speed = checkScrollSpeed();
console.log(speed);
if (speed > 150) {
console.log('150+');
container.classList.add('red');
}
});
#container {
width: 75%;
height: 170vh;
background-color: yellow;
}
#container.red {
background-color: red !important;
}
<div id="container"></div>
Related
Theres some wierd behaviour when playing with Paperjs, i was trying to curve a line up with 7 points separately - which works fine once, but when trying to make the link overshoot and return to 3 different points (to create a bounce effect) doesn't seem to play ball. On the second if statement, the 'counter' variable doesnt seem to increase instead of decrease, '+ steps' instead of '- steps'.
Maybe i'm not using if statements properly in this case, or paperjs has some strange behaviour?
Heres the codepen for it in full, click above the blue line to trigger it off. . Following is one setInterval for one of the points of the segment.
var seg6first = true;
var seg6sec = false;
var seg6thir = false;
setInterval(function() {
if (seg6first == true) {
counter = counter - steps;
if (counter >= 230) {
path.segments[6].point.y = counter;
path.smooth(); }
else {
seg6first = false;
seg6sec = true;
}
}
if (seg6sec == true) {
counter = counter + steps;
if (counter <= 260) {
path.segments[6].point.y = counter;
path.smooth();}
else {
seg6sec = false;
seg6thir = true;
}
}
if (seg6sec == true) {
counter = counter - steps;
if (counter >= 250) {
path.segments[6].point.y = counter;
path.smooth(); }
else {
seg6thir = false;
}
}
}, mintiming);
Thanks!
Rather than manually building your bounce effect, you can use an animation library like GSAP.
It has a lot of features that will make your task easier (see easing documentation).
Here is an example of what you are trying to do (click on the canvas to animate the line).
html,
body {
margin: 0;
overflow: hidden;
height: 100%;
}
canvas[resize] {
width: 100%;
height: 100%;
}
<canvas id="canvas" resize></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.8/paper-full.min.js"></script>
<script type="text/paperscript" canvas="canvas">
// user defined constants
var SEGMENTS_COUNT = 6;
var CURVE_HEIGHT = 80;
var ANIMATION_DURATION = 2;
// init path
var path = new Path({
fillColor: 'orange',
selected: true
});
// add points
for (var i = 0; i <= SEGMENTS_COUNT; i++) {
path.add([view.bounds.width * i / SEGMENTS_COUNT, view.center.y]);
}
// on mouse down...
function onMouseDown() {
// ...animate points
for (var i = 0, l = path.segments.length; i < l; i++) {
// get a reference to the point
var point = path.segments[i].point;
// calculate offset using sine function to form a curve
var offset = CURVE_HEIGHT * Math.sin(point.x * Math.PI / view.bounds.width);
// register animation
TweenLite.fromTo(
// target
point,
// duration
ANIMATION_DURATION,
// initial value
{ y: view.center.y },
{
// final value
y: view.center.y - offset,
// easing
ease: Elastic.easeOut.config(1, 0.3),
// on update...
onUpdate: function() {
// ...smooth the path
path.smooth();
}
}
);
}
}
</script>
I want to stop the animation on an HTML page once it is out of view
I tried to use
$(window).focus(function () {
// start animation
}).blur(function () {
// stop animation
});
but this code also stops the animation if it is the background of another window.
Is there an handler for window in/out of view in JS or jQuery?
i.e. something like:
$(window).windowInView(function () {
// start animation
}).windowOutView(function () {
// stop animation
});
There's two ways:
window.requestAnimationFrame()
example taken/modified from linked page
<div id="rec" style="width: 5px; height: 5px; background: black;"></div>
var start = null;
var left=false;
var element = document.getElementById('rec');
element.style.position = 'absolute';
function step(timestamp) {
if (!start) start = timestamp;
if(!left){
var progress = timestamp - start;
element.style.left = Math.min(progress / 10, 600) + 'px';
if (progress < 6000) {
window.requestAnimationFrame(step);
}else{
start = timestamp;
left = true;
window.requestAnimationFrame(step);
}
}else{
var progress = (start+6000)-timestamp;
element.style.left = Math.max(progress / 10, 0) + 'px';
if (progress > 0) {
window.requestAnimationFrame(step);
}else{
start = timestamp;
left=false;
window.requestAnimationFrame(step);
}
}
}
or Page Visibility API
document.addEventListener("visibilitychange", function(){
if (document.hidden) {
console.log('hidden');
}else{
console.log('visible');
}}, false);
Can you recommend a JS library that actually provides edge swipe functionality when working with bare-bones HTML & CSS?
I've searched all over and haven't found a source of truth for that problem.
I've seen lots and lots of libraries enabling swipe gestures but not edge swipe.
My last attempt was using Hammer.js which I've tried implementing as:
var swipe = new Hammer(document);
// detect swipe and call to a function
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
var endPoint = e.pointers[0].pageX;
var distance = e.distance;
var origin = endPoint - distance;
//swipe right to open nav
if (origin <= 15 && e.type == 'swiperight') {
// open main menu
$('#navigation-menu').animate({
left: '0'
});
} else {
// close/hide menu(s)
$('#navigation-menu').animate({
left: '-100%'
});
}
});
Further, if not using any library, how can I implement a mobile edge swipe to show and hide content, (in my case it'd be a navigation menu) with vanilla JS?
At this point I'm open to either solution/direction.
Here is a solution, you can set thresholdStart, End, Milliseconds. You may want to tidy up the code, and port it for touch events (I used mouse events for testing in my browser more easily).
Use:
swipeEdgeFromLeft function and swipeEdgeFromRight function.
var div = document.body;
var mouse = {
isDown: false,
inLeft: false,
inRight: false,
downTimestamp: null
};
var width, thresholdStart, thresholdEnd, thresholdMilliseconds;
function resize(){
width = window.innerWidth;
thresholdStart = 0.1*width;//within 10% of screen width
thresholdEnd = 0.13*width;//beyond 13% of screen width
thresholdMilliseconds = 500;//must be done in 500 milliseconds
}
document.addEventListener("resize", resize, false);
resize();//initialize
div.addEventListener('mousedown'/*'touchstart'*/, function(e){
var x = e./*touches[0].*/pageX;
mouse.isDown = true;
mouse.downTimestamp = performance.now();
if(x < thresholdStart){
mouse.inLeft = true;
} else if(x > width-thresholdStart){
mouse.inRight = true;
}
});
div.addEventListener('mousemove'/*'touchmove'*/, function(e){
var x = e./*touches[0].*/pageX;
if(mouse.inLeft && x > thresholdEnd){
mouse.inLeft = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromLeft();
}
} else if(mouse.inRight && x < width-thresholdEnd){
mouse.inRight = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromRight();
}
}
});
div.addEventListener('mouseup'/*'touchend'*/, function(e){
//var x = e./*changedTouches[0].*/pageX;
mouse.isDown = false;
mouse.inLeft = false;
mouse.inRight = false;
mouse.downTimestamp = null;
});
function swipeEdgeFromLeft(){
console.log("edge swipe from left");
}
function swipeEdgeFromRight(){
console.log("edge swipe from right");
}
body {
max-width: 100vw;
height: 100vh;
}
.bar {
height: 100vh;
background-color: rgba(0,0,0,0.4);
position: fixed;
pointer-events: none;
}
#left-inner-threshold {
width: calc(0.1 * 100vw);
left: 0;
}
#right-inner-threshold {
width: calc(0.1 * 100vw);
right: 0;
}
#left-outer-threshold {
width: calc(0.13 * 100vw);
left: 0;
}
#right-outer-threshold {
width: calc(0.13 * 100vw);
right: 0;
}
<div id="left-inner-threshold" class="bar"></div>
<div id="left-outer-threshold" class="bar"></div>
<div id="right-inner-threshold" class="bar"></div>
<div id="right-outer-threshold" class="bar"></div>
Here's a solution to your existing code using Hammer.js v2.0.8
The explanation for how to achieve the edge swipe can be found here answered by #jovinbm.
$(document).ready(function () {
const swipe = new Hammer(document);
function getStartPosition(e) {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
}
};
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
const { x } = getStartPosition(e);
console.log(x);
//swipe right to open nav /* note the condition here */
if (e.type == 'swiperight' && x >= 0 && x <= 50) {
// open menu
$('#navigation').animate({
left: '0'
});
//swiping left should slide out nav and/or sub-nav
} else {
// close/hide menu
$('#navigation, #task-menu').animate({
left: '-100%'
});
}
});
});
Here's a pen showing it in action:
For swipes, only the final pointerup event is included as the srcEvent in the event object passed to your handler (see http://hammerjs.github.io/api/). The initial pointerdown event that carries the details of the initial position of where the swipe event started is not provided in the hammer event object. Fortunately, you can use the srcEvent in the event object to get the starting position of the event initial pointerdown event.
const getStartPosition = (e) => {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
};
};
const handleSwipe = (e) => {
const {x} = getStartPosition(e);
if (x >= 0 && x <= 50) {
// handle swipe from left edge e.t.c
}
else {
// handle other case
}
};
The srcEvent is just a normal javascript event that inherits properties from UIEvent hence the pageX/pageY api above. This will probably not work in other browsers since some of them are not standardized
I was working on collision detection and thought I would start out simple by testing when the object reaches a certain x-position. It works when I set it to 100, the initial top value for 'character, which leads me to believe the problem is with top updating; however, I don't see why the circles would be moving if that were the case.If you could tell me how to keep 'top' updated or better yet, help me with collision detection that would be great!
(ps. I know it's not good to put css, javascript, and html in one page. I have this as part of a website but moved it to one file so I could test it separately without looking through the code of the entire website and I will add it in the appropriate files once I get this figured out.)
<html>
<head>
<style>
#character {
position: absolute;
width: 42px;
height: 42px;
background: black;
border-radius: 50%;
}
#character2 {
position: absolute;
width: 42px;
height: 42px;
background: pink;
border-radius: 50%;
} </style>
</head>
<body>
<div id = 'character'></div>
<div id = 'character2'></div>
<script>
var keys = {};
keys.UP = 38;
keys.LEFT = 37;
keys.RIGHT = 39;
keys.DOWN = 40;
keys.W = 87;
keys.A = 65;
keys.D = 68;
keys.S = 83;
/// store reference to character's position and element
var character = {
x: 1000,
y: 100,
speedMultiplier: 1,
element: document.getElementById("character")
};
var character2 = {
x: 100,
y: 100,
speedMultiplier: 3,
element: document.getElementById("character2")
};
/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup =
document.body.onkeydown = function(e){
/// prevent default browser handling of keypresses
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
var kc = e.keyCode || e.which;
keys[kc] = e.type == 'keydown';
};
/// character movement update
var moveCharacter = function(dx, dy){
character.x += (dx||0) * character.speedMultiplier;
character.y += (dy||0) * character.speedMultiplier;
character.element.style.left = character.x + 'px';
character.element.style.top = character.y + 'px';
};
var moveCharacter2 = function(dx, dy){
character2.x += (dx||0) * character2.speedMultiplier;
character2.y += (dy||0) * character2.speedMultiplier;
character2.element.style.left = character2.x + 'px';
character2.element.style.top = character2.y + 'px';
};
/// character control
var detectCharacterMovement = function(){
if ( keys[keys.LEFT] ) {
moveCharacter(-1, 0);
}
if ( keys[keys.RIGHT] ) {
moveCharacter(1, 0);
}
if ( keys[keys.UP] ) {
moveCharacter(0, -1);
}
if ( keys[keys.DOWN] ) {
moveCharacter(0, 1);
}
if ( keys[keys.A] ) {
moveCharacter2(-1, 0);
}
if ( keys[keys.D] ) {
moveCharacter2(1, 0);
}
if ( keys[keys.W] ) {
moveCharacter2(0, -1);
}
if ( keys[keys.S] ) {
moveCharacter2(0, 1);
}
};
/// update current position on screen
moveCharacter();
moveCharacter2();
/// game loop
setInterval(function(){
detectCharacterMovement();
}, 1000/24);
function getPosition() {
var elem = document.getElementById("character");
var top = getComputedStyle(elem).getPropertyValue("top");
if (top == '200px') {
alert ("hi");
}
getPosition()
}
getPosition()
// var pos1 = document.getElementById('character').style.top
</script>
</body>
</html>
I figured it out! I just added Jquery's .position() to get the coordinates! For the collision detection I added this:
function collision1() {
$(document).ready(function(){
var x = $("#character").position();
var y = $("#character2").position();
var zid = '#' + id
var z = $(zid).position();
var topX = parseInt(x.top);
var topZ = parseInt(z.top);
var leftX = parseInt(x.left);
var leftZ = parseInt(z.left);
var topY = parseInt(y.top);
var leftY = parseInt(y.left);
if (topX > topZ && topX < (topZ + 50) && leftX > leftZ && leftX < (leftZ + 100)) {
alert('Player 1 Won! Click restart to play again.')
document.getElementById("button2").style.display = "block";
document.getElementById("def").style.display = "none";
document.getElementById("def2").style.display = "none";
document.getElementById("def3").style.display = "none";
document.getElementById("def4").style.display = "none";
document.getElementById("term").style.display = "none";
}
else {
collision1()}
});}
I know it is probably not the most effective way to program this but it works!
Sample:
http://codepen.io/anon/pen/xgZMVd/
html file:
<div class="game-page">
</div>
css file:
.game-page {
width: 1024px;
height: 768px;
position: absolute;
background-color: grey;
}
.stick {
position: absolute;
left: 50%;
margin-left: -94px;
top: -60px;
width: 188px;
height: 50px;
background-color: black;
}
js file:
$(document).ready(function() {
init();
});
var CSS_CLASS = { STICK: "stick" },
changeSpeedTime = 1000, //in milliseconds
gameTime = 60, //in seconds
timer = 1, //in seconds
windowWidth = 1024,
windowHeight = 768,
stickTop = -60,
restStickTime = 0, //in milliseconds
initialStickInterval = 1000, //in milliseconds
stickInterval = null, //in milliseconds
initialStickDuration = 7, //in seconds
stickDuration = null, //in seconds
stickTweensArray = [],
changeSpeedInterval = null,
countdownTimerInterval = null,
generateSticksInterval = null,
generateSticksTimeout = null,
$gamePage = null;
function init() {
initVariables();
initGamePage();
}
function changingSpeedFunction(x){
var y = Math.pow(2, (x / 20));
return y;
}
function initVariables() {
$gamePage = $(".game-page");
stickDuration = initialStickDuration;
stickInterval = initialStickInterval;
}
function initGamePage() {
TweenMax.ticker.useRAF(false);
TweenMax.lagSmoothing(0);
initGamePageAnimation();
}
function initGamePageAnimation () {
generateSticks();
changeSpeedInterval = setInterval(function () {
changeSpeed();
}, changeSpeedTime);
countdownTimerInterval = setInterval(function () {
updateCountdown();
}, 1000);
}
function changeSpeed () {
var x = timer;
var y = changingSpeedFunction(x); //change speed function
stickDuration = initialStickDuration / y;
stickInterval = initialStickInterval / y;
changeCurrentSticksSpeed();
generateSticks();
}
function changeCurrentSticksSpeed () {
stickTweensArray.forEach(function(item, i, arr) {
var tween = item.tween;
var $stick = item.$stick;
var oldTime = tween._time;
var oldDuration = tween._duration;
var newDuration = stickDuration;
var oldPosition = stickTop;
var newPosition = $stick.position().top;
var oldStartTime = tween._startTime;
var distance = newPosition - oldPosition;
var oldSpeed = distance / oldTime;
var newSpeed = oldSpeed * oldDuration / newDuration;
var newTime = distance / newSpeed;
var currentTime = oldStartTime + oldTime;
var newStartTime = currentTime - newTime;
item.tween._duration = newDuration;
item.tween._startTime = newStartTime;
});
}
function generateSticks () {
if (restStickTime >= changeSpeedTime) {
restStickTime -= changeSpeedTime;
restStickTime = Math.abs(restStickTime);
} else {
generateSticksTimeout = setTimeout(function () {
generateSticksInterval = setInterval(function () {
generateStick();
restStickTime -= stickInterval;
if (restStickTime <= 0) {
clearInterval(generateSticksInterval);
restStickTime = Math.abs(restStickTime);
}
}, stickInterval);
generateStick();
restStickTime = changeSpeedTime - Math.abs(restStickTime) - stickInterval;
if (restStickTime <= 0) {
clearInterval(generateSticksInterval);
restStickTime = Math.abs(restStickTime);
}
}, restStickTime);
}
}
function generateStick () {
var $stick = $("<div class='" + CSS_CLASS.STICK + "'></div>").appendTo($gamePage);
animateStick($stick);
}
function animateStick ($stick) {
var translateYValue = windowHeight + -stickTop;
var tween = new TweenMax($stick, stickDuration, {
y: translateYValue, ease: Power0.easeNone, onComplete: function () {
$stick.remove();
stickTweensArray.shift();
}
});
stickTweensArray.push({tween:tween, $stick:$stick});
}
function updateCountdown () {
timer++;
if (timer >= gameTime) {
onGameEnd();
clearInterval(changeSpeedInterval);
clearInterval(countdownTimerInterval);
clearInterval(generateSticksInterval);
clearTimeout(generateSticksTimeout);
}
}
function onGameEnd () {
var $sticks = $gamePage.find(".stick");
TweenMax.killTweensOf($sticks);
}
So, as I researched, I have next situation:
TweenMax (as it uses requestAnimationFrame) freezes when tab is inactive.
setInterval keep going when tab is inactive (also it's delay may change when tab is inactive, depends on browser)
Is there any other javascript functionality that changes when tab is inactive?
Then I have 2 solutions:
Freeze whole game, when tab is inactive.
Keep going, when tab is inactive.
With first solution I have next problem: as TweenMax uses requestAnimationFrame, it works correct according to this solution (freezes animation), but how can I freeze intervals and timeouts when tab is inactive and then resume intervals and timeouts?
With second solution I can use TweenMax.lagSmoothing(0) and TweenMax.ticker.useRAF(false) for animation and it works, but anyway something goes wrong with intervals and/or timeouts. I expected that animation goes wrong because of change of interval delay to 1000+ ms when tab is inactive (according to http://stackoverflow...w-is-not-active), but I disabled acceleration and set delays to 2000ms and it didn't help.
Please help me with at least one solution. Better with both to have some variety.
Resolved problem with first solution: freeze whole game. Just used TweenMax.delayedCall() function instead of setTimeout and setInterval and whole animation synchronized pretty well.