Search code examples
javascripthtmlhtml5-canvas

How to draw ringing bell in HTML canvas?


I am trying to draw a bell icon on an HTML canvas and need animation effects on the image like a bell ringing. I haven't been able to find a good way to resolve this. Like draw a bell icon and only do vibration on the lower part of the bell. Can someone give me a hint and show an example? Here's what I've done so far:

var x = 20;
var canvas = document.getElementById('canvas');
var c = canvas.getContext('2d');

var mapSprite = new Image();
mapSprite.src = "https://png.pngtree.com/png-vector/20190411/ourmid/pngtree-vector-bell-icon-png-image_927119.jpg";

function drawIt() {
  window.requestAnimationFrame(drawIt);


  c.globalAlpha = 0.07;
  c.clearRect(0, 0, canvas.width, canvas.height);
  // c.fillStyle = "white"; 
  c.fillRect(x, 10, 80, 100);
  c.stroke();
  c.globalAlpha = 1;

  c.drawImage(mapSprite, x, 10, 80, 100);


  if (x == 20)
    x -= 3
  else
    x += 1;
}

//window.requestAnimationFrame(drawIt);
drawIt();
#canvas {
  border: 1px solid silver;
  width: 100%;
}
<canvas id="canvas"></canvas>


Solution

  • You need to separate the bell dome from the clapper and animate them separately (or draw the clapper your self with a circle).

    You can either only move the clapper or add different animations to each of them: while the clapper would describe an arc trajectory, bouncing from side to side of the dome, the dome would rotate from the top center.

    Here's a simplified version of this:

    const canvas = document.getElementById('canvas'); 
    const ctx = canvas.getContext('2d'); 
    
    const width = canvas.width = document.body.offsetWidth;
    const height = canvas.height = document.body.offsetHeight;
    
    const  bellImg = new Image();
    
    bellImg.src = "https://i.sstatic.net/Hc7Ih.png";
    
    const clapperRadius = 25;
    const lineWidth = 10;
    
    let progress = 0.5;
    let progressDelta = 0.05;
    
    function tick() {
      ctx.clearRect(0, 0, width, height); 
      
      ctx.lineWidth = lineWidth;
    
      ctx.beginPath();
      
      const circleLeftMin = width / 2 - bellImg.width / 2 + clapperRadius + lineWidth;
      const circleLeftMax = width / 2 + bellImg.width / 2 - clapperRadius - lineWidth;
      const circleLeft = Math.min(Math.max(circleLeftMin + (circleLeftMax - circleLeftMin) * progress, circleLeftMin), circleLeftMax);
      
      progress += progressDelta
      
      if (progress >= 1 || progress <= 0) {
        progressDelta = -progressDelta
      }
      
      ctx.arc(
        circleLeft,
        height / 2 + bellImg.height / 2,
        clapperRadius,
        0,
        2 * Math.PI,
      );
      
      ctx.stroke();
    
      ctx.drawImage(
        bellImg,
        width / 2 - bellImg.width / 2,
        height / 2 - bellImg.height / 2
      );
      
      window.requestAnimationFrame(tick); 
    } 
    
    bellImg.onload = () => {
      tick();
    }
    html,
    body {
      margin: 0;
      height:100%;
    }
    
    #canvas {
      position:absolute;
      width: 100%;
      height :100%;
    }
    <canvas id="canvas"></canvas>