DEV Community

Cover image for Bouncing Shapes Background With Canvas
Krisztián Maurer
Krisztián Maurer

Posted on • Updated on

Bouncing Shapes Background With Canvas

The goal of this project is to make a cool background with randomized shapes and to be customizable.

Demo: https://canvas-shapes-background.netlify.app/

Code: https://github.com/MaurerKrisztian/CanvasShapesBackground

Simple example usage:

Maurer Krisztián
https://codepen.io/maurerkrisztian/pen/rNdGerb

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type='text/javascript' src="https://canvas-shapes-background.netlify.app/bundle.js"></script>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
    createShapeCanvas("myCanvas", {
        modelNumbers: 11,
        colors: ['blue', 'red', 'green', 'yellow'],
        backgroundColor: 'black', //'none',
        enabledModels: ['Triangle', 'Circle', 'Rect'],
        minSpeed: 11,
        maxSpeed: 11,
        lineWidth: 3,
        sizeMultiplier: 2,
        startPosition: 'random' //  'middle' | 'random',
        isFullScreen: true,
    })
</script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

I added a configurator with the demo: https://canvas-shapes-background.netlify.app/

  • Easily test different configs and copy the configured settings.

Maurer Krisztián

"createShapeCanvas" has 2 arg

  • canvas id
  • config

config

  • startPosition: 'middle' | 'random' initial model position
  • enabledModels: thies shapes will be generated randomly
  • modelNumbers: generate this amount of models from "enabledModels" array
  • colors: pisck random color from this array for shapes
  • backgroundColor: canvas background color, can be "none"
  • minSpeed: shape min speed
  • maxSpeed: shape max speed
  • lineWidth: shape border line width
  • sizeMultiplier: multiply the generated size with this
  • isFullScreen: fullscreen or element size

How I made it?

I used HTML Canvas.

I wanted to be easily extendable, so abstracted some thing:

Animator:

  • it is responsible for setup every frame and call the modell update()

Model:

  • has 2 main responsibility:
  • draw() - draw the shape,
  • update()
    • howto behave based on the current x,y position and
    • xd, dy aka where to want to move and how fast. If you confused how dx, dy it works it basicly a vector. it has a direction (this will be the model direction) and a length (this will be the model speed). vector

Example model:

export class CircleModel implements IModel {
    static MODEL_NAME = 'Circle'

    color: string;

    constructor(private x: number, private y: number, private dx: number, private dy: number, private radius: number, private lineWidth: number = Setup.CONFIG.lineWidth) {
        this.color = Utils.pickRandomFromArray<string>(Setup.CONFIG.colors)
    }

    draw(context: CanvasRenderingContext2D) {
        context.lineWidth = this.lineWidth;
        context.beginPath();
        context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
        context.strokeStyle = this.color;
        context.stroke();
        context.lineWidth = DEFAULT_LINE_WIDTH;
    }


    update(context: CanvasRenderingContext2D) {
        if (this.x > Context.canvasWidth - this.radius || this.x < 0 + this.radius) {
            this.dx = -this.dx;
        }
        if (this.y > Context.canvasHeight - this.radius || this.y < 0 + this.radius) {
            this.dy = -this.dy;
        }

        this.x = this.x + this.dx;
        this.y = this.y + this.dy;

        this.draw(context);
    }
}
Enter fullscreen mode Exit fullscreen mode

I wanted to baunce of from the side of the canvas so

Good to know where is the canvas side.

Coordinate system:
Maurer Krisztián Coordinate system

So x(0) the left min position and x(canvasWidth) the right side posion, similarly with y.

        if (this.x > Context.canvasWidth - this.radius || this.x < 0 + this.radius) {
            this.dx = -this.dx;
        }
Enter fullscreen mode Exit fullscreen mode

If the current position (center of the circle) greather than canvas width - radius than change the x direction

Similarly with y.

At the end we need to set the new position:

this.x = this.x + this.dx;
this.y = this.y + this.dy;
Enter fullscreen mode Exit fullscreen mode

I made 3 model: Cirle, Rect, Triangle.

But you can add easily your model:

  • create a model
  • add to ModelFactory
    private static createRandomModel() {
        const speed = Utils.generateRandom(Setup.CONFIG.minSpeed, Setup.CONFIG.maxSpeed)
        const randoModelName = Utils.pickRandomFromArray(Setup.CONFIG.enabledModels)
        switch (randoModelName) {
            case CircleModel.MODEL_NAME:
                return this.createRandomCircle(speed)
                break;
            case TriangleModel.MODEL_NAME:
                return this.createRandomTriangle(speed)
                break;
            case RectModel.MODEL_NAME:
                return this.createRandomRect(speed)
                break;
            default:
                throw new Error(`Model not found with name: ${randoModelName}`)
        }
    }
Enter fullscreen mode Exit fullscreen mode

Howto create importable script?

I just added the init function to the window so you can import the bundle script and call this function.

window.createShapeCanvas = createShapeCanvas
Enter fullscreen mode Exit fullscreen mode

Future plans:

  • add more models
  • add more config options
  • create a react component and publish to npm

Top comments (1)

Collapse
 
maurerkrisztian profile image
Krisztián Maurer

What do you think? should I make it a react component?