Jump to content
Search Community

Animating Canvas fillStyle with GSAP

cgorton test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hello all, I am working on learning canvas. I mostly work with SVG and GSAP so getting the hang of canvas has been a little rough :)
I am having trouble animating something I thought would be simple. I need to animate the fillStyle of a couple of squares I created with Canvas. It needs to be a stagger animation. I originally did something similar here with just DOM elements and GSAP

See the Pen 63d15b9bb37e19c8cd4ee3191bb1a42d?editors=0010 by cgorton (@cgorton) on CodePen


From looking at the forum I see I should use the colorProps plugin to animate the fillStyle. I was able to get it working on one square by forking an example I found here

See the Pen f40d8c929d7dc6f03ea335ce0791f0bb?editors=0110 by cgorton (@cgorton) on CodePen

. What I can't seem to understand is how I would do something like this as a stagger animation in Canvas instead of just onUpdate.
This is the canvas squares I have created.  ( if anyone has a better suggestion for creating the squares feel free to let me know :) )

See the Pen 73443ae63119ea6153d73649c54dedcc?editors=0110 by cgorton (@cgorton) on CodePen


Any help on where to go from here would be greatly appreciated.

Link to comment
Share on other sites

GSAP works with objects, so in order to animate multiple square you need to use objects. But better option would be to use prototype so you can have more flexibility, each object will have it's own draw method and other methods as well if you need. I am passing context to draw method so just in case you want to reuse your code or it gets too complex, you can easily separate your code to organize it better or create your own simple library to draw common shapes.

 

Once you do that you can animate each square independently just like you animate DOM elements. Also, please avoid posting private pens as we can't fork them.

 

See the Pen JajWBM by Sahil89 (@Sahil89) on CodePen

 

In case you haven't already, I would recommend getting this book: https://www.apress.com/in/book/9781430236658

  • Like 7
Link to comment
Share on other sites

Hi @cgorton :)

 

After chatting with you via Twitter it's good to see you posting in the forum. I tweeted that book at you 10 days ago and you still haven't ordered it? :D

 

@Sahil gave you some good info there. He's a master of the canvas. Via Twitter I recall Jack mentioning Pixi.js to you too. If that's of interest to you, here's a quick example of creating some rectangles with Pixi and pushing them into an array. You can then stagger all the properties you like. 

 

See the Pen eLYWRV by PointC (@PointC) on CodePen

Pixi is fairly easy to use and GreenSock has a terrific Pixi plugin:

https://greensock.com/?product-plugin=js-pixiplugin

 

Lots of cool stuff happening with Pixi right now with the release of version 5.

http://www.pixijs.com/

 

Hopefully that gives you some ideas. Happy tweening.

:)

 

 

 

  • Like 6
Link to comment
Share on other sites

Thanks for the Pixi demo @PointC I'd love to try it out and might learn it on my own time but unfortunately I have been tasked with not adding any more libraries to the app I am animating for. I originally did all of the animations in SVG and GSAP (there are a lot of animations I have already completed) but my manager wants me to try to do the same animations in canvas to see if the game will be faster ¯\_(ツ)_/¯

@Sahil sorry about the private pens. I'll try to remember that for next time :)

  • Like 3
Link to comment
Share on other sites

Well you will certainly get better performance compared to DOM elements because you would be writing minimal code that draws directly and skips any DOM API calls. If your game has a lot of effects or particles, PIXI JS might give you better performance because WebGL takes advantage of hardware accelaration. Otherwise canvas is your best option.

 

 

If you really want to squeeze out performance then you can avoid rendering text on each frame and render text to offscreen canvas once to reuse it as image for far better performance because rendering text on canvas is expensive. In fact, you can do that for any complex shapes to avoid redrawing them and use sprites wherever possible. Check out following article,

 

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas

 

And our discussion about canvas performance,

 

 

  • Like 5
Link to comment
Share on other sites

Hi @cgorton  

 

How are you making out? I didn't recognize you without a avatar.

 

Learning the basics of canvas isn't that hard. Instead of declaring what you want the browser to display with CSS and markup, you write instructions to draw stuff. It works a lot like Logo programming (turtle graphics), which is a programming language for children. You tell the turtle where to go with very simple instructions/commands. With canvas, the context is the turtle, and that's the only thing you can really interface with.

https://turtleacademy.com/lessons/en

 

 

Having only 1 object to work with can be quite limiting, so the solution it to create your objects to work on. If you had some CSS and an SVG...

 

<svg>
  <style>
    #rect {
      fill: #2196F3;
      fill-opacity: 0.5;
      stroke: #111;
      stroke-width: 2;
    }
  </style>  
  <rect id="rect" x="20" y="100" width="200" height="150" />
</svg>

 

 

... it could be represented as a simple object like this.

 

var myRect = {
  x: 20,
  y: 100,
  width: 200,
  height: 150,
  fill: "#2196F3",
  fillOpacity: 0.5,
  stroke: "#111",
  strokeWidth: 2
};

 

 

 

Now you can tween that object as if it were an a real element. If it's a number, GSAP can animate it.

 

See the Pen GXgMra by osublake (@osublake) on CodePen

 

 

 

The properties you use and how you name them is entirely up to you. The context has no concept of your objects, so do what makes the most sense.

 

You can also make optimizations by reusing objects. Notice how the line has 3 different strokes applied to it. With SVG, that would require 3 different elements. The only way to end a path is to call context.beginPath(), so you can keep applying different styles to the same path.

 

See the Pen yxyMWO by osublake (@osublake) on CodePen

 

 

 

If you haven't already, check out these threads. 

 

 

 

 

 

 

CSS tricks. It's a little advanced, but it shows some nice rendering techniques.

https://css-tricks.com/using-gsap-animate-game-ui-canvas/

 

I would also recommend going through every property and method on MDN. It's very helpful, and most of the pages have demos.

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D

 

MDN also has some pretty good tutorials. Some of the stuff is dated, but it's fine if you're just getting started.

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial

 

 

 

 

 

 

  • Like 6
  • Thanks 2
Link to comment
Share on other sites

Hey @OSUblake. Thanks for the demos and resources. Last week I went through all of these tutorials https://www.kirupa.com/canvas/

Today I have gone through almost half of the book that Sahil and Craig suggested. I'll look at the resources you suggested as well. One thing I am stuck on (and this is probably mostly due to the fact that I am not very good with JS) is how to reuse the objects you create. So for example the myRect object you put in the demo. If I wanted to add multiple squares would I use
new myRect(); each time I wanted to make a new square?

Link to comment
Share on other sites

44 minutes ago, cgorton said:

So for example the myRect object you put in the demo. If I wanted to add multiple squares would I use
new myRect(); each time I wanted to make a new square?

 

Good question. The myRect object I created cannot be reused like that. You can make a function that returns a simple object like that i.e. a factory function.

 

To use "new" you have to call a constructor function or a class, which is what I used in my second demo. They both do the same thing, but a class is a little cleaner, and newer, so you have to use babel if you want to support IE.

 

The book that you bought is old, so you'll mostly see constructor functions being used, like this ball class in chapter 6.

https://github.com/lamberta/html5-animation/blob/master/examples/ch06/classes/ball.js

 

 

Here's a demo where I use all 3 methods, classes, constructor functions, and factory functions.

 

See the Pen xabPyx by osublake (@osublake) on CodePen

 

 

  • Like 5
Link to comment
Share on other sites

I've been following this thread in the background. So impressed by all the thoughtful and detailed answers the moderators have provided. 

However, what's even better is seeing the initiative @cgorton is taking to to study, practice and learn! 

 

So often people drop in here when they hit a snag just wanting us to provide a quick "copy and paste" solution that will hold them over until they hit the next problem... only to return again.

 

Cgorton, I know many people appreciate the help the mods give them, but its extra special when we see the people we are trying to help APPLY what they've learned, take it one step further and continue to learn new things. Thanks for taking the time to share what you've learned along the way. Its really cool to see the progress and I know it means a lot to the mods to know their time is appreciated and being put to good use.

 

 

 

 

  • Like 5
Link to comment
Share on other sites

Be careful @cgorton. This is how it starts. You post a few questions. You help a few other members. You start spending more time on the forum. Before you know it, you're suffering from GreenSock Fever. The only known cure is a promotion to Moderator, but then you spend even more time here. ?

  • Like 3
  • Haha 2
Link to comment
Share on other sites

If anyone has some time and feels up to it I could use a little more help. I have been trying to figure out for the past week how to turn a canvas emitter like this into a reusable function that I can add to a master timeline.

See the Pen eLZLab by cgorton (@cgorton) on CodePen


I've done something similar was divs and svg but I can't seem to figure out how to add this one to a timeline.

I'm wanting to add it to the end of the master timeline here when the last square turns red after you hit the bid button.

See the Pen JaddaB by cgorton (@cgorton) on CodePen


I was able to turn the gridColor animation into a reusable timeline function and added it to my master timeline but couldn't figure out this one ? Any help in the right direction is appreciated.

Link to comment
Share on other sites

Howdy @cgorton. I didn't have time to digest all 300+ lines of code but I had a few questions...

  1. Why do you want an emitter animation like that in a timeline? Typically timelines are for animations that you need to have random access to (like tl.seek(20)) or scrub or reverse or something. Emitters by their very nature would endlessly generate animations, so there's not really any "end" time and scrubbing is typically an odd concept for this type of thing. So what's your goal here in terms of getting it into a master timeline?
  2. Is there any way you could simplify your question (or demo)? 

Oh, and just a suggestion - I always get scared when I start building functions that accept more than 3-ish parameters because it can get really confusing to read and maintain. You end up with function calls like:

all('.box', 0.5, 1, 1.1, Power4.easeOut, 0, 0, 0, 'center center', true, true, 5)

And then it's like "wait, what's the 8th parameter again? Where do I define the 'x'?" And it's more difficult to just leverage defaults too, like if you want to use all the default values except the 9th parameter...you're stuck defining 1-8 as well. I find it more comfortable/efficient to use config objects in cases like this:

all({
  item: '.box',
  time: 0.5,
  opacity: 1,
  scale: 1.1,
  ease: Power4.easeOut
});

 

Just food for thought. :) 

  • Like 3
Link to comment
Share on other sites

Emitters usually have a way to start and stop them, and you can usually position them. 

 

Here's a quicky modification of your first demo. Probably not the best way to do it, but it demonstrates those concepts. The emitter will follow your mouse, and you can toggle it on and off. Timelines can call functions, so you could start your emitter that way.

 

See the Pen mGEOop by osublake (@osublake) on CodePen

 

 

 

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

Sorry for the overly large demo :) . This is for a "game" basically I wanted to be able to add it to a timeline because I will be using it all over the place, sometimes multiple emitters with different images at the same time. As an example ( I have to keep this set as a private pen, sorry!)  this is one of the prototypes for one of the game sequences I originally did with SVG, clip-paths for the emitters, and GSAP. I am now trying to do a lot of the animations in Canvas along with SVG for some of the more complicated ones but the emitters especially they want to be done with canvas now.

@OSUblake Thank you for the demo.  I'm used to seeing the emitters just continuously emitting so seeing how to toggle it on and off definitely helps. 
 

Link to comment
Share on other sites

  • 3 years later...
On 8/28/2018 at 11:05 AM, GreenSock said:

 

Oh, and just a suggestion - I always get scared when I start building functions that accept more than 3-ish parameters because it can get really confusing to read and maintain. You end up with function calls like:

all('.box', 0.5, 1, 1.1, Power4.easeOut, 0, 0, 0, 'center center', true, true, 5)

And then it's like "wait, what's the 8th parameter again? Where do I define the 'x'?" And it's more difficult to just leverage defaults too, like if you want to use all the default values except the 9th parameter...you're stuck defining 1-8 as well. I find it more comfortable/efficient to use config objects in cases like this:

all({
  item: '.box',
  time: 0.5,
  opacity: 1,
  scale: 1.1,
  ease: Power4.easeOut
});

 

Just food for thought. :) 

 

I don't know why I didn't read this sooner, but I was in the exact situation recently. It's not only inefficient but also leaves a lot of room for errors, so thank you for the super helpful tip.

  • Like 3
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...