How to disable a function to relaunch on hover - javascript

I'm trying to make a bouncy ball which move on hover by the user. I got a problem on how to disable the function to relaunch if the ball cross again the mouse. It cause some freeze/bugs sometimes. I'm not exactly sure on how to proceed to avoid that. Do you guys have any tips ?
https://codepen.io/kombolo/pen/YvzPvm
Thanks !
window.onmousemove = logMouseMove;
function logMouseMove(e) {
e = e || window.event;
mousePos = e.clientX;
el = document.querySelector('.circle');
const elPos = getOffset(el);
if(mousePos < (elPos -20) || mousePos > (elPos + 20)) {
triggerAnimation();
};
}
function getOffset(el) {
el = el.getBoundingClientRect();
return el.left + window.scrollX + 50;
}
function triggerAnimation() {
xValues = [100, 0, 400];
yValues = [0, 50, 200];
const xVal = xValues[Math.floor(Math.random()*xValues.length)];
const yVal = yValues[Math.floor(Math.random()*yValues.length)];
var duration = anime({
targets: '#duration .el',
translateX: [
{ value: xVal, duration: 1000, delay: 500, elasticity: 0 },
{ value: xVal, duration: 1000, delay: 500, elasticity: 0 }
],
scaleX: [
{ value: 2, duration: 100, delay: 500, easing: 'easeOutExpo' },
{ value: 1, duration: 900, elasticity: 300 },
{ value: 2, duration: 100, delay: 500, easing: 'easeOutExpo' },
{ value: 1, duration: 900, elasticity: 300 }
],
translateY: [
{ value: yVal, duration: 1000, delay: 500, elasticity: 0 },
{ value: yVal, duration: 1000, delay: 500, elasticity: 0 }
],
});
}

Mouse hover is an extensive event. You are doing too much on hover.
what I would suggest is - set window.onmousemove = undefined in
triggerAnimation function and then turn it on in the last. This helped a little.
function triggerAnimation() {
window.onmousemove = void 0;
xValues = [100, 0, 400];
.....
....
//loop: true
});
window.onmousemove = logMouseMove;
}

Related

Phaser: How to fix the delay of the restart function, i am using window.setTimeout

var config = {
type: Phaser.AUTO,
width: 1900,
height: 1000,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
var main = document.getElementById("startBtn")
var gameOver
score = 0;
function start() {
game = new Phaser.Game(config);
main.innerHTML = ''
score = 0;
}
function preload() {
this.load.image('Background', 'assets/Background.jpg');
this.load.image('ground', 'assets/platform.png');
this.load.image('coin', 'assets/coin.png');
this.load.image('redCoin', 'assets/redCoin.png');
this.load.spritesheet('monkey',
'assets/monkey.png',
{ frameWidth: 600, frameHeight: 720 }
);
}
var platforms;
var score = 0;
var scoreText;
function create() {
this.add.image(500, 275, 'Background').setScale(3);
this.w = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W)
this.a = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A)
this.s = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S)
this.d = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D)
platforms = this.physics.add.staticGroup();
platforms.create(200, 650, 'ground').setScale(0.15).refreshBody();
platforms.create(600, 400, 'ground').setScale(0.15).refreshBody();
platforms.create(1600, 650, 'ground').setScale(0.15).refreshBody();
platforms.create(750, 100, 'ground').setScale(0.15).refreshBody();
platforms.create(850, 750, 'ground').setScale(0.15).refreshBody();
platforms.create(100, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(400, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(700, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(1000, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(1300, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(1600, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(1900, 950, 'ground').setScale(0.15).refreshBody();
platforms.create(1800, 800, 'ground').setScale(0.15).refreshBody();
platforms.create(250, 250, 'ground').setScale(0.15).refreshBody();
platforms.create(1000, 500, 'ground').setScale(0.15).refreshBody();
platforms.create(1150, 220, 'ground').setScale(0.15).refreshBody();
player = this.physics.add.sprite(100, 450, 'monkey').setScale(0.075);
this.physics.add.collider(player, platforms);
player.setBounce(0.2);
player.setCollideWorldBounds(true);
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('monkey', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'turn',
frames: [{ key: 'monkey', frame: 4 }],
frameRate: 20
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('monkey', { start: 5, end: 8 }),
frameRate: 10,
repeat: -1
});
coins = this.physics.add.group({
key: 'coin',
repeat: 10,
setXY: { x: 12, y: 0, stepX: 150 }
});
coins.children.iterate(function (child) {
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
child.setScale(0.05)
});
this.physics.add.collider(coins, platforms);
this.physics.add.overlap(player, coins, collectCoin, null, this);
redCoins = this.physics.add.group();
this.physics.add.collider(redCoins, platforms);
this.physics.add.collider(player, redCoins, hitredCoin, null, this);
scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '64px', fill: 'rgb(85, 1, 1)' });
}
function update() {
cursors = this.input.keyboard.createCursorKeys();
if (cursors.left.isDown) {
player.setVelocityX(-160);
player.anims.play('left', true);
}
else if (cursors.right.isDown) {
player.setVelocityX(160);
player.anims.play('right', true);
}
else {
player.setVelocityX(0);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down) {
player.setVelocityY(-330);
}
}
function collectCoin(player, coin) {
coin.disableBody(true, true);
score += 1;
scoreText.setText('Score: ' + score);
if (coins.countActive(true) === 0) {
coins.children.iterate(function (child) {
child.enableBody(true, child.x, 0, true, true);
});
var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400);
var redCoin = redCoins.create(x, 16, 'redCoin').setScale(0.05);
redCoin.setBounce(1);
redCoin.setCollideWorldBounds(true);
redCoin.setVelocity(Phaser.Math.Between(-200, 200), 20);
}
}
function hitredCoin(player, redCoin) {
this.physics.pause();
player.setTint(0xff0000);
player.anims.play('turn');
gameOver = true;
window.setTimeout(restart, 3000);
}
function restart () {
this.scene.stop();
this.scene.start();
}
This code is my game and it doesn't work because you can not respawn when you die, the delay doesn't work. I would like help fixing the time delay at the end of the hitredcoinfunction that calls the restart function. Please help me with this problem
it may be because I am using a delay thing from javascript, if so please tell me how to swap it out for the phaser version
pls help me with this, I really need my game to work
The problem is that the this context is not set.
you can do this simply with the bind function. Here is the link to the documentation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind)
Just change the line to this:
window.setTimeout(restart.bind(this), 3000);
the bind function, passes an object/context (in this case the phaser scene) to the function, so that it can be referred to as this.
If you want to use the phaser delayedCall function you have to replace the line with (link to the documentation https://photonstorm.github.io/phaser3-docs/Phaser.Time.Clock.html ):
this.time.delayedCall(3000, restart, null, this);
the first parameter is the time in ms: 3000
the second parameter is the function to call: restart
the third parameter are arguments, that should be passed to the passed function: null
the last parameter is the context, for the passed function: this (the current scene)

How to scroll new page to top on load on click?

I'm using barba.js to fade new pages in. Code must be placed between the barba wrapper div. When I click a link to a new page though, the new page fades in at the same position as the previous page was in. So if the user clicks a link, the new page is loading in near , which I don't want.
How can I get the new page to load in at the top?
I found the answer on stack about scrollRestoration function but I still don't understand how to make this work.
Could somebody help me to compile the js code below, add some css or html to fix this issue?
$(window).scrollTop(0);
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
function delay(n) {
n = n || 2000;
return new Promise((done) => {
setTimeout(() => {
done();
}, n);
});
}
function pageTransition() {
var tl = gsap.timeline();
tl.to(".animate-this", {
duration: .2, opacity: 0, delay: 0, y: 30, //scale:0, delay: 0.2,
});
tl.to(".animate-this", {
duration: 0, opacity: 0, delay: 0, scale:0, //scale:0, delay: 0.2,
});
tl.fromTo(".loading-screen", {width: 0, left: 0}, {
duration: 1,
width: "100%",
left: "0%",
ease: "expo.inOut",
delay: 0.3,
});
tl.to("#svg-1", {
duration: 1, opacity: 0, y: 30, stagger: 0.4, delay: 0.2,
});
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "100%",
ease: "expo.inOut",
delay: 0, //CHANGE TIME HERE END TRANS
});
tl.set(".loading-screen", { left: "-100%", });
tl.set("#svg-1", { opacity: 1, y: 0 });
}
function contentAnimation() {
var tl = gsap.timeline();
tl.from(".animate-this", { duration: .5, y: 30, opacity: 0, stagger: 0.4, delay: 0.2 });
}
$(function () {
barba.init({
sync: true,
transitions: [
{
async leave(data) {
const done = this.async();
pageTransition();
await delay(2500);
done();
},
async enter(data) {
contentAnimation();
},
async once(data) {
contentAnimation();
},
},
],
});
});
Have a look:
https://pasteboard.co/Ja33erZ.gif
Here also a codepen to check my issue (html,css):
https://codepen.io/cat999/project/editor/AEeEdg
scrollRestoration shouldn't be necessary, this can be done by resetting scroll position during the transition:
tl.fromTo(".loading-screen", {width: 0, left: 0}, {
duration: 1,
width: "100%",
left: "0%",
ease: "expo.inOut",
delay: 0.3,
onComplete: function() {
window.scrollTo(0, 0);
}
});
Working example here: https://codepen.io/durchanek/project/editor/ZWOyjE
This should be what you're looking for:
barba.hooks.enter(() => {
window.scrollTo(0, 0);
});
(can be found in the docs)

What is the best method of starting/ stoping js anime timeline on input

I'm attempting to create an animation (using anime.js) that plays an animation timeline once when the users starts typing and will loop for the duration that the user is typing in the input box. When the user stops typing the animation will complete its current loop and stop playing.
Here's my progress at the moment, you can observe the animation working incorrectly: https://codepen.io/andrewbentley/full/XqMrze
Here's what my anime.js code timeline:
var basicTimeline = anime.timeline({
loop: true,
autoplay: false,
duration: 700
});
basicTimeline
.add({
targets: '#circ1',
duration: 100,
translateY: -10,
easing: 'easeInQuad'
})
.add({
targets: '#circ1',
duration: 100,
translateY: 0,
easing: 'easeInQuad'
})
.add({
targets: '#circ2',
duration: 100,
translateY: -10,
easing: 'easeInQuad'
})
.add({
targets: '#circ2',
duration: 100,
translateY: 0,
easing: 'easeInQuad'
})
.add({
targets: '#circ3',
duration: 100,
translateY: -10,
easing: 'easeInQuad'
})
.add({
targets: '#circ3',
duration: 100,
translateY: 0,
easing: 'easeInQuad'
})
.add({
targets: '#circ3',
delay: 100
});
document.querySelector('#email').onkeypress = basicTimeline.play;
document.querySelector('#email').onkeyup = basicTimeline.pause;
Is anyone able to advise me as to the best way of achieving the desired effect whilst using anime.js timeline utility and the use of event listeners such as onkeypress, onkeyup etc.
I think you need a timer which pauses your animation after some time of inactivity.
https://codepen.io/anon/pen/qYoPKQ
var timer = false;
document.querySelector('#email').onkeypress = function () {
if(basicTimeline.paused) basicTimeline.play();
if(timer) clearTimeout(timer);
timer = setTimeout(function() {
basicTimeline.pause();
basicTimeline.reset();
}, 500);
};
The issue of stopping an animation at the end of the current loop has been raised here:
on github There are some good suggestions there.
Perhaps the best one is from Edrees Jalili
function pausableLoopAnime({loopComplete, ...options}) {
const instance = anime({
...options,
loop: true,
loopComplete: function(anim) {
if(instance.shouldPause) anim.pause();
if(typeof loopComplete === 'function') loopComplete(anim);
}
});
instance.pauseOnLoopComplete = () => instance.shouldPause = true;
return instance;
}
const animation= pausableLoopAnime({
targets: '.polymorph',
points: [
{ value: '215, 110 0, 110 0, 0 47.7, 0 67, 76' },
{ value: '215, 110 0, 110 0, 0 0, 0 67, 76' }
],
easing: 'easeOutQuad',
duration: 1200,
});
setTimeout(animation.pauseOnLoopComplete, 100)

Using for-loop instead of creating new var for every element

I have created animations that trigger when elements enter in viewport. Problem is that I have to repeat same code for every element. I'm using anime.js and scrollMonitor.js for animations, and it's kinda hard to make working for-loop.
Here is my code:
$(window).on("load", function() {
'use strict';
var elementWatcher1 = scrollMonitor.create('#about', -200);
elementWatcher1.enterViewport(function() {
var startAnimation = anime.timeline();
startAnimation
.add({
targets: '#about .toAnimate',
translateY: [50, 0],
opacity: 1,
duration: 600,
delay: function(el, index) {
return index * 80;
},
easing: 'easeOutQuad'
});
this.destroy();
});
var elementWatcher2 = scrollMonitor.create('#portfolio', -200);
elementWatcher2.enterViewport(function() {
var startAnimation = anime.timeline();
startAnimation
.add({
targets: '#portfolio .toAnimate',
translateY: [50, 0],
opacity: 1,
duration: 600,
delay: function(el, index) {
return index * 80;
},
easing: 'easeOutQuad'
});
this.destroy();
});
var elementWatcher3 = scrollMonitor.create('#gallery', -200);
elementWatcher3.enterViewport(function() {
var startAnimation = anime.timeline();
startAnimation
.add({
targets: '#gallery .toAnimate',
translateY: [50, 0],
opacity: 1,
duration: 600,
delay: function(el, index) {
return index * 80;
},
easing: 'easeOutQuad'
})
.add({
targets: '#gallery .toAnimateToo',
opacity: 1,
duration: 600,
delay: function(el, index) {
return index * 80;
},
easing: 'easeOutQuad',
offset: 0
});
this.destroy();
});
});
Does anyone have idea how can I put this in for-loop?
In ES5 just use a forEach loop on an array of those selectors:
$(window).on("load", function() {
'use strict';
["#about", "#portfolio", "#gallery"].forEach(function(selector) {
var elementWatcher = scrollMonitor.create(selector, -200);
elementWatcher.enterViewport(function() {
var startAnimation = anime.timeline();
startAnimation
.add({
targets: selector + ' .toAnimate',
translateY: [50, 0],
opacity: 1,
duration: 600,
delay: function(el, index) {
return index * 80;
},
easing: 'easeOutQuad'
});
this.destroy();
});
});
});
In ES2015+, you could use for-of instead (with const for elementWatcher) instead.

Using Ajax.Updater to get a javascript file (prototypejs)

Here is my ajax request:
new Ajax.Updater({ success: 'footer' }, '/dyn/actions/checkSystemMessage', {
insertion: 'after',
evalScripts: true
});
Here is what's at /dyn/actions/checkSystemMessage:
<script type="text/javascript"><!--
document.observe('dom:loaded', function() {
buildSystemMsg = function(SystemMsg) {
//behind container
behindContainer = new Element('div', {id: 'behind-system-message'});
behindContainer.setStyle({display: 'none'});
document.body.appendChild(behindContainer);
//main container
container = new Element('div', {id: 'system-message'}).update(SystemMsg);
container.setStyle({display: 'none'});
document.body.appendChild(container);
//hide button
hideBtn = new Element('a', {'class': 'close-button', 'title': 'Close System Message'}).update('Close');
hideBtn.setStyle({ marginTop: '5px'});
container.insert({bottom: hideBtn});
offsetY = container.getHeight();
//show
if ($('mod-system-alert'))
{ new Effect.Move($('mod-system-alert'), { queue: 'front', x: 0, y: offsetY, mode: 'relative', duration: 0 }); }
new Effect.Move($('footer'), { queue: 'front', x: 0, y: offsetY, mode: 'relative', duration: 0 });
new Effect.Move($('page-container'), { queue: 'front', x: 0, y: offsetY, mode: 'relative', duration: 0 });
new Effect.Move($('nav'), { queue: 'front', x: 0, y: offsetY, mode: 'relative', duration: 0 });
new Effect.Move($('header-container'), { queue: 'front', x: 0, y: offsetY, mode: 'relative', duration: 0 });
Effect.BlindDown(behindContainer, { queue: 'front', duration: 0 });
Effect.BlindDown(container, { queue: 'end', duration: 0.5 });
hideBtn.observe('click', function() {
if ($('mod-system-alert'))
{ new Effect.Move($('mod-system-alert'), { queue: 'front', x: 0, y: -offsetY, mode: 'relative', duration: 0 }); }
new Effect.Move($('footer'), { queue: 'end', x: 0, y: -offsetY, mode: 'relative', duration: 0 });
new Effect.Move($('page-container'), { queue: 'end', x: 0, y: -offsetY, mode: 'relative', duration: 0 });
new Effect.Move($('nav'), { queue: 'end', x: 0, y: -offsetY, mode: 'relative', duration: 0 });
new Effect.Move($('header-container'), { queue: 'end', x: 0, y: -offsetY, mode: 'relative', duration: 0 });
Effect.BlindUp(behindContainer, { queue: 'front', duration: 0 });
Effect.BlindUp(container, { queue: 'front', duration: 0.5 });
set_cookie("HideSystemMsg", true);
});
}
hideMsg = get_cookie("HideSystemMsg");
systemMsg = '${SystemMsg}';
if (systemMsg.length > 0 && !hideMsg)
buildSystemMsg(systemMsg);
});
--></script>
This is neither inserting the javascript after the element with ID footer nor is it executing the script. It does rely on other javascript libraries that are included on the page where the update is occurring. Could this be where my problem is?
I believe evalScripts will only work if your response headers have a "text/javascript" content type. This is what tells the AJAX library that what you're getting from the server is a script..
Additionally, you would not need the mark up: <script type="text/javascript"><!--, and: --></script>
I hope this helps.

Categories