Display bounce off the walls object on the page

Viewed 506

I built a dashboard for my first baby with the ability to add milestones. I decided to use (⭐️) to represent each milestone achieved on a day and represent it as score. As you can see today, I added First smile, his score is (⭐️ x 1). To make it fun, on the desktop view, I would like to display the bouncing star. If his score is 3, 3 stars will be bouncing on the desktop.

Example

I've tried this

https://jsfiddle.net/bheng/cnxt9Lju/2/

<canvas id="myCanvas" width="400" height="200"></canvas>

var canvas = document.getElementById( "myCanvas" );
var context = canvas.getContext( "2d" );
var width = 400;
var height = 200;

var ball = {
    x: 100,
    y: 100,
    radius: 25,
    xSpeed: 3,
    ySpeed: 3,
    draw: function( ctx ) {
        ctx.beginPath();
        ctx.arc( this.x, this.y, this.radius, 0, 2*Math.PI );
        ctx.fill();
    },
    move: function() {
        this.x += this.xSpeed;
        this.y += this.ySpeed;
    }
}

setInterval( function(){
    
    context.clearRect( 0, 0, width, height );
    context.strokeRect( 0, 0, width, height );
    
    ball.move();
    
    // right 
    if ( ball.x + ball.radius >= width ) {
        ball.x = width - ball.radius;
        ball.xSpeed = -ball.xSpeed;
    }
    
    // left
    if ( ball.x - ball.radius <= 0 ) {
        ball.x = ball.radius;
        ball.xSpeed = -ball.xSpeed;
    }
    
    // down
    if ( ball.y + ball.radius >= height ) {
        ball.y = height - ball.radius;
        ball.ySpeed = -ball.ySpeed;
    }
    
    // up
    if ( ball.y - ball.radius <= 0 ) {
        ball.y = ball.radius;
        ball.ySpeed = -ball.ySpeed;
    }
    
    ball.draw( context );
    
}, 10 );

but I'm not sure how to change the ball to ⭐️.

Also when I tried to modify the width and height to window.width, and window.height, it seem to break things.

1 Answers

This question has two parts - drawing an emoji (the star) on a canvas and sizing so that the star bounces around the whole viewport.

First - the emoji. Normal keyboards don't have the star emoji on them and the code given in the question uses the HTML & representation to display the star. Having been all over Stackoverflow looking for ways to convert non-8bit Unicode characters into something that fillText (the way to draw text onto a canvas) can recognise, the accepted answer at How to load and draw emojis on canvas efficiently? led me to https://emojipedia.org/star/

I do not claim to understand exactly what is being copied but copying the star and pasting it into ctx.fillText drew the star on the canvas.

For the resizing to fill the viewport we have to do several things - resize the actual canvas, set width/height in the JS on starting and on a resize event and clear timeout before setting a new one on resize.

This snippet deliberately keeps all the code given in the question apart from the changes needed to do these two things. But see notes below for other potential methods.

var canvas = document.getElementById( "myCanvas" );
var context = canvas.getContext( "2d" );
let timeout;

function setup() {
clearTimeout(timeout); // ADDED
var width = window.innerWidth; // CHANGED
var height = window.innerHeight; // CHANGED

canvas.width = width; // ADDED
alert(canvas.width);
canvas.height = height //ADDED

var ball = {
    x: 100,
    y: 100,
    radius: 25,
    xSpeed: 3,
    ySpeed: 3,
    draw: function( ctx ) {
        ctx.beginPath();
        //ctx.arc( this.x, this.y, this.radius, 0, 2*Math.PI );
       // ctx.fill();
       ctx.font = "48px serif";
ctx.fillText("⭐", this.x, this.y);
    },
    move: function() {
        this.x += this.xSpeed;
        this.y += this.ySpeed;
    }
}

timeout = setInterval( function(){
    
    context.clearRect( 0, 0, width, height );
    context.strokeRect( 0, 0, width, height );
    
    ball.move();
    
    // right 
    if ( ball.x + ball.radius >= width ) {
        ball.x = width - ball.radius;
        ball.xSpeed = -ball.xSpeed;
    }
    
    // left
    if ( ball.x - ball.radius <= 0 ) {
        ball.x = ball.radius;
        ball.xSpeed = -ball.xSpeed;
    }
    
    // down
    if ( ball.y + ball.radius >= height ) {
        ball.y = height - ball.radius;
        ball.ySpeed = -ball.ySpeed;
    }
    
    // up
    if ( ball.y - ball.radius <= 0 ) {
        ball.y = ball.radius;
        ball.ySpeed = -ball.ySpeed;
    }
    
    ball.draw( context );
    
}, 10 );
}
window.onresize = setup;
setup();
<canvas id="myCanvas" width="400" height="200"></canvas>

Notes: this code would need tidying up to allow several stars at once - starting each perhaps at a different point on the canvas so they didn't all just follow the same path.

Although this code works it is not very efficient - a call being made every 10ms to redraw the canvas. Also it means you have a full size canvas covering the whole viewport (at some high z-index) which you probably don't particularly want.

Doing away with using a canvas by altering the code so that each star is an HTML element with the single (star) character in it and changing its position with a star.el.style.top/left each time gets round the problem of needing an element covering the whole viewport.

Less processing power would also be used if the animations were done without needing a setInterval at such short time intervals. For example, calculating the straightline path from border to border and updating the endpoints (which can be set as CSS variables) in a CSS animation as a star reaches the end of its bounce from one edge to another plus using transform rather than left/top might move more processing onto a GPU and reduce the overall processing power (== user's battery) needed.

Related