Drawing and Animating Jelly Shapes with Canvas Part 2

Luis Manuel
👁️ 3,385 views
💬 comments

In the last tutorial we saw how to draw and animate jelly shapes. This tutorial will be a continuation, and we will see how to create an original slider full of jelly effects. Here is a gif:

We recommend to read the previous tutorial, for understanding the basics to get jelly shapes directly in the browser.

Table of Contents

    As we announced in the last post, while we build the slider, we will learn how to:

    • Draw more jelly shapes, and text!
    • Use images inside the shapes, not only solid colors.
    • Animate the shapes to show or hide them smoothly.
    • Morph from a jelly shape to another.
    • Make the entire slider responsive.

    Let's begin!

    Basic HTML and CSS

    The HTML and CSS code for our slider is pretty simple. The markup will be just a container element, and a canvas to draw all the jelly things:

    <div class="jelly-container">
        <canvas class="jelly-canvas"></canvas>
    </div>

    And the CSS (SCSS) will be the following:

    /* General styles */
    
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    
    body {
      background-color: #106CD9;
      overflow: hidden;
    }
    
    /* Jelly slider */
    
    $width: 1300px;
    $height: 600px;
    
    // Setting dimensions
    .jelly-container, .jelly-canvas {
      width: $width;
      height: $height;
    }
    
    // Centering
    .jelly-container {
      position: relative;
      left: 50%;
      top: 50%;
      margin-left: - $width / 2;
      margin-top: - $height / 2;
    }

    As you can see, the markup and styles required are very straightforward. All the hard work will be in the JavaScript. But first, we need to draw our paths in a vector editor, let's see how to do it!

    Drawing SVG paths

    We have used a vector editor to draw the SVG paths we need. We have exported the final result to a SVG file, getting something like this:

    As you can see, our paths are overlapping, and there's nothing wrong with that, because only one text and one arrow will be displayed at a time, while the others remain hidden. In addition, the circles around the big shape have been drawn to guide us when positioning the elements on the canvas.

    Note also that each letter (and any other shape) must consist of a single closed path. We can use whatever font we want (turn to paths), but we need to make sure we are respecting this condition.

    Finally, we have setted id attributes to every shape in the vector editor, so we can select them later from JavaScript.

    Making the shapes jelly with Canvas

    As we can see in the first part of this tutorial, to initialize the jelly shapes we only need this line of code:

    var jelly = new Jelly('.jelly-canvas', options);

    The hard work here is to populate the options to draw the paths we want with the effects we want.

    As we will be working with many objects (the options for each shape), we will use a function to extend and object as we need. You can find it in the JavaScript file, and we will use it a lot. So, let's see how we are setting the basic options for each type of shape:

    /* Setup options */
    
    // Base options for every shape
    var baseOptions = {
        svg: 'jelly.svg',
        pointsNumber: 16
    };
    
    // Options for each circle
    var optionsCircle = extend({}, baseOptions, {
        paths: '#jelly-circle',
        maxDistance: 40,
        mouseIncidence: 25,
        maxIncidence: 25
    });
    
    // Options for each image (big shapes)
    var optionsImage = extend({}, baseOptions, {
        paths: '#jelly-image',
        maxDistance: 150,
        mouseIncidence: 50,
        maxIncidence: 50
    });
    
    // Options for each text
    var optionsText = extend({}, baseOptions, {
        color: 'rgba(0, 0, 0, 0.6)',
        maxDistance: 15,
        mouseIncidence: 20,
        maxIncidence: 20
    });
    
    // Options for each arrow
    var optionsArrow = extend({}, baseOptions, {
        color: 'rgba(255, 255, 255, 0.5)',
        pointsNumber: 30,
        maxDistance: 40,
        mouseIncidence: 20,
        maxIncidence: 20
    });

    Then we are going to populate an array with all the options for each shape. We have described the entire process for better understanding:

    var items = 5;      // Number of items in the slider
    var current = 2;    // Index of current item
    var busy = false;   // To check if there is an animation in progress
    var options = [];   // Array to populate the options
    
    // Positions for each circle, obtained with the help of the vector editor
    var circlePositions = [
        {x: -530, y: 5},
        {x: -330, y: -205},
        {x: 0, y: -285},
        {x: 330, y: -205},
        {x: 530, y: 5}
    ];
    
    // Function to build the options for an specific item
    function buildOptions(i) {
        var index = (i + 1);
        var isCurrent = i === current;
    
        // Options for each text, arrow and image, using the base options and the index
        var text = extend({}, optionsText, {paths: '#jelly-text-' + index + ' path'});
        var arrow = extend({}, optionsArrow, {paths: '#jelly-arrow-' + index});
        var image = extend({}, isCurrent ? optionsImage : optionsCircle, {image: 'img/image-' + index + '.jpg'});
    
        // If not the current item, set circle in the position defined, hide text, and hide arrow
        if (!isCurrent) {
            extend(image, circlePositions[i]);
            extend(text, {hidden: true});
            extend(arrow, {hidden: true});
        }
    
        // Push all of these to the options array
        options.push(text);
        options.push(arrow);
        options.push(image);
    }
    
    // Build the options for each item
    for (var i = 0; i < items; i++) {
        buildOptions(i);
    }

    And with this we should have all our jelly shapes working in the browser :)

    Handling events and adding interactivity

    In the previous tutorial we have seen how we can handle the hover event to get the hovered item. We will not explain again how to do this, just keep in mind that we are saving the index of the hovered item in the variable hoverItem.

    Let's see the code below to understand how to handle the click events and perform animations accordingly.

    canvas.addEventListener('click', function () {
        // Checking if an item is hovered and it is not busy
        if (hoverItem >= 0 && !busy) {
            busy = true;
    
            // Hide current text and arrow, and morph the big shape to a circle with the right position
            if (current !== undefined) {
                jelly.hide({i: current * 3, maxDelay: 400});
                jelly.hide({i: current * 3 + 1, maxDelay: 400});
                jelly.morph(extend({i: current * 3 + 2}, optionsCircle, circlePositions[current]));
            }
    
            // For the clicked item, show the hovered text and arrow, and morph the circle into the big shape
            jelly.show({i: hoverItem * 3, maxDelay: 400});
            jelly.show({i: hoverItem * 3 + 1, maxDelay: 400});
            jelly.morph(extend({i: hoverItem * 3 + 2}, optionsImage, {x: 0, y: 0}));
    
            // Update current item and release busy after some time to prevent malfunction
            current = hoverItem;
            setTimeout(function () { busy = false; }, 500);
        }
    });

    Surely you've noticed that we are using the functions show, hide and morph to perform the animations. All of these functions receive a set of options, and they have been properly documented in the github repository.

    Making it responsive

    So far, our slider should work very well. But if we see it in a small screen, we will notice a clear lack of responsiveness. Fortunately, we can solve this issue with the following JavaScript code:

    /* Scale the container accordingly to windows/device size, making it responsive */
    
    var jellyContainer = document.querySelector('.jelly-container');
    
    function resize() {
        var scale = Math.min(
            1,
            window.innerWidth / jellyContainer.clientWidth,
            window.innerHeight / jellyContainer.clientHeight
        );
        jellyContainer.style.transform = 'scale(' + scale + ')';
    }
    
    window.addEventListener('resize', resize);
    
    resize();

    Conclusion

    And with all these we should have an original jelly slider in the browser :)

    See the DEMO, and get the full code on Github.

    Please note that this demo is very experimental, with a high consumption of resources. So, use jelly shapes moderately as they can kill the performance of the web page.

    We hope you find it fun and inspiring!

    Luis Manuel

    12 posts

    Engineer in Computer Science and Freelance Front-End Developer. Available for Hire.