Increase a number in an function and reload the function - javascript

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

Related

Gsap - on.resize issue

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()
}
});

JavaScript animations not running in order?

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.

Snap.svg - How to always connect two objects with a line?

How do we always connect two objects with a line in Snap.svg?
Plunkr: https://plnkr.co/edit/IgY0jyHbWTLuH4FfZt6o?p=preview
var circleOne = s.circle(150, 150, 100);
var circleTwo = s.circle(450, 450, 100);
var boxOne = circleOne.getBBox();
var boxTwo = circleTwo.getBBox();
var line = s.line(boxOne.cx, boxOne.cy, boxTwo.cx, boxTwo.cy);
line.attr({
stroke: 'red'
});
var t = new Snap.Matrix();
t.translate(200, 0, boxTwo.cx, boxTwo.cy);
setTimeout(function() {
circleTwo.animate({ transform: t}, 500, mina.easein);
}, 1000);
Nevermind, figured it out!
setTimeout(function() {
circleTwo.animate({ transform: t}, 500, mina.easein);
line.animate({
x2: boxTwo.cx + 200
}, 500, mina.easein);
}, 1000);
https://plnkr.co/edit/IgY0jyHbWTLuH4FfZt6o?p=preview

Scale loaded SVG and zoom in with snap svg

So basically what I need to do is to load an SVG with SNAP.svg and add an effect (zoom in) I understand that in order to achieve this I need to respect this :
Load the SVG file
Scale de SVG in (0 ?)
Append this SVG
Add a transform effect (with the suitable scale)
The problem is that I need to display this in a 650 width and 280 height size.
The SVG I'm loading, witch I'll name it 'map' is in 1920 width and 1080 height.
This is my code so far :
<svg id="svg" width="650px" height="280px"></svg>
<script type="text/javascript">
var s = Snap("#svg");
var map = Snap.load("./src/map.svg", function (f) {
g = f.select("g");
var t = Snap.matrix().scale(0.35);
s.append(g);
g.group(g.selectAll("path")).transform(t);
});
</script>
It seems the scale instruction is working find but not the animation.
Also, how I can center this loaded SVG not matter what scale it takes ?
Thank you !
UPDATE :
I managed to add some effects but I don't think the way I'm doing it it's the correct one :
var carte = Snap.load("./src/carte.svg", function (f) {
g = f.select("g");
//var t = Snap.matrix().scale(0.35);
s.append(g);
//Set the map in first position
var firstScene = new Snap.Matrix();
firstScene.translate(300, 160);
firstScene.scale(0.05);
//Zoom effect
var secondScene = new Snap.Matrix();
secondScene.scale(2.0);
secondScene.translate(-850, -360);
//Move the matrix till desired point (not finish)
var threeScene = new Snap.Matrix();
threeScene.translate(-850, -360);
g.animate({ transform: firstScene }, 0, function() {g.animate ({ transform: secondScene}, 1500, mina.linear )});
});
It seems impossible to add a timer or more than two effects ?
Just as an alternative if there are quite a few sequenced animations I may be tempted to write a function to handle an array of animations. It could look something like this...
function nextFrame ( el, frameArray, whichFrame ) {
if( whichFrame >= frameArray.length ) { return }
el.animate( frameArray[ whichFrame ].animation,
frameArray[ whichFrame ].dur,
frameArray[ whichFrame ].easing,
nextFrame.bind( null, el, frameArray, whichFrame + 1 ) );
}
var block = s.rect(100, 100, 100, 100, 20, 20);
.attr({ fill: "rgb(236, 240, 241)", stroke: "#1f2c39",
strokeWidth: 3, transform: 's1' });
var frames = [
{ animation: { transform: 's0.4,0.4,200,200' }, dur: 1000, easing: mina.bounce },
{ animation: { transform: 't-100,-80' }, dur: 1000, easing: mina.bounce },
{ animation: { transform: 's1.2,1.2,300,300t200,-100' },dur: 1000, easing: mina.bounce }
];
nextFrame( block, frames, 0 );
jsfiddle
I think if you want to sequence animations, the most elegant way would be to use promises. For that all you would need is to wrap animate function in Q library or jQuery.Deferred. Here is the example I put together on jsFiddle https://jsfiddle.net/stsvilik/Lnhc77b2/
function animate(obj, conf, duration, asing) {
var def = Q.defer();
obj.animate(conf, dur, easing, function(){
def.resolve();
});
return def.promise;
}
This seems to work fine, but like lan said above, maybe his method it's better for doing animations
var carte = Snap.load("./src/carte.svg", function (f) {
g = f.select("g");
//var t = Snap.matrix().scale(0.35);
s.append(g);
//Load the map
var firstScene = new Snap.Matrix();
firstScene.translate(295, 160);
firstScene.scale(0.04);
//Set scene 1
var secondScene = new Snap.Matrix();
secondScene.scale(0.4);
secondScene.translate(-300, -10);
//Set scene 2
var threeScene = new Snap.Matrix();
//threeScene.scale(0.5);
threeScene.translate(-825, -380);
//Set scene 3
var fourScene = new Snap.Matrix();
fourScene.scale(21.0);
fourScene.translate(-1164, -526);
var anim1 = function() {
g.animate({ transform: firstScene}, 0, anim2);
}
var anim2 = function() {
g.animate({ transform: secondScene}, 1500, mina.easing, anim3);
}
var anim3 = function() {
g.animate({ transform: threeScene}, 1000, mina.linear, anim4);
}
var anim4 = function() {
g.animate({ transform: fourScene}, 2000, mina.easing);
}
anim1();
});
Is the fact of adding several matrix a performance killer ? Or this is the way it should be done ?

kineticJS loading images sequentially (and set fillPatternImg) opacity tween not working

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];

Categories

Resources