Jump to content


Animating alternating circes on canvas with scrolltrigger

Moderator Tag

Recommended Posts

Hi all, thanks in advance for your help.


I've never really used canvas before but i got pretty far with ChatGPT... 


However ChatGPT was not good at getting these to animate with gsap.

How can I set it up so that these circles animate (i'm thinking a staggered fade in, the first row from the left and the second row from the right) when the canvas element scrolls into view using ScrollTrigger



var HW = {};

var $$ = function (selector, parent) {
    return Array.prototype.slice.call((parent ? parent : document).querySelectorAll(selector));

var loop = function (arr, callback, method) {
    method = method || 'forEach';
    return Array.prototype[method].call(arr, callback);

HW.circMotif = function(canvas) {            
            const ctx = canvas.getContext('2d');
            const circleSize = 80; // Size of the circles
            const padding = 3; // Padding between circle and square
            const canvasWidth = window.innerWidth + 6;
            const numRows = 2; // Number of rows
            // Calculate the square size based on the available width and padding
            const squareSize = (canvasWidth - 2 * padding) / Math.ceil(canvasWidth / (circleSize + 2 * padding));
            // Calculate the number of circles that can fit in a row
            const numCirclesPerRow = Math.floor(canvasWidth / squareSize);
            // Calculate the total width of the circles and padding
            const totalWidth = numCirclesPerRow * squareSize;
            // Calculate the left margin to center the circles
            const marginLeft = (canvasWidth - totalWidth) / 2;
            // Calculate the canvas height based on the number of rows
            const canvasHeight = numRows * squareSize;
            // Set the canvas dimensions
            canvas.width = canvasWidth;
            canvas.height = canvasHeight;
            // Clear the canvas
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            // Loop through the rows and columns to draw the circles
            for (let row = 0; row < numRows; row++) {
                for (let col = 0; col < numCirclesPerRow; col++) {
                    // Calculate the x and y coordinates for the current circle
                    const x = marginLeft + col * squareSize;
                    const y = row * squareSize;
                    // Determine the starting color combination for each row
                    const isBlueCircle = ((row + col) % 2 === 0);
                    // Set the fill color and draw the square
                    ctx.fillStyle = (isBlueCircle) ? 'blue' : 'white';
                    ctx.fillRect(x, y, squareSize, squareSize);
                    // Draw the circle
                    ctx.arc(x + squareSize / 2, y + squareSize / 2, circleSize / 2, 0, 2 * Math.PI);
                    ctx.fillStyle = (isBlueCircle) ? 'white' : 'blue';
        HW.doCircMotif = function() {
            var $circleZazz = $$('.circle-zazz');
            loop($circleZazz, function(el){



See the Pen GRYLjeE by handiworknyc (@handiworknyc) on CodePen

Link to comment
Share on other sites


This thread is a good place to start


Link to comment
Share on other sites

Thanks @Cassie

I'm very lost with this as the example shown has a single square where as my example is drawing rows and columns of circles.


Any further hints you can give? thanks

  • Like 1
Link to comment
Share on other sites

That thread is an absolute gold mine of information. You should be able to extrapolate what you need if you go through the demos and info!

Largely you just need to know that canvas needs to be redrawn every tick, that's why you add the function that draws the elements to the GSAP ticker. 

After that, what you animate and draw is up to you. I've simplified your code down so that there's an obvious draw function. (That's what gets called every tick) Then you can just call that.

You can either call it from GSAP's ticker, or you could call it from an onUpdate callback in a tween, timeline or ScrollTrigger.

See the Pen xxyezNd?editors=0010 by GreenSock (@GreenSock) on CodePen

If you want to stagger you'll need to have the concept of individual circles with individual size values for GSAP to target, which you don't really have right now, you just have one 'size' value that they're all using. So you'll need to create more objects to work with, just like you'd have with DOM or SVG. Possibly using a constructor, that would likely be the easiest way. Then you can use GSAP to stagger them.


Here's a demo

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


And a thread

  • Like 2
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.