This is my script for drawing an SVG path with ScrollMagic:
// Prepare SVG
function pathPrepare_journey($el) {
var lineLength_journey = $el[0].getTotalLength();
$el.css("stroke-dasharray", lineLength_journey);
$el.css("stroke-dashoffset", lineLength_journey);
}
var $journey1 = $("path#path1");
var $journey1_2 = $("path#path2");
var $journey1_3 = $("path#path3");
pathPrepare_journey($journey1);
pathPrepare_journey($journey1_2);
pathPrepare_journey($journey1_3);
// Reference to container
var container = $("#section1");
var containerHeight = $(container).height();
var vpHeight = $(window).height();
// Init controller
var SVGcontroller_journey = new ScrollMagic.Controller();
// Build tween
var tween_journey = new TimelineMax().add(
TweenMax.to($journey1, 1, { strokeDashoffset: 0, ease: Linear.easeNone })
);
// Build scene
var scene = new ScrollMagic.Scene({
triggerElement: container,
duration: containerHeight - vpHeight / 2,
tweenChanges: true
})
.setTween(tween_journey)
.addIndicators({
name: "Draw Journey Lines#1",
colorTrigger: "brown",
colorStart: "brown",
colorEnd: "brown",
indent: 600
})
.addTo(SVGcontroller_journey);
It works perfectly fine, but as you can see, I have three individual paths inside my SVG ($journey1,$journey1_2 & $journey1_3), and the ScrollMagic scene currently only draws one of them, $journey1, because I was only able to add that one to the TimelineMax().
My simple question: How do I add the other paths so they are drawn at the same time as $journey1?
I was able to add the other paths, but they are being drawn consecutively:
// Build tween
var tween_journey = new TimelineMax()
.add(
TweenMax.to($journey1, 1, { strokeDashoffset: 0, ease: Linear.easeNone })
)
.add(
TweenMax.to($journey1_2, 1, { strokeDashoffset: 0, ease: Linear.easeNone })
);
I figured out, that I can target paths by class inside the TweenMax timeline like so:
var tween_journey = new TimelineMax();
tween_journey.to(".cls-2", 1, { strokeDashoffset: 0, ease: Linear.easeNone });
This animates/draws all paths of the given class at the same time and hence solves my problem.
Related
As an experiment I want to animate a square with the Tween class and save all the frames as png files, however a proper callback such as onUpdate doesn't seem to exist. I would like to call it on every new frame and generate a png of the current state with a function I have already created or via another solution.
The code below is based on an example I found in this repository on Github.
let i = 0;
let tween = new Konva.Tween({
node: rect,
duration: 1,
x: 140,
y: 90,
rotation: Math.PI * 2,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5,
onUpdate: () => { // does not exist
i++;
console.log(i);
render_frame(i);
},
onFinish: () => {
console.log("finished");
}
});
tween.play();
There is no onUpdate callback, but we can use the draw event of layer to save the image:
let tween = new Konva.Tween({
node: circle,
duration: 1,
x: 140,
y: 90,
rotation: Math.PI * 2,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5,
onFinish: () => {
// remove draw event
console.log('finish');
layer.off('.tween');
}
});
tween.play();
layer.on('draw.tween', () => {
// save layer to image
console.log('to image')
})
https://jsbin.com/regiduzusi/1/edit?html,js,console,output
Update:
If you need synchronous updates you can change tween time manually.
const duration = 1;
const tween = new Konva.Tween({
node: circle,
duration: duration,
x: 140,
y: 90,
rotation: Math.PI * 2,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5
});
const FPS = 25;
const tics = FPS * duration;
for (let i = 0; i < tics; i++) {
tween.seek(i / tics);
layer.draw();
console.log('to image')
}
Demo 2: https://jsbin.com/fudanozani/1/edit?html,js,console,output
I'm pretty new to GSAP with Scrollmagic, was trying to do multiple tweens in one scene but couldn't figure out how so I ended up doing 3 scenes like this.
// init controller
var controller = new ScrollMagic.Controller();
// build scene
var scene1 = new ScrollMagic.Scene({
triggerElement: "#trigger1"
})
.setTween("#animate1", 0.4, {
opacity: 1,
left: 0
})
.reverse(false)
var scene2 = new ScrollMagic.Scene({
triggerElement: "#trigger1"
})
.setTween("#animate2", 0.4, {
opacity: 1,
left: 0,
delay: .4
})
.reverse(false)
var scene3 = new ScrollMagic.Scene({
triggerElement: "#trigger1"
})
.setTween("#animate3", 0.4, {
opacity: 1,
left: 0,
delay: .8
})
.reverse(false)
//.addTo(controller);
controller.addScene([
scene1,
scene2,
scene3
]);
Is there a way to simplify this code? I am looking to add more but I feel like there is a short way to write this?
Thanks in advance!
Final code
var timeline = new TimelineMax();
var tween1 = TweenMax.to("#animate4", .5, {
opacity: 1,
top: 0
});
var tween2 = TweenMax.to("#animate5", .5, {
opacity: 1,
top: 0
});
var tween3 = TweenMax.to("#animate6", .5, {
opacity: 1,
top: 0
});
var scene = new ScrollMagic.Scene({
triggerElement: "#trigger2"
});
//.addTo(controller);
//controller.addScene([
// scene4
//]);
timeline.add(tween1).add(tween2).add(tween3);
scene.setTween(timeline)
scene.addTo(controller);
You can use TimeLine to add multiple tweens to a scene
var timeline = new TimelineMax();
var tween1 = TweenMax.from("#animate1", 1, {opacity: 1, left:0});
var tween2 = TweenMax.to("#animate2", 1, {opacity:1, left:0, delay:0.4});
timeline.add(tween1).add(tween2);
scene.addTween(timeline);
If you want to have multiple items appear one after the other, you can use stagger
TweenMax.staggerTo(".myclass", 0.5, {opacity:0, y:-100, ease:Back.easeIn}, 0.1);
I am utilizing FabricJs it is a javascript canvas library to make a small application where you can create shapes and animation between different objects.
To Run it You can follow the following steps.
Click new animation
Click Rectangle
Click Add Child button (This allows you to link objects)
Click Circle or Rectangle
If you follow the steps above you will see that you can create 2 shapes and animation between the two displayed by a small circle going back and forth.
I was wondering if it is possible to create similar animation that goes either left to right or right to left only once. I would really appreciate if someone can guide me.
Here is my FIDDLE
Here is the code for the animation
var animateBallBetweenObjects = function (obj1, obj2) {
// Add the "ball"
var circle = new fabric.Circle({
radius: 10,
fill: 'blue',
left: obj1.getCenterPoint().x,
top: obj1.getCenterPoint().y,
originX: 'center',
originY: 'middle',
selectable: false
});
canvas.add(circle);
var period = 1000;
var amplitude = 0;
var angle = 0;
var prevTime = Date.now();
var loop = function () {
// Calculate the new amplitude
var now = Date.now();
var elapsed = now - prevTime;
prevTime = now;
angle += Math.PI * (elapsed / (period * 0.5));
amplitude = 0.5 * (Math.sin(angle - (0.5 * Math.PI)) + 1);
// Set the new position
var obj1Center = obj1.getCenterPoint();
var obj2Center = obj2.getCenterPoint();
circle.setLeft(obj1Center.x + (amplitude * (obj2Center.x - obj1Center.x)));
circle.setTop(obj1Center.y + (amplitude * (obj2Center.y - obj1Center.y)));
canvas.renderAll();
requestAnimationFrame(loop);
}
// Animate as fast as possible
requestAnimationFrame(loop);
};
You can try yo use fabric built in animation:
http://jsfiddle.net/asturur/s6ju2858/2/
I did not replicate your loop function, that requires extra work, but fabricJS gives you the ability to define an animation for a property ( in our case left and top ) with a start and end value.
var animateBallBetweenObjects = function (obj1, obj2) {
var completedLeft, completedTop;
// obj1, obj2 are predefined.
var c1 = obj1.getCenterPoint();
var c2 = obj2.getCenterPoint();
var circle = new fabric.Circle({
radius: 10,
fill: 'blue',
left: c1.x,
top: c1.y,
originX: 'center',
originY: 'center',
selectable: false
});
canvas.add(circle);
circle.animate('left', c2.x, {
onChange: canvas.renderAll.bind(canvas),
startValue: c1.x,
endValue: c2.x,
onComplete: function() {
completedLeft = true;
completedTop && canvas.remove(circle);
},
easing: fabric.util.ease['easeInQuad']
}).animate('top', c2.y, {
startValue: c1.y,
endValue: c2.y,
onChange: canvas.renderAll.bind(canvas),
onComplete: function() {
completedTop = true;
completedLeft && canvas.remove(circle);
},
easing: fabric.util.ease['easeInQuad']
});
};
Updated fiddle with ball removal at the end of animation.
I've been staring at this for the past hour and cannot figure it out.
I'm trying to use KineticJS to rotate a shape 45 degrees when it is clicked. I found http://jsfiddle.net/JUu2Q/6/ from a previous question on Stack Overflow which does basically what I want. When I apply this to my code (and change 'layer' to 'stage'), I get the following error: Cannot read property 'rotation' of undefined:
layer.on('click tap', function(evt) {
evt.targetNode.tween = new Kinetic.Tween({
node: evt.targetNode,
duration: 0.3,
rotationDeg: evt.targetNode.rotation()+45,
easing: Kinetic.Easings.EaseOut
});
evt.targetNode.tween.play();
});
I'm sure I'm doing something wrong but I just can't figure it out.
My code can be found at http://jsfiddle.net/0h55fdzL/
I've only be using KineticJS for a few hours so I apologize if this is stupid question
Thanks for your help!
1 Create closure for x variable.
2 Use target instead of targetNode
var x = -50;
var y = -50;
var stage = new Kinetic.Stage({
container: 'container',
width: 1200,
height: 1200,
});
for (i=0; i<3; i++){
x = x + 50;
y = y + 50;
var layer = [];
layer[i] = new Kinetic.Layer();
var hex = [];
(function(x){
hex[i] = new Kinetic.Shape({
sceneFunc: function(context) {
context.beginPath();
context.moveTo(x+25, 0);
context.lineTo(x+40, 10);
context.lineTo(x+40, 25);
context.lineTo(x+25, 35);
context.lineTo(x+10, 25);
context.lineTo(x+10, 10);
context.closePath();
// KineticJS specific context method
context.fillStrokeShape(this);
},
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true,
rotation:0
});
})(x);
// add the triangle shape to the layer
layer[i].add(hex[i]);
// add the layer to the stage
stage.add(layer[i]);
}
stage.on('click tap', function(evt) {
evt.target.tween = new Kinetic.Tween({
node: evt.target,
duration: 0.3,
rotationDeg: evt.target.rotation()+45,
easing: Kinetic.Easings.EaseOut
});
evt.target.tween.play();
});
http://jsfiddle.net/0h55fdzL/1/
I would like to animate moving a draggable shape to another position after it has been dragged in KineticJS. I would like to animate the movement of the shape over a period of time (for example, over 1 second). For example, I create a draggable shape and save its initial xy coordinates. I register a "dragend" event on this shape. Then, I drag the shape to a new position. When I release the drag, the dragend event is called. In that event function, I want to animate/ease the shape back to its original position. See my JSFiddle for a complete example: DragSample.
(function () {
//create variables at global scope
var layer;
var stage;
var triangle;
var triangleLastX = 190;
var triangleLastY = 120;
var tween;
function initTween() {
tween = new Kinetic.Tween({
node: triangle,
duration: 1,
easing: Kinetic.Easings.EaseInOut,
x: 400,
y: 200,
});
}
this.init = function () {
layer = new Kinetic.Layer();
stage = new Kinetic.Stage({
container: 'container',
width: 800,
height: 600
});
triangle = new Kinetic.RegularPolygon({
x: 190,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
triangle.on('dragstart', function () {
triangleLastX = triangle.attrs.x;
triangleLastY = triangle.attrs.y;
});
triangle.on('dragend', function () {
tween.play();
stage.draw();
});
layer.add(triangle);
stage.add(layer);
initTween ();
}
window.onload = init();
})();
I have tried doing this several ways. The last way I attempted to do this was using Kinetic's Tween(), however, when I play this Tween from the dragend event handler function, it moves the shape back to its original position immediately (i.e. the position when the drag started), then applies the Tween.
Is there any way to achieve animating the movement of a draggable shape to its original position (or any other position for that matter) in dragend using KineticJS?
As I mentioned in my comment above, I have found the solution and that is to simply call my initTween() function in the dragend event handler. Following is the updated source for DragSample
(function () {
//create variables at global scope
var layer;
var stage;
var triangle;
var triangleLastX = 190;
var triangleLastY = 120;
var tween;
function initTween() {
tween = new Kinetic.Tween({
node: triangle,
duration: 1,
easing: Kinetic.Easings.EaseInOut,
x: triangleLastX,
y: triangleLastY,
});
}
this.init = function () {
layer = new Kinetic.Layer();
stage = new Kinetic.Stage({
container: 'container',
width: 800,
height: 600
});
triangle = new Kinetic.RegularPolygon({
x: 190,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
triangle.on('dragstart', function () {
});
triangle.on('dragend', function () {
initTween();
tween.play();
stage.draw();
});
layer.add(triangle);
stage.add(layer);
}
window.onload = init();
})();