ExtJs 4.2 - Cancelling/clearing the animations chain for a component - javascript

I'm building a web app and there's a part where if a button is clicked, this animation chain will execute:
var textReference = Ext.getCmp('splashText');
var checker = Ext.getCmp('arrow');
var img;
//for some reason, the logic is flipped
if(checker){
img = Ext.getCmp('arrow');
}
else{
console.log('checker is defined');
img = Ext.create('Ext.window.Window', {
header: false,
style: 'background-color: transparent; border-width: 0; padding: 0',
bodyStyle: 'background-color: transparent; background-image: url(graphics/arrow_1.png); background-size: 100% 100%;',
width: 70,
id: 'arrow',
height: 70,
border: false,
bodyBorder: false,
frame: false,
cls: 'noPanelBorder',
x: textReference.getBox().x - 90,
y: textReference.getBox().y - 10,
shadow: false,
});
}
img.show();
var origW = img.getBox().width,
origH = img.getBox().height,
origX = img.getBox().x,
origY = img.getBox().y;
//go down
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 0,
from: {
x: textReference.getBox().x - 90,
y: textReference.getBox().y - 10,
},
to: {
y: origY + 180,
opacity: 1,
}
});
//bounce up 1st
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 500,
from: {
},
to: {
y: origY + 50,
}
});
//fall down 1st
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 1000,
from: {
},
to: {
y: origY + 180,
}
});
//bounce up 2nd
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 1500,
from: {
},
to: {
y: origY + 100,
}
});
//fall down 2nd
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 2000,
from: {
},
to: {
y: origY + 180,
}
});
//fade out
Ext.create('Ext.fx.Anim', {
target: img,
duration: 1000,
delay: 3500,
from: {
},
to: {
x: textReference.getBox().x - 90,
y: textReference.getBox().y - 10,
opacity: 0
}
});
It's just a basic animation where a "ball" will drop, then bounce up and down that makes it look like its bouncing up then down twice before staying at the bottom, then fades out.
It's working well, however, if the user clicks on the button over and over again while the previous animation chain is still animating, the animations will compound and the calculations for the positions will get compounded, making the ball appear much lower than it should.
To prevent this, what I want to happen is once the button gets clicked, the whole animation chain gets cancelled first, then the animation starts from the top. This is to ensure that any existing animation will get halted and then a fresh sequence will get started.
Does anyone have any idea on how to execute this? I've tried stopAnimation() but nothing happens.

The problem with ball dropping lower and lower is that you define original Y as textReference.getBox().y - 10 and then you try to move it to a different original Y + 180. Set your original Y first as textReference.getBox().y - 10 and then just use it for window placement, start of the aniamtion and other animation parts.
Secondly, you should use img.animate() instead of
Ext.create('Ext.fx.Anim', {
target: img,
If you do that, you can then use the stopAnimation() method you mentioned.
Example code:
var windowId = 'basketBallAnimation';
var img = Ext.getCmp( windowId );
// Set whatever you want here and use it
var originalX = 30;
var originalY = 30;
if( !img )
{
console.log('creating new image');
img = Ext.create('Ext.window.Window', {
header: false,
style: 'background-color: transparent; border-width: 0; padding: 0',
bodyStyle: 'background-color: transparent; background-image: url(//ph-live-02.slatic.net/p/6/molten-official-gr7-fiba-basketball-orange-4397-1336487-66ec7bf47676dee873cbb5e8131c4c1f-gallery.jpg); background-size: 100% 100%;',
width: 70,
height: 70,
id: windowId,
border: false,
bodyBorder: false,
frame: false,
cls: 'noPanelBorder',
x: originalX,
y: originalY,
shadow: false,
});
}
img.stopAnimation();
img.show();
// go down
img.animate({
duration: 500,
from: {
x: originalX,
y: originalY,
},
to: {
y: originalY + 180,
}
});
// ...
Working fiddle here.
Other off-topic comments on your coding style:
There's no need to save the image window to 'checker' and if the 'checker' exists to call Ext.getCmp() again, it saves time just to get the image and create it, if it is not saved.
Generally use variables more to store your data instead of calling the same method numerous times (textReference.getBox().x - 90 is called three times just in your small snippet. If you ever want to change it, you have to look really carefully where else it is applied).
If at all possible, avoid defining any style in ExtJS. Add cls configuration with CSS class and apply your styles in separate CSS file.

Related

Phaser 3 - Particles become more the more often I emit them?

I have particles that I emit when clicking and moving my mouse over a certain object. However I noticed that while the particles start out as little, they become more and more the more often I click and move my mouse,until the stream of particles is far too dense.
This only seems to happen when I click down multiple times (thus triggering the pointerdown event multiple times), not when I click once and keep moving.
How can I stop this?
function pet(start, scene, pointer = null)
{
if(start){
scene.input.on('pointermove', function(){
if (scene.input.activePointer.isDown && gameState.chara.getBounds().contains(scene.input.activePointer.x, scene.input.activePointer.y)){
gameState.sparkle.emitParticle(1,scene.input.activePointer.x, scene.input.activePointer.y); // !!!! Here is where I emit my particles
}
});
} else {
gameState.sparkle.stop(); // !!!! Here I stop my particles
}
}
const gameState = {
gameWidth: 800,
gameHeight: 800,
menu: {},
textStyle: {
fontFamily: "'Comic Sans MS'",
fill: "#fff",
align: "center",
boundsAlignH: "left",
boundsAlignV: "top"
},
};
function preload()
{
this.load.baseURL = 'assets/';
// Chara
this.load.atlas('chara', 'chara.png', 'chara.json');
// Particle
this.load.image('sparkle', 'sparkle.png'); // Here I load my particle image
}
function create()
{
// Scene
let scene = this;
// Chara
this.anims.create({
key: "wag",
frameRate: 12,
frames: this.anims.generateFrameNames("chara", {
prefix: 'idle_000',
start: 0,
end: 5}),
repeat: 0,
});
this.anims.create({
key: "happy",
frameRate: 12,
frames: this.anims.generateFrameNames("chara", {
prefix: 'happy_000',
start: 0,
end: 5}),
repeat: -1
});
gameState.chara = this.add.sprite(400, 400, "chara", "idle_0000");
gameState.chara.setInteractive({cursor: "pointer"});
// !!!! Here I set up my Particle Emitter !!!!
gameState.sparkle = this.add.particles('sparkle').createEmitter({
x: gameState.height/2,
y: gameState.width/2,
scale: { min: 0.1, max: 0.5 },
speed: { min: -100, max: 100 },
quantity: 0.1,
frequency: 1,
lifespan: 1000,
gravityY: 100,
on: false,
});
gameState.chara.on('pointerdown', function(){ pet(true, scene) });
gameState.chara.on('pointerout', function(){ pet(false, scene, 'default') });
gameState.chara.on('pointerup', function(){ pet(false, scene, 'pointer') });
}
function update()
{
}
// Configs
var config = {
backgroundColor: "0xf0f0f0",
scale: {
width: gameState.gameWidth,
height: gameState.gameHeight,
autoCenter: Phaser.Scale.CENTER_BOTH
},
scene: {
preload, create, update
}
};
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
The problem is, that you are adding a new scene.input.on('pointermove',...) event-handler in the pet function, on each click.
I would only change the code abit (look below), this should prevent generating too many particles and too many event-handlers (too many event-handlers could harm performance, so be careful, when adding them).
Here is how I would modify the Code:
(I stripped out and added some stuff to make the demo, easier to understand and shorter. And also that the snippet can be executed, without errors/warnings)
The main changes are marked and explained in the code, with comments
function pet(start, scene, pointer = null)
{
if(start){
// Update: remove Event listener add click state
gameState.mouseDown = true
} else {
// Update: add click state
gameState.mouseDown = false;
gameState.sparkle.stop();
}
}
const gameState = {
gameWidth: 400,
gameHeight: 200,
// Update: add click state
mouseDown: false
};
function create()
{
// Scene
let scene = this;
// Just could for Demo START
var graphics = this.add.graphics();
graphics.fillStyle(0xff0000);
graphics.fillRect(2,2,10,10);
graphics.generateTexture('particle', 20, 20);
graphics.clear();
graphics.fillStyle(0xffffff);
graphics.fillRect(0,0,40,40);
graphics.generateTexture('player', 40, 40);
graphics.destroy();
// Just Code for Demo END
gameState.chara = this.add.sprite(200, 100, "player");
gameState.chara.setInteractive({cursor: "pointer"});
gameState.sparkle = this.add.particles('particle').createEmitter({
scale: { min: 0.1, max: 0.5 },
speed: { min: -100, max: 100 },
quantity: 0.1,
frequency: 1,
lifespan: 1000,
gravityY: 100,
on: false,
});
gameState.chara.on('pointerdown', function(){ pet(true, scene) });
gameState.chara.on('pointerout', function(){ pet(false, scene, 'default') });
gameState.chara.on('pointerup', function(){ pet(false, scene, 'pointer') });
// Update: add new single Event Listener
gameState.chara.on('pointermove', function(pointer){
if(gameState.mouseDown){
gameState.sparkle.emitParticle(1,pointer.x, pointer.y);
}
});
}
// Configs
var config = {
width: gameState.gameWidth,
height: gameState.gameHeight,
scene: { create }
};
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

ExtJS Classic: When animating x/y/width/height of a window, the title is not ending at the final position specified

Using ExtJS Classic 7.3.0
Trying to animate the window while it is showing, but the title of the window is not ending up at the specifications in 'TO' config. Fiddle is here: Sencha Fiddle
If you try to drag the window by clicking on the "blue title", you will see the entire title how it should be, but thats it.
Anyone know if I need to specify something else here?
I tinkered with it a bit and found that it worked if you set the width property on the window:
width: Ext.getBody().getViewSize().width / 2,
Full code:
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.create('Ext.Window', {
id: 'win',
title: "Window",
width: Ext.getBody().getViewSize().width / 2,
html: "<h1>Hello</h1> ",
listeners: {
show: function (win) {
var el = win.getEl();
var size = Ext.getBody().getViewSize();
el.animate({
from: {
opacity: 0,
width: 0,
height: 0,
x: size.width/2,
y: size.height/2
},
to: {
opacity: 100,
width: size.width/2,
height: size.height/2,
x: size.width/4,
y: size.height/4
},
});
},
beforeclose: function (win) {
if (!win.shouldClose) {
win.getEl().fadeOut({
duration: 2000,
callback: function () {
win.shouldClose = true;
win.close();
}
});
}
return win.shouldClose ? true : false;
}
}
}).show();
}
});
width: '50%'
also seems to work.

How can i make a child animation happen every time the parent animation is beginning using framer motion & react

I'm trying to make a squeezing bubble animation on repeat, using framer motion & react, but I cant make the squeeze animation happen every time the movement animation is beginning.
instead only the first time the animations run it works but after that only the movement animation repeats itself, if I try to repeat the squeeze animation it just gets out of order
import React from "react";
import styled from "styled-components";
import { motion } from "framer-motion";
const Bubble = () => {
const shapeVariants = {
hidden: {
height: 450,
width: 50,
},
visible: {
height: 250,
width: 250,
transition: {
type: "spring",
bounce: 1,
stiffness: 700,
ease: "easeIn",
},
},
};
const MoveVariants = {
hidden: {
y: 1300,
},
visible: {
y: -280,
transition: {
duration: 2,
ease: "easeIn",
repeat: Infinity,
},
},
};
return (
<motion.div variants={MoveVariants} initial={"hidden"} animate={"visible"}>
<RoundDiv
onAnimationComplete={(definition) => console.log(definition)}
variants={shapeVariants}
/>
</motion.div>
);
};
const RoundDiv = styled(motion.div)`
height: 250px;
width: 250px;
background-color: #05386b;
border-radius: 50%;
`;
export default Bubble;
You just needed to add to your shapeVariants transition to get them to sync up.
import React from "react";
import styled from "styled-components";
import { motion } from "framer-motion";
const Bubble = () => {
const shapeVariants = {
hidden: {
height: 450,
width: 50,
},
visible: {
height: 250,
width: 250,
transition: {
type: "spring",
bounce: 1,
stiffness: 700,
ease: "easeIn",
duration: 2, /* new */
repeat: Infinity, /* new */
},
},
};
const MoveVariants = {
hidden: {
y: 1300,
},
visible: {
y: -280,
transition: {
duration: 2,
ease: "easeIn",
repeat: Infinity,
},
},
};
return (
<motion.div
variants={MoveVariants}
initial={"hidden"}
animate={"visible"}
>
<RoundDiv
onAnimationComplete={(definition) => console.log(definition)}
variants={shapeVariants}
/>
</motion.div>
);
};
const RoundDiv = styled(motion.div)`
height: 250px;
width: 250px;
background-color: #05386b;
border-radius: 50%;
`;
export default Bubble;
I would also recommend using originX and originY to manipulate the spring transition on the bubble otherwise it will animate the bounce based on the top left corner. I would use percentage values such as originX: "50%" but it depends on the effect you are looking for.
The cascading animation in framer-motion is powered by the variant being propagated through the children.
You are running into this setback because you are only animating to the `visible variant once. Thus the variant propagation only happens once.
Potential Solutions
Dumb solution: include a duration into the shapeVariant and make it also repeat then manually time your animation to where you need it. This isn't optimal because you probably want your bubble animation to be type spring?
const shapeVariants = {
hidden: {
height: 450,
width: 50,
},
visible: {
height: 250,
width: 250,
transition: {
type: "spring",
bounce: 1,
stiffness: 700,
ease: "easeIn",
duration: 2,
repeat: Infinity
},
},
};
Alternatively you could control your animation with an effect that would use setTimeout or something to change the variant of the parent over and over to get the cascading effect

How to scroll to a Label in GSAP scrollTrigger scrub Timeline

So I'm having this problem I have a scrollTrigger Timeline and i want a button to scroll to take me to a specific position.
I tried using seek but it didnt work even ScrollTo plugin lack a support for this
I have a gsap Timeline
const tl = gsap.timeline({
paused: true,
scrollTrigger: {
trigger: '.home',
start: 'top top',
end: 'bottom+=1000 top',
pin: true,
scrub: 0.5,
markers: true
},
defaults: {
duration: 2
}
})
// Main home animation
.to(['.left'], {
clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',
ease: 'power1.out'
}, 'scene')
// Name scaling animation
.to(['.name-title'], {
scale: 0,
ease: 'power1.out'
}, 'scene')
.from(document.getElementById('achievements'), {
autoAlpha: 0
}, '-=1.5')
// Achievements reveal animation
.from(document.getElementById('achievements').querySelectorAll('.row'), {
motionPath: {
path: [
{
x: 0,
y: 100
}
]
},
autoAlpha: 0,
stagger: 0.2,
ease: 'power3.out'
}, '-=2')
.addLabel('achievements')
// Hide achievements animation
.to(document.getElementById('achievements').querySelectorAll('.row'), {
motionPath: {
path: [
{
x: 0,
y: -100
}
]
},
autoAlpha: 0,
stagger: 0.2,
ease: 'power3.in'
}, '+=2')
and i have a button in my home that should take me to achievements section but i cant find any proper solution.
So i deviced my own workaround solution (P.S: im using Vue)
scrollTo () {
const pos = Math.ceil(document.body.scrollHeight * (this.loadTl.labels.achievements / this.loadTl.duration()))
gsap.to(window, { duration: 2, scrollTo: pos, ease: 'linear' })
}
loadTl here is a computed property for my Timeline i Found the position of achievements and used ScrollToPlugin to scroll to that pixel.
But im looking for a better way of doing this.

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)

Categories