Anonymous
Anonymous

Reputation: 113

How to get alert Box with sound?

I am trying to get alert like pop-up with sound when the balls collide. When I use alert function it gets freeze and it will keep on showing alert. As far as now i tried to get alert like in text. But how to get alert with sound(eg.beep) ? I have attached the code snippet. Can anyone help to get this. Thanks in advance

const ctx = document.getElementById("Canvas").getContext("2d");
const containerR = 150;
const size = containerR * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx.globalAlpha = 0.8

const getBall = (x, y, dx, dy, r, color) => ({x, y, dx, dy, r, color});

const balls = [
  getBall(size / 2, size - 30, 1, 1, 8, "Green"),
  getBall(size / 3, size - 50, 1, 1, 8, "Green"),
  getBall(size / 4, size - 60, 1, 1, 8, "Green"),
  getBall(size / 2, size / 5,  1, 1, 8,  "Green"),
];

const drawBall = (ball) => {
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
  ctx.fillStyle = ball.collider ? "red" : ball.color;
  ctx.fill();
  ctx.closePath();
}

const updatePos = (ball) => {

  ball.x += ball.dx;
  ball.y += ball.dy;
  const dx = ball.x - containerR;
  const dy = ball.y - containerR;

  if (Math.sqrt(dx * dx + dy * dy) >= containerR - ball.r) {
    const v = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
    const angleToCollisionPoint = Math.atan2(-dy, dx);
    const oldAngle = Math.atan2(-ball.dy, ball.dx);
    const newAngle = 2 * angleToCollisionPoint - oldAngle;
    ball.dx = -v * Math.cos(newAngle);
    ball.dy = v * Math.sin(newAngle);
  }
}


const collides = (a, b) => (Math.hypot(Math.abs(a.x - b.x), Math.abs(a.y - b.y)) < (a.r + b.r));

function engine() {
  //console.clear(); // Clear console test messages
  mydiv.textContent =" ";
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

  balls.forEach((a, ai) => {
    a.collider = undefined;
    
    balls.forEach((b, bi) => {
      if (bi === ai) return; // Don't look at self
      if (collides(a, b)) a.collider = b; // Store the colliding B ball
    });
    
    if (a.collider) { // If ball has a collider:
     mydiv.textContent = ("Alert");
      //console.log(`${a.color[0]} → ← ${a.collider.color[0]}`);
    }
    
    updatePos(a);
    drawBall(a);
  });

  requestAnimationFrame(engine);
}

engine();
<style>
canvas {
  background: #eee;
  margin: 0 auto;
  border-radius: 50%;
  box-shadow: 0 0 0 4px #000;
}
</style>
<html>
<canvas id="Canvas"></canvas>
<div id="mydiv"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>
</html>

Upvotes: 1

Views: 1040

Answers (2)

ROOT
ROOT

Reputation: 11622

You can't use normal .alert() without halting the javascript execution so you have to stick with HTML alert/prompt (there is many libraries out there provide neat and customizable UI), for the beep sound, you can just use based64 data with data URI, this snippet here wont work because the SO snippets are sandbox, here is a working fiddle you can improve.

the beep sound example based on this answer here

Update: I added the custom alert and its based on this example from bluebird implementation, there is custom HTML and CSS, and Javascript PromptDialog(), basically this Javascript object implementation is asynchronous ans doesn't block the execution, please refer to the linked bluebird article for more information since its a guide on how to build it and it explains everything better than me.

function beep() {
    var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");  
    snd.play();
}


var noop = function() {
  return this;
};

function UserCanceledError() {
  this.name = 'UserCanceledError';
  this.message = 'User canceled dialog';
}
UserCanceledError.prototype = Object.create(Error.prototype);

function Dialog() {
  this.setCallbacks(noop, noop);
}
Dialog.prototype.setCallbacks = function(okCallback, cancelCallback) {
  this._okCallback     = okCallback;
  return this;
};
Dialog.prototype.waitForUser = function() {
  var _this = this;
  return new Promise(function(resolve, reject) {
    _this.setCallbacks(resolve, reject);
  });
};

Dialog.prototype.show = noop;
Dialog.prototype.hide = noop;

function PromptDialog() {
  Dialog.call(this);
  this.el           = document.getElementById('dialog');
  this.messageEl    = this.el.querySelector('.message');
  this.okButton     = this.el.querySelector('button.ok');
  this.attachDomEvents();
}
PromptDialog.prototype = Object.create(Dialog.prototype);
PromptDialog.prototype.attachDomEvents = function() {
  var _this = this;
  this.okButton.addEventListener('click', function() {
    _this.hide();
    console.log('Ok clicked!!');
  });
  
};
PromptDialog.prototype.show = function(message) {
  this.messageEl.innerHTML = '' + message;
  this.el.className = '';
  return this;
};
PromptDialog.prototype.hide = function() {
  this.el.className = 'hidden';
  return this;
};



const ctx = document.getElementById("Canvas").getContext("2d");
const containerR = 150;
const size = containerR * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx.globalAlpha = 0.8


var prompt = new PromptDialog();

const getBall = (x, y, dx, dy, r, color) => ({x, y, dx, dy, r, color});

const balls = [
  getBall(size / 2, size - 30, 1, 1, 8, "Green"),
  getBall(size / 3, size - 50, 1, 1, 8, "Green"),
  getBall(size / 4, size - 60, 1, 1, 8, "Green"),
  getBall(size / 2, size / 5,  1, 1, 8,  "Green"),
];

const drawBall = (ball) => {
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
  ctx.fillStyle = ball.collider ? "red" : ball.color;
  ctx.fill();
  ctx.closePath();
}

const updatePos = (ball) => {

  ball.x += ball.dx;
  ball.y += ball.dy;
  const dx = ball.x - containerR;
  const dy = ball.y - containerR;

  if (Math.sqrt(dx * dx + dy * dy) >= containerR - ball.r) {
    const v = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
    const angleToCollisionPoint = Math.atan2(-dy, dx);
    const oldAngle = Math.atan2(-ball.dy, ball.dx);
    const newAngle = 2 * angleToCollisionPoint - oldAngle;
    ball.dx = -v * Math.cos(newAngle);
    ball.dy = v * Math.sin(newAngle);
  }
}


const collides = (a, b) => (Math.hypot(Math.abs(a.x - b.x), Math.abs(a.y - b.y)) < (a.r + b.r));

function engine() {
  //console.clear(); // Clear console test messages
  mydiv.textContent =" ";
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

  balls.forEach((a, ai) => {
    a.collider = undefined;
    
    balls.forEach((b, bi) => {
      if (bi === ai) return; // Don't look at self
      if (collides(a, b)) a.collider = b; // Store the colliding B ball
    });
    
    if (a.collider) { // If ball has a collider:
     mydiv.textContent = ("Alert");
     beep();
     
     
     prompt.show('collision detected')
      .waitForUser()
      .then(function(name) {
        output.innerHTML = '' + name;
      })
      .catch(function(e) {
        console.log('Unknown error', e);
      })
      .finally(function() {
        prompt.hide();
      });
      
      //console.log(`${a.color[0]} → ← ${a.collider.color[0]}`);
    }
    
    updatePos(a);
    drawBall(a);
  });

  requestAnimationFrame(engine);
}

engine();
canvas {
  background: #eee;
  margin: 0 auto;
  border-radius: 50%;
  box-shadow: 0 0 0 4px #000;
}

#dialog {
  width: 200px;
  margin: auto;
  border: thin solid black;
  padding: 10px;
  background: lightgreen;
}
.hidden {
  display: none;
}
<canvas id="Canvas"></canvas>
<div id="mydiv"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>

<div id="dialog" class="hidden">
  <div class="message"></div>
  <div>
    <button class="ok">Ok</button>
  </div>
</div>

Upvotes: 1

Hossein Zamanian
Hossein Zamanian

Reputation: 16

UPDATE: User interaction

you can add an audio tag and play it for sound

note: in newer browser chrome ^50, you need user interaction to audio tag work fine

const startBtn = document.getElementById("start-btn")
const sound = document.getElementById("beeb-player")
const ctx = document.getElementById("Canvas").getContext("2d");
const containerR = 150;
const size = containerR * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx.globalAlpha = 0.8

const getBall = (x, y, dx, dy, r, color) => ({x, y, dx, dy, r, color});

const balls = [
  getBall(size / 2, size - 30, 1, 1, 8, "Green"),
  getBall(size / 3, size - 50, 1, 1, 8, "Green"),
  getBall(size / 4, size - 60, 1, 1, 8, "Green"),
  getBall(size / 2, size / 5,  1, 1, 8,  "Green"),
];

const drawBall = (ball) => {
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
  ctx.fillStyle = ball.collider ? "red" : ball.color;
  ctx.fill();
  ctx.closePath();
}

const updatePos = (ball) => {

  ball.x += ball.dx;
  ball.y += ball.dy;
  const dx = ball.x - containerR;
  const dy = ball.y - containerR;

  if (Math.sqrt(dx * dx + dy * dy) >= containerR - ball.r) {
    const v = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
    const angleToCollisionPoint = Math.atan2(-dy, dx);
    const oldAngle = Math.atan2(-ball.dy, ball.dx);
    const newAngle = 2 * angleToCollisionPoint - oldAngle;
    ball.dx = -v * Math.cos(newAngle);
    ball.dy = v * Math.sin(newAngle);
  }
}


const collides = (a, b) => (Math.hypot(Math.abs(a.x - b.x), Math.abs(a.y - b.y)) < (a.r + b.r));

function engine() {
  //console.clear(); // Clear console test messages
  mydiv.textContent =" ";
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

  balls.forEach((a, ai) => {
    a.collider = undefined;
    
    balls.forEach((b, bi) => {
      if (bi === ai) return; // Don't look at self
      if (collides(a, b)) a.collider = b; // Store the colliding B ball
    });
    
    if (a.collider) { // If ball has a collider:
     mydiv.textContent = ("Alert");
     sound.play()
//console.log(`${a.color[0]} → ← ${a.collider.color[0]}`);
    }
    
    updatePos(a);
    drawBall(a);
  });

  requestAnimationFrame(engine);
}

startBtn.addEventListener('click', engine, {once:true})
<style>
canvas {
  background: #eee;
  margin: 0 auto;
  border-radius: 50%;
  box-shadow: 0 0 0 4px #000;
}
</style>
<html>
<button id="start-btn">start</button>
<canvas id="Canvas"></canvas>
<div id="mydiv"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>
<audio src="http://soundbible.com/grab.php?id=1815&type=mp3" id="beeb-player"></audio>
</html>

you can add an audio tag and play it when the balls collide

function alertWithSound() {
const el = document.getElementById("beeb-player")
el.play()
}
<audio src="http://soundbible.com/grab.php?id=1815&type=mp3" id="beeb-player"></audio>

Upvotes: 0

Related Questions