Canvas

Used for 2d or 3d(WebGL) rendering

Width and height are special attributes and introduce weird conflicts with CSS

Can be styling like any normal image(not affecting content)

<canvas id="tutorial" width="150" height="150"></canvas>
const canvas = document.getElementById('tutorial');
const ctx = canvas.getContext('2d');

Beautiful Planet Example

var sun = new Image();
var moon = new Image();
var earth = new Image();
function init() {
  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
  window.requestAnimationFrame(draw);
}

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.globalCompositeOperation = 'destination-over';
  ctx.clearRect(0, 0, 300, 300); // clear canvas

  ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
  ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)';
  ctx.save();
  ctx.translate(150, 150);

  // Earth
  var time = new Date();
  ctx.rotate(((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds());
  ctx.translate(105, 0);
  ctx.fillRect(0, -12, 40, 24); // Shadow
  ctx.drawImage(earth, -12, -12);

  // Moon
  ctx.save();
  ctx.rotate(((2 * Math.PI) / 6) * time.getSeconds() + ((2 * Math.PI) / 6000) * time.getMilliseconds());
  ctx.translate(0, 28.5);
  ctx.drawImage(moon, -3.5, -3.5);
  ctx.restore();

  ctx.restore();

  ctx.beginPath();
  ctx.arc(150, 150, 105, 0, Math.PI * 2, false); // Earth orbit
  ctx.stroke();

  ctx.drawImage(sun, 0, 0, 300, 300);

  window.requestAnimationFrame(draw);
}

init();

Balls With Collision Example

ball.js

const SPEED = 10;
import { randomColor } from "./helpers.js";

export default class Ball {
  constructor(cc, color, radius, tx, ty) {
    this.cc = cc;
    this.color = color;
    this.radius = radius;
    this.startradius = this.radius;
    this.tx = tx;
    this.ty = ty;
    this.x = Math.random() * (tx - this.radius * 2) + this.radius;
    this.y = Math.random() * (ty - this.radius * 2) + this.radius;
    this.dy = (Math.random() - 0.5) * SPEED;
    this.dx = (Math.random() - 0.5) * SPEED;
  }

  collide(balls) {
    for (let b of balls) {
      if (b === this) {
        continue;
      }

      const distX = b.x - this.x;
      const distY = b.y - this.y;
      const dist = Math.sqrt(distX * distX + distY * distY);
      const minDist = b.radius + this.radius;
      if (dist < minDist) {
        this.color = randomColor();
        const tempX = this.dx;
        this.dx = b.dx;
        b.dx = tempX;
        const tempY = this.dy;
        this.dy = b.dy;
        b.dy = tempY;
      }
    }
  }

  move(tx, ty) {
    this.tx = tx;
    this.ty = ty;
    this.y += this.dy;
    this.x += this.dx;
    if (this.y + this.radius >= this.ty || this.y - this.radius <= 0) {
      this.dy = -this.dy;
    }

    if (this.x + this.radius >= this.tx || this.x - this.radius <= 0) {
      this.dx = -this.dx;
    }
  }

  draw() {
    this.cc.beginPath();
    this.cc.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    this.cc.fillStyle = this.color;
    this.cc.fill();
    //c.stroke();
  }
}

canvas.js

import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";

import { randomColor } from "./helpers.js";
import Ball from "./Ball.js";
const debug = require("debug")("app:Canvas");

const NUMBER_OF_BALLS = 5;
const scaleWidth = 500;
const scaleHeight = 500;

export default function Canvas() {
  const canRef = useRef(null);

  useEffect(() => {
    debug("Hello World");

    const canvas = canRef.current;
    const cc = canvas.getContext("2d");
    let tx = window.innerWidth;
    let ty = window.innerHeight;
    canvas.width = tx;
    canvas.height = ty;
    //c.lineWidth= 5;
    //c.globalAlpha = 0.5;

    let mousex = 0;
    let mousey = 0;

    addEventListener("mousemove", function () {
      mousex = event.clientX;
      mousey = event.clientY;
    });

    cc.strokeWidth = 5;

    let balls = [];
    for (let i = 0; i < NUMBER_OF_BALLS; i++) {
      balls.push(new Ball(cc, randomColor(), 50, tx, ty));
    }

    function animate() {
      debug("CW", canvas.clientWidth, "CH", canvas.clientHeight);
      if (tx != window.innerWidth || ty != window.innerHeight) {
        tx = window.innerWidth;
        ty = window.innerHeight;
        canvas.width = tx;
        canvas.height = ty;
      }
      requestAnimationFrame(animate);
      cc.clearRect(0, 0, tx, ty);
      for (let b of balls) {
        b.collide(balls);
        b.move(tx, ty);
        b.draw();

        if (
          mousex > b.x - 20 &&
          mousex < b.x + 20 &&
          mousey > b.y - 50 &&
          mousey < b.y + 50 &&
          b.radius < 70
        ) {
          //bal[i].x += +1;
          b.radius += 5;
        } else {
          if (b.radius > b.startradius) {
            b.radius += -5;
          }
        }
      }
    }

    animate();
  }, []);

  return <canvas className="border border-black w-full h-64" ref={canRef} />;
}

Last updated