Creating an Animated Hero Image with Canvas

By Dave DeHaan

When we were looking at laying out spark.lamplighter.io, we decided we wanted to add the feel of our graphs to the top of the page. The suggestion came up that it would be cool if our graph actually “cut off” our hero image, so that any part of our hero image under the lines would be the page background color. You can see the result on our company 404 page. Let’s look at how it was created.

Setting the Context

By changing the hero image’s div to a canvas, while keeping the background image, we were able to draw on top of the image without changing the formatting of the page. From there, we started by randomly generating the numbers for each of the graph lines, so we’d be able to draw both the lines, and the area below them.

First, we got our canvas, made sure the dimensions would be what we expected, and created a context.

var pageWidth = document.width ? document.width : document.documentElement.clientWidth;
var canvas = document.getElementById('header');
canvas.width    = pageWidth;
canvas.height   = 500;
var context = canvas.getContext('2d');

From there, we created a colors object, so we could loop through it for how many lines we wanted to create on the graph, and then created an array of points between 400 and 500 for each of the colors.

var points = [];
var colors = {
    'green': '#81bf06',
    'yellow': '#ffc50c',
    'red': '#ff0808',
}
var pointCount = 20;
for (var i = 0; i < pointCount; i++) {
    point[i] = [];
    // Get a number between 400 & 500
    for (var color in colors) {
        point[i][color] = Math.floor(Math.random() * 100) + 400;
    }
}

Filling the Gaps

At this point, we wanted to be able to fill in the area below each graph, so we drew each of the lines across the canvas, then created a bounded area by drawing lines from the last line point to the bottom right, from the bottom right to the bottom left, and from the bottom left to the first line point. After that, we filled in the area with the page background color.

var fillColor = '#F9F9F9';
// Draw the area under the graph
for (var color in colors) {
    for (var i = 1; i < pointCount; i++) {
        context.lineTo(pageWidth * i/pointCount, point[i][color]);
    }
    context.lineTo(pageWidth, 450);
    context.lineTo(pageWidth, 500);
    context.lineTo(0, 500);
    context.lineTo(0, 450);
}
// Set fill color
context.fillStyle = fillColor;
context.fill();

Since we have the space under each of the lines filled now, we just need to draw the actual graph lines. We decided it would look best if each line started and ended at 450.

var lineWidth = 4;
// Set our line width
context.lineWidth = lineWidth;
// Draw our lines
for (var color in colors) {
    context.beginPath();
    // Move to the first point
    context.moveTo(0,450);
    // Loop through all the points
    for (var i = 1; i < pointCount; i++) {
        context.lineTo(pageWidth * i/pointCount, point[i][color]);
    }
    // Line to the last point
    context.lineTo(pageWidth, 450);
    // Set our color
    context.strokeStyle = colors[color];
    // Draw the line.
    context.stroke();
}

Creating Multiple Graphs

When this was finished and working, we moved all the configuration variables at the top, and wrapped the entire thing in a function, so it could be called multiple times to create different graphs.

Our finished code:

/**
 *  Graph Canvas --
 *  Adds a graph to the bottom of a hero image
 *  By Masuga Design
 *  https://masugadesign.com
 *  Copyright (c) 2013 Masuga Design
 **/

/** START CONFIG VARIABLES **/

    // Set our line colors
    var colors = {
        'green': '#81bf06',
        'yellow': '#ffc50c',
        'red': '#ff0808',
    };

    // Set the fill color for under the graph
    var fillColor = '#F9F9F9';

    // Set our line width
    var lineWidth = 4;

    // How many points in the array?
    var pointCount  = 20;

/** END CONFIG VARIABLES **/

// Point Array
var point       = [];

function generateGraph() {
    var pageWidth = document.width ? document.width : document.documentElement.clientWidth;

    // Set up our canvas & context
    canvas          = document.getElementById('header');
    canvas.width    = pageWidth;
    canvas.height   = 500;
    context         = canvas.getContext('2d');

    // Create our points array with our colors.
    for (var i = 0; i < pointCount; i++) {
        point[i] = [];
        // Get a number between 400 & 500
        for (var color in colors) {
            point[i][color] = Math.floor(Math.random() * 100) + 400;
        }
    }

    // Draw the area under the graph
    for (var color in colors) {
        for (var i = 1; i < pointCount; i++) {
            context.lineTo(pageWidth * i/pointCount, point[i][color]);
        }
        context.lineTo(pageWidth, 450);
        context.lineTo(pageWidth, 500);
        context.lineTo(0, 500);
        context.lineTo(0, 450);
    }

    // Set fill color
    context.fillStyle = fillColor;
    context.fill();

    // Set our line width
    context.lineWidth = lineWidth;

    // Draw our lines
    for (var color in colors) {
        context.beginPath();
        // Move to the first point
        context.moveTo(0,450);
        // Loop through all the points
        for (var i = 1; i < pointCount; i++) {
            context.lineTo(pageWidth * i/pointCount, point[i][color]);
        }
        // Line to the last point
        context.lineTo(pageWidth, 450);
        // Set our color
        context.strokeStyle = colors[color];
        // Draw the line.
        context.stroke();
    }
}

Since this was all in a function, we were able to call the function on an interval using setInterval, creating the effect found on our 404 page, where our graph is being regenerated every second.

setInterval(function() {
        generateGraph();
    }, 1000);

This article was originally published at https://spark.lamplighter.io/post/2013/10/10/creating-a-dynamic-hero-image-using-canvas

Previous

Audio Synthesis in JavaScript

Using Bootstrap 3 as a Web Development Workflow Tool

Next

1 thought on “Creating an Animated Hero Image with Canvas”

Comments are closed.