Fabric.js animate sprites and text field - javascript

about month ago i asked how to quee animatations for objects in Fabric.js to which i got response from #nickvans. This is the code. Based on this I changed the code and created with HTML5 drag and drop, aplication which allows you to give to each object on canvas its own set of different commands. Basicly creating moving scene. Mine 1st question is :if it is possible to also use sprites. So instead of the triangle in the exmaple below it would be animated sprite that would also change its position. And 2nd question is it possible to somehow add text field that would follow the object during its movement? Something like those comics bubles.
Thanks in advance for any tips
function startAnimationQueue(animationQueue){
// queues up the animations for the shape
var runningDuration = 0; // variable that adds up the animationQueue durations
for (var i=0; i<animationQueue.length; i++){
var animationDefinition = animationQueue[i];
// Create a closure around the animationDefiniton so that each setTimeout gets sequential animationDefinition inputs
var fn = (function(animationDefinition){
return function(){
triangle.animate('left', animationDefinition.left, {duration: animationDefinition.duration, onChange:canvas.renderAll.bind(canvas)})
triangle.animate('top', animationDefinition.top, {duration: animationDefinition.duration, onChange:canvas.renderAll.bind(canvas)})
// Note: you can animate additional attributes here if you want, just add additional attributes to the objects in
// the animationQueue list. You could also have one of those inputs be the object to be animated in case you want
// to animate multiple objects using the same queue.
};
})
// Create the timeout that will apply the transformations sequentially
// If you want you could set the window.setTimeout to a variable that you could destroy if you need
// to interrupt the animation queue after you create it (but before it finishes)
window.setTimeout(fn(animationDefinition), runningDuration);
// set the next setTimeout duration to be .duration later than the previous one
// this makes the second animation fire after the first one completes
runningDuration += animationDefinition.duration;
}
}
document.onreadystatechange = function () {
if (document.readyState == "complete") {
// I put the canvas init stuff in here because of (I think) a failed race condition or something that caused
// your original method to fail in Chrome
window.canvas = new fabric.Canvas('scene');
window.triangle = new fabric.Triangle({
width: 30
, height: 30
, fill: 'red'
, left: 30
, top: 0
});
window.canvas.add(window.triangle);
window.canvas.renderAll();
// Create a list of animations to apply
var animationQueue = [
{"left": "+=0", "top": "+=100", "duration": 1000},
{"left": "+=55", "top": "+=0", "duration": 2000}
]
// Apply the animations in sequence using window.setTimeout
startAnimationQueue(animationQueue);
}
}
and HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
</head>
<body>
<canvas id="scene" width="400" height="400" />
</body>
</html>

Related

fabricjs - creating endless animation loop without lags

I am trying to create an endless animation loop of rotation with FabricJS. It works like this: the rotate function gets animation details (object to animate, duration of one animation loop, starting angle of rotation) as parameters, and invokes inner function rotateInner to launch the animation itself. rotateInner rotates given object using fabric.util.animate with linear easing function (to make rotation speed constant) to run one animation loop and to invoke itself after the loop ends, which results in an infinite animation loop.
The problem is that rotateInner lags every time between animation loops when it invokes itself through fabric.util.animate.
For example, I use animation loop with duration = 1 second and rotation angle = 180 degrees. In that case, animation makes lag every 1 second after rotating by 180 degrees.
I've found a solution to make lag occur N times less. Instead of rotating by 180 degrees for 1 second, I rotate object by 180*N degrees for N seconds. If I pick enough big N, the user will meet lag potentially never. But only potentially.
What are reasons of lags and is it possible to remove lags between animation loops completely? Or, perhaps, I went wrong way, and I should use something completely different to create endless animation loop?
Here's example which demonstrates lag (on jsfiddle). (The rotate function has additional argument - multiplier. It's N I've just wrote about above) Left rectangle rotates 180 degrees per 800 milliseconds, and the right rectangle rotates 180*10 degrees per 800*10 milliseconds, and thus the right one rotates quicker than the left one.
And, if you don't want to use jsfiddle or if example became inaccessible for some reason, here's the example itself in code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.min.js"></script>
<style>canvas {border: 5px dotted black;}</style>
<title>FabricJS animation loop lag</title>
</head>
<body>
<canvas id="mycanvas" width="700" height="300"></canvas>
<script>
var canvas = new fabric.StaticCanvas('mycanvas');
var rectCreationOptions = {
left: canvas.getWidth()/4,
top: canvas.getHeight()/2,
fill: '#0bb',
width: canvas.getWidth()/5,
height: canvas.getHeight()/5,
originX: 'center',
originY: 'center'
};
// creating left rectangle:
var rect1 = new fabric.Rect(rectCreationOptions);
rectCreationOptions.left *= 3;
// creating right rectangle:
var rect2 = new fabric.Rect(rectCreationOptions);
canvas.add(rect1);
canvas.add(rect2);
function rotate(animatedObject, duration = 1000, multiplier = 1, startAngle = 0) {
duration *= multiplier;
var addAngle = 180 * multiplier;
(function rotateInner(startAngle) {
fabric.util.animate({
startValue: startAngle,
endValue: startAngle+addAngle,
duration: duration,
// linear easing function:
easing: function(t, b, c, d) { return c*t/d + b; },
onChange: function (value) {
animatedObject.angle = value;
canvas.renderAll();
},
onComplete: function() {
rotateInner(startAngle+addAngle);
}
});
})(startAngle);
}
rotate(rect1, 800, 1); // lags every 800 ms
rotate(rect2, 800, 10); // lags every 800*10 ms
</script>
</body>
</html>
I will be thankful for help.
Please, don't throw rotten tomatoes at me if I made tiny mistake. This is my first question on stackoverflow. :)
Best wishes everyone.

Snap.svg: How to use Set.bind(...)?

I can't figure out how to use the Set.bind(...) feature for Snap.svg.
Below is an example with three(3) elements in a set: 2 circles and an ellipse.
I'd like to access and change some attr's in the various elements, using bind.
A few examples of bind would be appreciated.
(Actually, at this moment, I can't see any advantage in using the Set object, rather than an array. Are there any features of the Set that can't be handled just as well with an array?)
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="http://svgDiscovery.com/_SNP/snap.svg-min.js"></script>
</head>
<body>
<svg id=mySVG width=400 height=200></svg>
<script>
var SNPsvg = Snap("#mySVG");
var circle1 = SNPsvg.circle(150,100,50).attr({fill: 'red' });
var circle2 = SNPsvg.circle(250,100,50).attr({fill: 'blue' });
var ellipse = SNPsvg.ellipse(200,100,50,20).attr({fill: 'green' });
var mySet= Snap.set(circle1,circle2,ellipse)
setTimeout(function()
{
//mySet.bind(...)
},1000)
</script>
</body>
</html>
The main reason to use a set, is that you can act on every element with a single command. For example...
mySet.animate({ transform: 's2' },1000)
jsfiddle
Which will then act on every single element with that animation.
Why would you use Set.bind ? I must admit, I've never used it, nor seen the purpose yet, but I assume there is one :).
So to the actual question, how is it used. I guess you do..
mySet.bind('x', circle1, 'cx' )
mySet.attr({ 'x': '200' })
jsfiddle
If you set attribute x, it will set attribute cx on circle1 in this case.
Or
mySet.bind('x', function( val ) { console.log( val, ' is passed' )} )
mySet.attr({ 'x': '200' })
jsfiddle
As to why though, I'm not sure :), I can see the advantage of using a set object, but not particularly with set.bind(), especially as it doesn't seem to pass 'this' to the function. I was wondering if it was something like if you set x on a set of circles and rects, you could adjust cx OR x somehow, but I don't see how that is done in any simple way, if the object that's being acted on isn't passed somehow.
I'd normally be more inclined to do something like...
mySet.forEach( function( el ) { el.attr({ fill: 'blue' }) } );

Spine multiple animations

So I have 2, or more skeletons for an animation (so, 2 or more json files). I want them to play at the same time and 10 seconds after, to play another animation.
Problem is that there is only one animation playing, and the second isn't displayed.
The way I'm doing it is the following:
<canvas id="animationCanvas" width="240" height="240"></canvas>
<script>
var first = true,
rendererFirst = new spine.SkeletonRenderer('http://someurl.com/images/'),
spineAFirst = /* My JSON Code */,
parsedFirst = JSON.parse(spineAFirst);
rendererFirst.scale = 0.2;
rendererFirst.load(spineAFirst);
rendererFirst.state.data.defaultMix = 1.0;
for (var i in parsedFirst.animations) {
if (first) {
first = false;
rendererFirst.state.setAnimationByName(0, i, true);
} else {
rendererFirst.state.addAnimationByName(0, i, true, 10);
}
}
rendererFirst.skeleton.x = 120;
rendererFirst.skeleton.y = 120;
rendererFirst.animate('animationCanvas');
</script>
And, of course, I'm doing it twice (or more). I tried as well with a single SkeletonRenderer, just loading (and setting or adding) animations as many times as I need, and it didn't worked.
It seems that the renderer is cleaning the canvas each time it is called, the better way to achieve this seems to create a canvas for each animation, and bind a renderer to each one.

Animate color change of shape for certain number of frames EaselJs

I am using EaselJS and want to create a flashing color rectangle that will flash a certain number of times when a button is pressed, displaying random colors from an array. It should stop after 10 color flashes and then I want to extract the final color.
So far the relevant code I have is:
var colorArray = ["#FE7B62","#CB2DD3","#F1FD66","#004CE8","#FFD068", "#02A97E"];
square = new createjs.Shape();
square.graphics.beginFill("#000").drawRoundRect(850, 50, 100, 100, 20);
function pushButton(event) {
square.graphics.inject(animateColor);
}
function animateColor(event) {
this.fillStyle = colorArray[parseInt(Math.random()*6)];
}
This code successfully triggers the flashing of colors from my color array, but I am not sure what the best method of running the animation for a limited number of frames is. I tried pausing the ticker and restarting it on the onclick of the button but that failed. I also tried using a for loop in the pushButton function but that caused a "too much recursion error" in the browser. Here is my full file link https://github.com/RMehta95/sherman-land/blob/master/main.js.
I would suggest creating an event to trigger your action (xamountOfFrames). Here is a link to some information that might help
http://www.w3schools.com/tags/ref_eventattributes.asp
I ended up not using the inject function and instead redrawing the shape each time, creating a couple counter and timer variables to ensure that it looped through the proper amount of times.
function pushButton() {
if (timer === false) {
animate = setInterval(animateColor, 200);
timer = true;
}
}
function animateColor() {
square.graphics.clear();
displayColor = colorArray[Math.floor(Math.random()*colorArray.length)];
square.graphics.beginFill(displayColor).drawRoundRect(850, 50, 100, 100, 20);
counter++;
if (counter===15) {
clearInterval(animate);
counter=0;
movePlayer(displayColor);
timer = false;
return;
}
}

Multiple responsive video.js players

I'm using this solution, http://daverupert.com/2012/05/making-video-js-fluid-for-rwd/, to make the videojs player fluid. My problem is when I have multiple videos (each with a unique id), I'm not sure how to make this work.
Here is my dev site I have 3 videos on, http://tweedee.e-mediaresources.info/
Here is the code I have for the player (from Dave Rupert's solution above):
<script type="text/javascript">
// Once the video is ready
_V_('#my_video_1').ready(function(){
var myPlayer = this; // Store the video object
var aspectRatio = 9/16; // Make up an aspect ratio
function resizeVideoJS(){
// Get the parent element's actual width
var width = document.getElementById(myPlayer.id).parentElement.offsetWidth;
// Set width to fill parent element, Set height
myPlayer.width(width).height( width * aspectRatio );
}
resizeVideoJS(); // Initialize the function
window.onresize = resizeVideoJS; // Call the function on resize
});
</script>
This code works fine for one video, but how do I do multiple ids??? As you can see in my dev site, I just replicated the script above three times (each with a different id) and that only causes the last video to be fluid.
you overwrite window.onresize() each time, so only the last one is used.
replace
window.onresize = resizeVideoJS
with :
window.addEventListener("resize", resizeVideoJS, false); // all browsers except IE before version 9
The following works. It does involve a bit of repetition, which I think you might be able to avoid if you used something like jQuery's deferred object to wait until the ready event is fired for all of the video players, but it's a lot neater than duplicating the resize method as you're currently doing:
<script type="text/javascript">
var players = ['my_video_1', 'my_video_2', 'my_video_3'];
var aspectRatio = 9/16;
// Catch each of the player's ready events and resize them individually
// jQuery deferred might be a neater way to wait for ready on all components to load and avoid a bit of repetition
for (var i = 0; i < players.length; i ++) {
_V_('#' + players[i]).ready(function() {
resizeVideoJS(this);
});
}
// Loop through all the players and resize them
function resizeVideos() {
for (var i = 0; i < players.length; i ++) {
var player = _V_('#' + players[i]);
resizeVideoJS(player);
}
}
// Resize a single player
function resizeVideoJS(player){
// Get the parent element's actual width
var width = document.getElementById(player.id).parentElement.offsetWidth;
// Set width to fill parent element, Set height
player.width(width).height( width * aspectRatio );
}
window.onresize = resizeVideos;
</script>
// jQuery deferred might be a neater way to wait for ready
// on all components to load and avoid a bit of repetition
for (var i = 0; i < players.length; i ++) {
_V_('#' + players[i]).ready(function() {
resizeVideoJS(this);
});
}
// Loop through all the players and resize them
function resizeVideos() {
for (var i = 0; i < players.length; i ++) {
var player = _V_('#' + players[i]);
resizeVideoJS(player);
}
}
// Resize a single player
function resizeVideoJS(player){
// Get the parent element's actual width
var width = document.getElementById(player.id).parentElement.offsetWidth;
// Set width to fill parent element, Set height
player.width(width).height( width * aspectRatio );
}
window.onresize = resizeVideos;
I have slightly modified net.uk.sweet's very helpful answer above into a working script which deals with multiple video players using video js - which are also responsive. You can find my article (which also shows an example) here = http://www.andy-howard.com/videojsmultipleresponsivevideos/index.html
This also includes a provided callback function if you require it.
you can use css rather than javascript :
.wrapper {
width: 100%;
}
.video-js {
padding-top: 55.25%;
}
<div class="wrapper">
<video id="video-player" width="auto" height="auto" class="video-js vjs-default-skin vjs-big-play-centered" data-setup="{}">
</video>
</div>

Categories

Resources