When clicked, I want a button to produce three child options, which then when tapped again should do retract and then disappear when behind the parent button. I hope this is clear from the code (note this is a nativescript application, hence the slightly strange css choices).
exports.fabTap = function (args) {
var google = page.getViewById("google");
var facebook = page.getViewById("facebook");
var amazon = page.getViewById("amazon");
if (clicked == false) {
google.style.visibility = "visible";
facebook.style.visibility = "visible";
amazon.style.visibility = "visible";
google.animate({
translate: { x: -55, y: -66 },
duration: 500,
curve: enums.AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
facebook.animate({
translate: { x: 0, y: -75 },
duration: 500,
curve: enums.AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
amazon.animate({
translate: { x: 55, y: -66 },
duration: 500,
curve: enums.AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
} else {
google.animate({
translate: { x: 0, y: 0 },
duration: 500,
curve: enums.AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
facebook.animate({
translate: { x: 0, y: 0 },
duration: 500,
curve: enums.AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
amazon.animate({
translate: { x: 0, y: 0 },
duration: 500,
curve: enums.AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
google.style.visibility = "collapse";
facebook.style.visibility = "collapse";
amazon.style.visibility = "collapse";
}
clicked = !clicked;
}
However, as you can see from the gif, on the return journey, the buttons just disappear before the return. What can I do to ensure this animates in sequence?
You're doing this:
google.style.visibility = "collapse";
facebook.style.visibility = "collapse";
amazon.style.visibility = "collapse";
immediately after starting the animations, not giving the animations time to run before you do it.
If you want to wait, either use a callback on the animations, or just delay 500ms.
I don't know what animation lib you're using, so I can't show an example of a callback waiting for all three to be done.
Here's the "just wait 500ms" version, though:
setTimeout(function() {
google.style.visibility = "collapse";
facebook.style.visibility = "collapse";
amazon.style.visibility = "collapse";
}, 500);
You could probably do this easily with transitions. Instead of using visibility, try using opacity to fade your buttons in and out. Or, you could add a transition end listener to your JS and only set visibility to collapse after the three tabs have moved back under the main button. I think your problem is trying to animate visibility.
Related
I'm trying to make some kind of a notification/message box giving players hints to advance the gameplay, here is the code
var game = new Phaser.Game({
width: 320, height: 200,
scene: {
create: create
}
});
function create() {
let noti_bg = this.add.rectangle(160, 100, 200, 120, 0x000000, .5);
let noti_txt = this.add.text(0, 0, 'Magic is not ready yet\n\nwait a sec');
Phaser.Display.Align.In.Center(noti_txt, noti_bg);
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Phaser.Display.Align.In.Center works well with the first line of my text but doesn't center my 2nd line. How do I fix it?
Phaser.Display.Align.In.Center just aligns single GameObjects. Both lines of text are in the same GameObject noti_txt.
If you want to align both textlines, you could use the align property of the text GameObject, on creating it. { align: 'center' }
(Or after creation with the property setStyle here a link to the documentation)
Here the adapted Code:
var game = new Phaser.Game({
width: 320, height: 200,
scene: {
create: create
}
});
function create() {
let noti_bg = this.add.rectangle(160, 100, 200, 120, 0x000000, .5);
let noti_txt = this.add.text(0, 0, 'Magic is not ready yet\n\nwait a sec', { align: 'center' });
Phaser.Display.Align.In.Center(noti_txt, noti_bg);
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Alternatively / Extra:
I would only recommended it, if you are reusing text blocks (or dramatic effect), you could split the text, into two GameObjects.
But for that to work you would also have use the function Phaser.Display.Align.To.BottomCenter:
var game = new Phaser.Game({
width: 320, height: 200,
scene: {
create: create
}
});
function create() {
let noti_bg = this.add.rectangle(160, 100, 200, 120, 0x000000, .5);
let noti_txt1 = this.add.text(0, 0, 'Magic is not ready yet');
let noti_txt2 = this.add.text(0, 0, 'wait a sec');
// extra visual effect
this.tweens.add({
targets: noti_txt2 ,
alpha: 0,
ease: 'Power1',
duration: 1000,
yoyo: true,
repeat: -1,
});
Phaser.Display.Align.In.Center(noti_txt1, noti_bg);
// Just adding a minor horizontal offset
Phaser.Display.Align.To.BottomCenter(noti_txt2, noti_txt1, 0, 10);
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I have this piece of code,
let spin = new TimelineMax();
spin.to($('.particle'), 150, {
rotation: 360,
repeat: -1,
transformOrigin: '50% 50%',
ease: Linear.easeNone
});
What I need to do is to change the 150 every second (interval) which indicate how fast the spin is going and this to appear real-time. What's the best method to do it?
You have to clear the current TimelineMax and start another one:
let spin = new TimelineMax();
setInterval(() => {
spin.clear();
spin.to($('.particle'), Math.random(), {
rotation: 360,
repeat: -1,
transformOrigin: '50% 50%',
ease: Linear.easeNone
});
}, 1000);
You could use something like Generators for this,
function* myGen() {
let spin = 0;
for (;;) {
yield spin += 150;
}
}
const gen = myGen();
const start = setInterval(() => {
var next = gen.next();
if (next.done) {
clearInterval(start);
} else {
// your code here, commenting it for now
// spin.to($('.particle'), next.value, {
// rotation: 360,
// repeat: -1,
// transformOrigin: '50% 50%',
// ease: Linear.easeNone
// });
console.log(next.value);
}
}, 1000)
For long-running scripts, you might want to consider a worker
I've been trying to resolve my problem by reading other topics linked to gsap / on.resize issues but i didnt found any proper answer.
When I open the menu, the logo is working correctly and set it's position to center of the screen { y:0 } . However, once the menu is closed i cant manage to force the logo back to it's original position { y:-logoPosition +20 }. The values are not updating on "menuclosing state".
I must be messing with var updates but i'm clueless to make this work.
Hope someone can help me !
Thanks in advance :)
Jsfiddle
// Scope
var menuFrame = $('#menu-frame');
var menuH = window.innerHeight;
var navH = $("nav").height();
var logoH = $("#logo").height();
var logoPosition = (menuH -navH)/2;
var toggleMenu = $('.menu-toggle');
var logo = $('#logo');
var logoDiv = $('#logo-divider');
var navList = $('ul#menu-nav li');
// New Gsap timeline
var menuAnim = new TimelineMax({ paused: true, reversed: true });
// Animation when toggle clicked
menuAnim.fromTo([menuFrame], 0.6, {backgroundColor : 'rgba(0, 0, 0, 0.01)'}, {backgroundColor : 'rgba(250, 250, 250, 1)', ease: Power2.easeInOut},0.1);
menuAnim.from([logo], 0.6, { y:-logoPosition +20, ease: Power2.easeInOut });
menuAnim.fromTo([logoDiv], 0.6, { width: 0}, { width: '20%', ease: Power2.easeInOut });
menuAnim.staggerFromTo(navList, 1.2, {scale:0.5, opacity:0, delay:0.5}, { scale:1, opacity:1, ease:Elastic.easeOut, force3D:true}, 0.2);
// Reverse anmiation on click
toggleMenu.on('click', function() {
$(this).toggleClass('active');
menuAnim.reversed() ? menuAnim.play() : menuAnim.reverse();
});
// If toggle is active logo position is middle off the frame
$(window).resize(function(){
if ($(toggleMenu).hasClass('active')) {
menuH = window.innerHeight;
navH = $("nav").height();
logoH = $("#logo").height();
logoPosition = (menuH -navH)/2;
var menuAnim = new TimelineMax({ paused: true, reversed: true });
menuAnim.from([logo], 0.6, { y:0 }, { ease: Power2.easeInOut });
menuAnim.pause()
}
// If toggle is not active logo position = top of the frame
else {
menuH = window.innerHeight;
navH = $("nav").height();
logoH = $("#logo").height();
logoPosition = (menuH -navH)/2;
var menuAnim = new TimelineMax({ paused: true, reversed: true });
menuAnim.from([logo], 0.6, { y:-logoPosition +20 }, { ease: Power2.easeInOut });
menuAnim.pause()
}
});
I am creating a little app with KineticJS which creates multiple nodes (RegularPolygons). After the stage is loaded (triggered with play(); ) I fill each node sequentially with an (pattern)image (triggered with loadImages(urls);.
This works fine, but now I want to add a nice fade-in effect with a Tween, like:
set all nodes > load single image > set node patternImage > tween to opacity 0.8 > on tween complete, load next image (repeat).
For some reason the tweens won't play, they just appear in complete state (opacity 1) ;(
var stage = new Kinetic.Stage({
container: 'canvas',
width: $(window).width(),
height: $(window).height()
});
var layer = new Kinetic.Layer()
var layer2 = new Kinetic.Layer()
var urls =
[
'http://lorempixel.com/300/300/sports/1',
'http://lorempixel.com/300/300/sports/2',
'http://lorempixel.com/300/300/sports/3',
'http://lorempixel.com/300/300/sports/4',
'http://lorempixel.com/300/300/sports/5',
'http://lorempixel.com/300/300/sports/6',
'http://lorempixel.com/300/300/sports/7',
'http://lorempixel.com/300/300/sports/8',
'http://lorempixel.com/300/300/sports/9',
'http://lorempixel.com/300/300/sports/10',
'http://lorempixel.com/300/300/business/1',
'http://lorempixel.com/300/300/business/2',
'http://lorempixel.com/300/300/business/3',
'http://lorempixel.com/300/300/business/4',
'http://lorempixel.com/300/300/business/5',
'http://lorempixel.com/300/300/business/6',
'http://lorempixel.com/300/300/business/7',
'http://lorempixel.com/300/300/business/8',
'http://lorempixel.com/300/300/business/9',
'http://lorempixel.com/300/300/business/10'/*,
'http://lorempixel.com/300/300/cats/1',
'http://lorempixel.com/300/300/cats/2',
'http://lorempixel.com/300/300/cats/3',
'http://lorempixel.com/300/300/cats/4',
'http://lorempixel.com/300/300/cats/5',
'http://lorempixel.com/300/300/cats/6',
'http://lorempixel.com/300/300/cats/7',
'http://lorempixel.com/300/300/cats/8',
'http://lorempixel.com/300/300/cats/9',
'http://lorempixel.com/300/300/cats/10',
'http://lorempixel.com/300/300/nature/1',
'http://lorempixel.com/300/300/nature/2',
'http://lorempixel.com/300/300/nature/3',
'http://lorempixel.com/300/300/nature/4',
'http://lorempixel.com/300/300/nature/5',
'http://lorempixel.com/300/300/nature/6',
'http://lorempixel.com/300/300/nature/7',
'http://lorempixel.com/300/300/nature/8',
'http://lorempixel.com/300/300/nature/9',
'http://lorempixel.com/300/300/nature/10',
'http://lorempixel.com/300/300/people/1',
'http://lorempixel.com/300/300/people/2',
'http://lorempixel.com/300/300/people/3',
'http://lorempixel.com/300/300/people/4',
'http://lorempixel.com/300/300/people/5'*/
];
// LOAD IMAGES
function loadImages(arrayOfImages, index) {
index = index || 0;
if (arrayOfImages && arrayOfImages.length && arrayOfImages.length > index) {
var img = new Image();
img.onload = function() {
var pane = layer2.get('#pane_' + index );
pane.fill('').fillPatternImage(img);
stage.draw(); // <<< THIS WORKS
var tween = new Kinetic.Tween({
node: pane,
duration: 1,
opacity: 0.8,
easing: Kinetic.Easings.BackEaseOut,
onFinish: function() {
loadImages(arrayOfImages, index + 1
}
}).play; // <<< NOT WORKING
//setTimeout(function(){ loadImages(arrayOfImages, index + 1); }, 200);
};
img.src = arrayOfImages[index];
}
}
function start() {
console.log("numOfPanes: " +urls.length);
for (i = 0; i <= urls.length; i++) {
var shape0 = new Kinetic.RegularPolygon({
x: i * 15,
y: i * 15,
sides: 5,
rotation: i * 10,
radius: 70,
fill: 'Red'
});
var shape1 = new Kinetic.RegularPolygon({
x: i * 15,
y: i * 15,
sides: 5,
rotation: i * 10,
radius: 70,
opacity: 0,
fillPatternOffset: {x:-220, y:70},
id: 'pane_' + i,
name: 'pane',
fill: 'Green'
});
layer.add(shape0);
layer2.add(shape1);
}
stage.add(layer,layer2);
}
// INIT
start();
// trigger loadimages() with console
loadImages(urls);
play is a function. So you should call it.
var tween = new Kinetic.Tween({
node: pane,
duration: 1,
opacity: 0.8,
easing: Kinetic.Easings.BackEaseOut,
onFinish: function() {
loadImages(arrayOfImages, index + 1
}
}).play();
Also: when you are finding node with get function it returns Collection. So if you need just node use:
var pane = layer2.get('#pane_' + index )[0];
I'm trying to animate my jquery dial and have the libraries loaded and all yet when I try to animate them after building the knob object, nothing happens. If I call both $('#dial') on both the animate and knob constructor it animates but doesn't ceil the value or give it a percent until I click it after its finished animating.
Here is what I have got
HTML
<input type="text" value="0" class="dial"/>
Javascript
var dial = $('.dial').knob({
value: 0,
width: 120,
height: 120,
min: 0,
max: 100,
stopper: true,
readOnly: true,//if true This will Set the Knob readonly cannot click
draw: function () {
$(this.i).val(this.cv + '%'); //Puts a percent after values
},
'format': function(v) {return v + '%'},
thickness: 0.1,
tickColorizeValues: true,
skin: 'tron'
});
dial.animate({//animate to data targetValue
value: 89
}, {
duration: 5000,
easing: 'swing',
step: function () {
var percent = Math.ceil(this.value) + '%';
$(this).val(Math.ceil(this.value) + '%').trigger('change');
// $(this).val($(this).val() + '%');
}
});
here my code maybe help you (included animation and style )
http://codepen.io/AdelDima/pen/ueKrI
I used GSAP animation library as there seemed to be a conflict with step and progress callbacks from the jquery animate.