How to align 4 circles - javascript

I am trying to align these 4 circles ( top 2 circle below that 2 circle). I tried changing the margin style it fails and it is creating 4 circles vertically. I need 2 circles at the top below that 2 circle should be there at the right side of the webpage. Can anyone help how to align these 4 circle horizontally 2*2.
const ctx = document.getElementById("Canvas").getContext("2d");
const ctx2 = document.getElementById("Canvas2").getContext("2d");
const ctx3 = document.getElementById("Canvas3").getContext("2d");
const ctx4 = document.getElementById("Canvas4").getContext("2d");
const containerR = 80;
const size = containerR * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx2.canvas.width = ctx2.canvas.height = size;
ctx3.canvas.width = ctx3.canvas.height = size;
ctx4.canvas.width = ctx4.canvas.height = size;
ctx.globalAlpha = 0.6;
ctx2.globalAlpha = 0.8;
ctx3.globalAlpha = 0.8;
ctx4.globalAlpha = 0.8;
const getBall = (x, y, dx, dy, r, color) => ({x, y, dx, dy, r, color});
const balls = [
getBall(size / 2, size - 30, -0.1, -0.1, 4, "Green"),
getBall(size / 3, size - 50, 0.1, 0.1, 4, "Green"),
getBall(size / 4, size - 60, -0.1, 0.1, 4, "Green"),
getBall(size / 2, size / 5, 0.1, 0.1, 4, "Green"),
getBall(size / 2, size / 5, 0.1, -0.1, 4, "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();
ctx2.beginPath();
ctx2.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx2.fillStyle = ball.collider ? "red" : ball.color;
ctx2.fill();
ctx2.closePath();
ctx3.beginPath();
ctx3.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx3.fillStyle = ball.collider ? "red" : ball.color;
ctx3.fill();
ctx3.closePath();
ctx4.beginPath();
ctx4.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx4.fillStyle = ball.collider ? "red" : ball.color;
ctx4.fill();
ctx4.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
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx3.clearRect(0, 0, ctx3.canvas.width, ctx3.canvas.height);
ctx4.clearRect(0, 0, ctx4.canvas.width, ctx4.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
});
updatePos(a);
drawBall(a);
});
requestAnimationFrame(engine);
}
engine();
<canvas id="Canvas"
style = "background: #eee;
margin-top: 5%;
margin-left: 60%;
border-radius: 50%;
box-shadow: 0 0 0 4px #000;
align-content: right;"></canvas>
<canvas id ="Canvas2"
style = "background: #eee;
margin-top: 8%;
margin-left: 60%;
border-radius: 50%;
box-shadow: 0 0 0 4px #000;
align-content: right;"></canvas>
<canvas id ="Canvas3"
style = "background: #eee;
margin-top: 6%;
margin-left: 75%;
border-radius: 50%;
box-shadow: 0 0 0 4px #000;
align-content: right;"></canvas>
<canvas id ="Canvas4"
style = "background: #eee;
margin-top: 6%;
margin-left: 75%;
margin-bottom: 50%;
border-radius: 50%;
box-shadow: 0 0 0 4px #000;
align-content: right;"></canvas>

To align the layout I used some CSS flexbox features, within these features it's possible to align them on all possible ways.
Learn more about flexbox here: https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox and see my example for a quick start.
const ctx = document.getElementById("Canvas").getContext("2d");
const ctx2 = document.getElementById("Canvas2").getContext("2d");
const ctx3 = document.getElementById("Canvas3").getContext("2d");
const ctx4 = document.getElementById("Canvas4").getContext("2d");
const containerR = 80;
const size = containerR * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx2.canvas.width = ctx2.canvas.height = size;
ctx3.canvas.width = ctx3.canvas.height = size;
ctx4.canvas.width = ctx4.canvas.height = size;
ctx.globalAlpha = 0.6;
ctx2.globalAlpha = 0.8;
ctx3.globalAlpha = 0.8;
ctx4.globalAlpha = 0.8;
const getBall = (x, y, dx, dy, r, color) => ({
x,
y,
dx,
dy,
r,
color
});
const balls = [
getBall(size / 2, size - 30, -0.1, -0.1, 4, "Green"),
getBall(size / 3, size - 50, 0.1, 0.1, 4, "Green"),
getBall(size / 4, size - 60, -0.1, 0.1, 4, "Green"),
getBall(size / 2, size / 5, 0.1, 0.1, 4, "Green"),
getBall(size / 2, size / 5, 0.1, -0.1, 4, "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();
ctx2.beginPath();
ctx2.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx2.fillStyle = ball.collider ? "red" : ball.color;
ctx2.fill();
ctx2.closePath();
ctx3.beginPath();
ctx3.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx3.fillStyle = ball.collider ? "red" : ball.color;
ctx3.fill();
ctx3.closePath();
ctx4.beginPath();
ctx4.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx4.fillStyle = ball.collider ? "red" : ball.color;
ctx4.fill();
ctx4.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
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx3.clearRect(0, 0, ctx3.canvas.width, ctx3.canvas.height);
ctx4.clearRect(0, 0, ctx4.canvas.width, ctx4.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
});
updatePos(a);
drawBall(a);
});
requestAnimationFrame(engine);
}
engine();
.row {
display: flex;
}
.container {
display: flex;
flex-direction: column;
}
#Canvas, #Canvas2, #Canvas3, #Canvas4 {
background: #eee;
border-radius: 50%;
border: solid 4px #000;
margin: 10px;
}
.canvas-holder {
display: flex;
flex-direction: column;
align-items: center;
}
<div class="container">
<div class="row">
<div class="canvas-holder">
<canvas id="Canvas"></canvas>
<span>Circle one</span>
</div>
<div class="canvas-holder">
<canvas id="Canvas2"></canvas>
<span>Circle two</span>
</div>
</div>
<div class="row">
<div class="canvas-holder">
<canvas id="Canvas3"></canvas>
<span>Circle tree</span>
</div>
<div class="canvas-holder">
<canvas id="Canvas4"></canvas>
<span>Circle four</span>
</div>
</div>
</div>

I suggest moving your styles to CSS to reduce repeated styles.
For example:
const ctx = document.getElementById("Canvas").getContext("2d");
const ctx2 = document.getElementById("Canvas2").getContext("2d");
const ctx3 = document.getElementById("Canvas3").getContext("2d");
const ctx4 = document.getElementById("Canvas4").getContext("2d");
const containerR = 80;
const size = containerR * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx2.canvas.width = ctx2.canvas.height = size;
ctx3.canvas.width = ctx3.canvas.height = size;
ctx4.canvas.width = ctx4.canvas.height = size;
ctx.globalAlpha = 0.6;
ctx2.globalAlpha = 0.8;
ctx3.globalAlpha = 0.8;
ctx4.globalAlpha = 0.8;
const getBall = (x, y, dx, dy, r, color) => ({x, y, dx, dy, r, color});
const balls = [
getBall(size / 2, size - 30, -0.1, -0.1, 4, "Green"),
getBall(size / 3, size - 50, 0.1, 0.1, 4, "Green"),
getBall(size / 4, size - 60, -0.1, 0.1, 4, "Green"),
getBall(size / 2, size / 5, 0.1, 0.1, 4, "Green"),
getBall(size / 2, size / 5, 0.1, -0.1, 4, "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();
ctx2.beginPath();
ctx2.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx2.fillStyle = ball.collider ? "red" : ball.color;
ctx2.fill();
ctx2.closePath();
ctx3.beginPath();
ctx3.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx3.fillStyle = ball.collider ? "red" : ball.color;
ctx3.fill();
ctx3.closePath();
ctx4.beginPath();
ctx4.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx4.fillStyle = ball.collider ? "red" : ball.color;
ctx4.fill();
ctx4.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
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx3.clearRect(0, 0, ctx3.canvas.width, ctx3.canvas.height);
ctx4.clearRect(0, 0, ctx4.canvas.width, ctx4.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
});
updatePos(a);
drawBall(a);
});
requestAnimationFrame(engine);
}
engine();
.canvas-wrapper {
display: flex;
flex-wrap: wrap;
}
.canvas-wrapper div {
width: 50%;
margin-bottom: 10px;
}
.canvas-wrapper canvas {
background: #eee;
border-radius: 50%;
box-shadow: 0 0 0 4px #000;
max-width: 80%;
box-sizing: border-box;
}
<div class="canvas-wrapper">
<div><canvas id="Canvas"></canvas></div>
<div><canvas id ="Canvas2"></canvas></div>
<div><canvas id ="Canvas3"></canvas></div>
<div><canvas id ="Canvas4"></canvas></div>
</div>

Related

Algorithm for drawing lines with 90-degree angles

To get straight to the point, what I hope to achieve is to be able to create a connecting line between two elements with this shape:
DBDIAGRAM.IO
When the elements move, the line resets but always maintains a 90 degree angle, instead of being a straight or diagonal line between [x,y] to [x,y].
Is there some kind of algorithm for this? Maybe a grid with some kind of A* implementation?
I don't know how to make rounded corners easy, but the easiest example will be this:
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// define the points
const p1 = {
x: 30,
y: 50
}
const p2 = {
x: 150,
y: 130
}
ctx.strokeStyle = 'red'
// draw the points
ctx.beginPath()
ctx.arc(p1.x, p1.y, 5, 0, Math.PI * 2)
ctx.stroke()
ctx.beginPath()
ctx.arc(p2.x, p2.y, 5, 0, Math.PI * 2)
ctx.stroke()
// get distance between
const horizontalDistance = p2.x - p1.x
ctx.strokeStyle = 'black'
// draw left part
ctx.beginPath()
ctx.moveTo(p1.x, p1.y)
ctx.lineTo(p1.x + horizontalDistance / 2, p1.y)
ctx.stroke()
// draw vertical part
ctx.beginPath()
ctx.moveTo(p1.x + horizontalDistance / 2, p1.y)
ctx.lineTo(p1.x + horizontalDistance / 2, p2.y)
ctx.stroke()
// draw right part
ctx.beginPath()
ctx.moveTo(p1.x + horizontalDistance / 2, p2.y)
ctx.lineTo(p2.x, p2.y)
ctx.stroke()
canvas {
border: 1px solid black;
}
<canvas></canvas>
Real-time version:
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const p1 = {
x: canvas.width / 2,
y: canvas.height / 2
}
const p2 = {
x: 150,
y: 130
}
canvas.addEventListener('mousemove', e => {
const mousePos = getMousePos(canvas, e)
p2.x = mousePos.x
p2.y = mousePos.y
})
loop()
function loop() {
draw()
requestAnimationFrame(loop)
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.strokeStyle = 'red'
ctx.beginPath()
ctx.arc(p1.x, p1.y, 5, 0, Math.PI * 2)
ctx.stroke()
ctx.beginPath()
ctx.arc(p2.x, p2.y, 5, 0, Math.PI * 2)
ctx.stroke()
const horizontalDistance = p2.x - p1.x
ctx.strokeStyle = 'black'
ctx.beginPath()
ctx.moveTo(p1.x, p1.y)
ctx.lineTo(p1.x + horizontalDistance / 2, p1.y)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(p1.x + horizontalDistance / 2, p1.y)
ctx.lineTo(p1.x + horizontalDistance / 2, p2.y)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(p1.x + horizontalDistance / 2, p2.y)
ctx.lineTo(p2.x, p2.y)
ctx.stroke()
}
function getMousePos(canvas, evt) {
const rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas {
border: 1px solid black;
}
<canvas></canvas>

discord/js and angle of arc function

Example:
Try to get a handle on Arc and I'm running into a wall trying to figure out the end angle. I need something simple so can feed it a percentage and it make an arc that that's percent of a circle.
I got the arc to start at the top and I know math.pi*2 will get me all the way around but when I try to modify that with the old *0.p trick of a converted percentage I get this.
white is 0.1/10%
red is 0.95/95%
green is 0.5/50%
blue is 0.7/70%
but none of them look like that and I just can't figure out the way it's figuring the arc.
var radius = 250 / 2 - 5;
var centerx = 250 / 2;
var centery = 250 / 2;
const startagle = Math.PI * 1.5;
var endagle = (Math.PI * 2)*0.1;
context.beginPath();
context.fillStyle = 'white';
context.arc(centerx, centery, radius, startagle, endagle, false);
context.lineTo(centerx, centery);
context.fill();
context.closePath();
var endagle = (Math.PI * 2)*0.95;
context.beginPath();
context.fillStyle = 'red';
context.arc(centerx, centery, radius-5, startagle, endagle, false);
context.lineTo(centerx, centery);
context.fill();
context.closePath();
var endagle = (Math.PI * 2)*0.5;
context.beginPath();
context.fillStyle = 'green';
context.arc(centerx, centery, radius-10, startagle, endagle, false);
context.lineTo(centerx, centery);
context.fill();
context.closePath();
var endagle = (Math.PI * 2)*0.7;
context.beginPath();
context.fillStyle = 'blue';
context.arc(centerx, centery, radius-15, startagle, endagle, false);
context.lineTo(centerx, centery);
context.fill();
context.closePath();
context.beginPath();
context.arc(centerx, centery, radius-20, 0, 360, false);
context.closePath();
context.clip();
const avatar = await Canvas.loadImage('https://i.imgur.com/lleYAsg.png');
context.drawImage(avatar, 10, 10, 250, 250);
Finalized/Fixed code
const Canvas = require('canvas');
var canvas = Canvas.createCanvas(700, 250);
var context = canvas.getContext('2d');
const background = await Canvas.loadImage('./images/photo-1538370965046-79c0d6907d47.jpg');
context.drawImage(background, 0, 0, canvas.width, canvas.height);
context.strokeStyle = '#0099ff';
context.strokeRect(0, 0, canvas.width, canvas.height);
var answers = [
"Red Ranger",
"Blue Ranger",
"Yellow Ranger",
"Pink Ranger",
"Green Ranger",
"Black Ranger",
"Orange Ranger",
"Violet Ranger",
"White Ranger",
"Silver Ranger",
"Gold Ranger",
"Bronze Ranger",
"Brown Ranger",
"Extra Hero",
"Meta Hero",
"Rider",
"Ultra",
"Kaiju"
];
var stats = [
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99),
Math.floor(Math.random() * 99)
];
var toonnamelist = [
"Evelyn Leon",
"Salvador Knight",
"Alyson Hensley",
"Tobias Harvey",
"Selah Frazier",
"Morgan Ward",
"Ronald Shepherd",
"Miriam Moody",
"Maren Gallagher",
"Alexia Crawford",
"Aliya Weiss",
"Jan Garrison"
];
var toonavatars = [
"https://i.imgur.com/lleYAsg.png",
"https://i.imgur.com/0x008b.png",
"https://i.imgur.com/ESPP3b.png",
"https://i.imgur.com/qv7cvb.png",
"https://i.imgur.com/VuYRAb.png",
"https://i.imgur.com/RB6lSb.png",
"https://i.imgur.com/hMlrIb.png",
"https://i.imgur.com/mLuRIb.png",
"https://i.imgur.com/QVQ7mb.png",
"https://i.imgur.com/IXEuqb.png",
"https://i.imgur.com/CgMlpb.png",
"https://i.imgur.com/vO008b.png"
];
var randomrank = answers[Math.floor(Math.random() * answers.length)];
var randomavy = toonavatars[Math.floor(Math.random() * toonavatars.length)];
var toonname = `${msg.member.displayName}`;
toonname = toonnamelist[Math.floor(Math.random() * toonnamelist.length)];;
var toonlevel = Math.floor(Math.random() * 99);
var toonrank = randomrank;
var curentexp = Math.floor(Math.random() * 9999);
var maxexp = Math.floor(Math.random() * 9999);
if (curentexp > maxexp){curentexp = maxexp;}
const cFont = context.font;
var fontArgs = context.font.split(' ');
var newSize = '30px';
context.font = newSize + ' ' + fontArgs[fontArgs.length - 1];
context.fillText(toonname, 250, 50);
fontArgs = context.font.split(' ');
newSize = '20px';
context.font = newSize + ' ' + fontArgs[fontArgs.length - 1];
context.fillText(`Level: `+toonlevel, 250, 80);
context.fillText(`Rank: `+toonrank, 350, 80);
context.font = '30px monospace';
context.fillText(`S:`+stats[0], 250, 170);
context.fillText(`P:`+stats[1], 355, 170);
context.fillText(`E:`+stats[2], 450, 170);
context.fillText(`W:`+stats[3], 250, 200);
context.fillText(`I:`+stats[4], 355, 200);
context.fillText(`L:`+stats[5], 450, 200);
context.fillText(`A:`+stats[6], 250, 230);
context.fillText(`F:`+stats[7], 355, 230);
context.fillText(`R:`+stats[8], 450, 230);
var recx = 250;
var recy = 90;
var recw = 300;
var rech = 40;
context.fillStyle = 'grey';
context.strokeStyle = 'silver';
context.lineWidth = '3';
context.beginPath();
context.fillRect(recx, recy, recw*(curentexp/maxexp), rech);
context.strokeRect(recx, recy, recw, rech);
context.fillStyle = 'white';
context.font = '20px sans-serif';
context.fillText('XP: '+curentexp+'/'+maxexp, 260, recy+28);
context.closePath();
let strokeWidth = 5;
var radius = 250 / 2 - strokeWidth;
let center = {
x: 250 / 2,
y: 250 / 2,
};
//var centerX = 250 / 2;
//var centerY = 250 / 2;
//const fullCircle = Math.PI * 2;
//const startAngle = Math.PI * 1.5;
//let endAngle;
let sectors = [
{ color: 'white', size: 0.1 },
{ color: 'crimson', size: 0.95 },
{ color: 'springgreen', size: 0.5 },
{ color: 'dodgerblue', size: 0.7 },
];
function drawSector({ context, color, center, radius, size }) {
let startAngle = Math.PI * 1.5;
let endAngle = startAngle + Math.PI * 2 * size;
context.beginPath();
context.fillStyle = color;
context.arc(center.x, center.y, radius, startAngle, endAngle);
context.lineTo(center.x, center.y);
context.fill();
context.closePath();
}
sectors.forEach(({ color, size }, i) => drawSector({
context, color, center, radius: radius - i * strokeWidth, size
}));
const conditionx = 575;
const conditiony = 5;
const conditionw = 125;
const conditionh = 250;
var conditionpic1 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Base/NekollxComm_BaseV2ArmS1_White.png');
var conditionpic2 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Base/NekollxComm_BaseV2ArmS2_White.png');
var conditionpic3 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Base/NekollxComm_BaseV2Head_White.png');
var conditionpic4 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Base/NekollxComm_BaseV2Torso_White.png');
var conditionpic5 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Base/NekollxComm_BaseV2LegS1_White.png');
var conditionpic6 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Base/NekollxComm_BaseV2LegS2_White.png');
var conditionpic3 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Adjustments/CatPeople/NekollxComm2_CatHead_F_White.png');
var conditionpic7 = await Canvas.loadImage('https://maskedriders.info/Mee6RP/PaperDoll/White_Adjustments/CatPeople/NekollxComm2_CatTail_F_White.png');
context.drawImage(conditionpic1, conditionx, conditiony, conditionw, conditionh);
context.drawImage(conditionpic2, conditionx, conditiony, conditionw, conditionh);
context.drawImage(conditionpic3, conditionx, conditiony, conditionw, conditionh);
context.drawImage(conditionpic4, conditionx, conditiony, conditionw, conditionh);
context.drawImage(conditionpic5, conditionx, conditiony, conditionw, conditionh);
context.drawImage(conditionpic6, conditionx, conditiony, conditionw, conditionh);
context.drawImage(conditionpic7, conditionx, conditiony, conditionw, conditionh);
context.beginPath();
context.arc(center.x, center.y, radius - sectors.length * strokeWidth, 0, 360, false);
context.closePath();
context.clip();
const avatar = await Canvas.loadImage(randomavy);
context.drawImage(avatar, 10, 10, 250, 250);
It's because you forgot that you changed the starting angle. You need to make the angle, at which the arc ends, to be relative to the starting angle. So if you increase the value by the starting value it will work as expected:
// ** For this demo only ** //
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
canvas.height = 250;
canvas.width = 250;
context.fillStyle = 'dimgray';
context.fillRect(0, 0, canvas.width, canvas.height);
// ** End ** //
const radius = canvas.width / 2 - 5;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const fullCircle = Math.PI * 2;
const startAngle = Math.PI * 1.5;
let endAngle;
endAngle = startAngle + fullCircle * 0.1;
context.beginPath();
context.fillStyle = 'white';
context.arc(centerX, centerY, radius, startAngle, endAngle, false);
context.lineTo(centerX, centerY);
context.fill();
context.closePath();
endAngle = startAngle + fullCircle * 0.95;
context.beginPath();
context.fillStyle = 'crimson';
context.arc(centerX, centerY, radius - 5, startAngle, endAngle, false);
context.lineTo(centerX, centerY);
context.fill();
context.closePath();
endAngle = startAngle + fullCircle * 0.5;
context.beginPath();
context.fillStyle = 'springgreen';
context.arc(centerX, centerY, radius - 10, startAngle, endAngle, false);
context.lineTo(centerX, centerY);
context.fill();
context.closePath();
endAngle = startAngle + fullCircle * 0.7;
context.beginPath();
context.fillStyle = 'dodgerblue';
context.arc(centerX, centerY, radius - 15, startAngle, endAngle, false);
context.lineTo(centerX, centerY);
context.fill();
context.closePath();
context.beginPath();
context.arc(centerX, centerY, radius - 20, 0, 360, false);
context.closePath();
context.clip();
// ** For this demo only ** //
let url = 'https://i.imgur.com/lleYAsg.png';
let img = new Image();
new Promise((resolve) => (img.onload = resolve), (img.src = url)).then(() =>
context.drawImage(img, 10, 10, 250, 250),
);
body {
align-items: center;
background-color: dimgray;
display: flex;
justify-content: center;
margin: 0;
min-height: 100vh;
}
<canvas></canvas>
It works as expected; however, you could also simplify the way you create the sectors. I would write a function to draw these (drawSector) and store the colours and sizes in an array (sectors).
// ** For this demo only ** //
let canvas = document.querySelector('canvas');
let context = canvas.getContext('2d');
canvas.height = 250;
canvas.width = 250;
context.fillStyle = '#232425';
context.fillRect(0, 0, canvas.width, canvas.height);
// ** End ** //
let center = {
x: canvas.width / 2,
y: canvas.height / 2,
};
let strokeWidth = 5;
let radius = canvas.width / 2 - strokeWidth;
let sectors = [
{ color: 'white', size: 0.1 },
{ color: 'crimson', size: 0.95 },
{ color: 'springgreen', size: 0.5 },
{ color: 'dodgerblue', size: 0.7 },
];
function drawSector({ context, color, center, radius, size }) {
let startAngle = Math.PI * 1.5;
let endAngle = startAngle + Math.PI * 2 * size;
context.beginPath();
context.fillStyle = color;
context.arc(center.x, center.y, radius, startAngle, endAngle);
context.lineTo(center.x, center.y);
context.fill();
context.closePath();
}
sectors.forEach(({ color, size }, i) => drawSector({
context, color, center, radius: radius - i * strokeWidth, size
}));
context.beginPath();
context.arc(center.x, center.y, radius - sectors.length * strokeWidth, 0, 360);
context.closePath();
context.clip();
// ** For this demo only ** //
let url = 'https://i.imgur.com/lleYAsg.png';
let img = new Image();
new Promise((resolve) => (img.onload = resolve), (img.src = url)).then(() =>
context.drawImage(img, 10, 10, 250, 250),
);
body {
align-items: center;
background-color: #232425;
display: flex;
justify-content: center;
margin: 0;
min-height: 100vh;
}
<canvas></canvas>

How can I make my sine wave in javascript work properly?

I am trying to make a sine wave on a canvas that would be filled with a certain color. I was successful with creating the wave animation. However, i can't seem to get the fill right. You can see the way my code works in the snippet below
const canvas = document.querySelector('canvas')
const gui = new dat.GUI()
const c = canvas.getContext('2d')
canvas.width = innerWidth
canvas.height = innerHeight
const wave = {
y: canvas.height / 2,
length: 0.01,
amplitude: 150,
frequency: 0.01
}
const strokeColor = {
h: 200,
s: 50,
l: 50
}
const backgroundColor = {
r: 255,
g: 50,
b: 255,
a: 1
}
const waveFolder = gui.addFolder('wave')
waveFolder.add(wave, 'y', 0, canvas.height)
waveFolder.add(wave, 'length', -0.01, 0.01)
waveFolder.add(wave, 'amplitude', -300, 300)
waveFolder.add(wave, 'frequency', -0.01, 1)
const strokeFolder = gui.addFolder('stroke')
strokeFolder.add(strokeColor, 'h', 0, 255)
strokeFolder.add(strokeColor, 's', 0, 100)
strokeFolder.add(strokeColor, 'l', 0, 100)
const backgroundFolder = gui.addFolder('background')
backgroundFolder.add(backgroundColor, 'r', 0, 255)
backgroundFolder.add(backgroundColor, 'g', 0, 255)
backgroundFolder.add(backgroundColor, 'b', 0, 255)
backgroundFolder.add(backgroundColor, 'a', 0, 1)
let increment = 0
function drawSineWave() {
c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},${backgroundColor.a})`
c.clearRect(0, 0, canvas.width, canvas.height)
increment++
c.beginPath()
c.moveTo(0, canvas.height / 2)
c.lineTo(0, canvas.height / 2)
c.lineTo(0, canvas.height)
c.lineTo(canvas.width, canvas.height)
c.lineTo(canvas.width, canvas.height / 2)
c.moveTo(0, canvas.height / 2)
for (let i = 0; i < canvas.width; i++) {
c.lineTo(i, wave.y + Math.sin(increment / 50) * wave.amplitude * Math.sin(i * wave.length + wave.frequency))
}
c.lineTo(canvas.width, canvas.height / 2)
c.strokeStyle = `hsl(${strokeColor.h},${strokeColor.s}%,${strokeColor.l}%)`
c.fill()
c.stroke()
}
function animate() {
requestAnimationFrame(animate)
drawSineWave()
}
animate()
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
canvas {
width: 100%;
height: 100%;
}
<head>
<title>Waves</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<canvas></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
<script src="index.js"></script>
</body>
The way I want the wave to look is this:
Can someone please help me achieve the desired result? Thanks!
You just need to draw the bottom half in the correct order
The result is
function drawSineWave() {
c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},${backgroundColor.a})`
c.clearRect(0, 0, canvas.width, canvas.height)
increment++
c.beginPath()
for (let i = 0; i < canvas.width; i++) {
c.lineTo(i, wave.y + Math.sin(increment / 50) * wave.amplitude * Math.sin(i * wave.length + wave.frequency))
}
c.strokeStyle = `hsl(${strokeColor.h},${strokeColor.s}%,${strokeColor.l}%)`
c.stroke(); // draw stroke along wave top only
c.lineTo(canvas.width, canvas.height) // bottom right
c.lineTo(0, canvas.height) // bottom left
c.fill()
}
Example
I played about with it a bit.
const canvas = document.querySelector('canvas')
const gui = new dat.GUI()
const c = canvas.getContext('2d')
canvas.width = innerWidth
canvas.height = innerHeight
const wave = {
offset: canvas.height/2,
speed: 1,
amplitude:50,
frequency: 1,
}
var disFreq = wave.frequency, disFreqChase = 0;
var disAmp = wave.amplitude, disAmpChase = 0;
var disY = wave.offset, disYChase = 0;
var disSp = wave.speed, disSpChase = 0;
const strokeColor = {width: 2, h:200,s:50, l:50 }
const backgroundColor = { r:255, g:50, b:255, a:1 }
const waveFolder = gui.addFolder('wave')
waveFolder.add(wave,'speed',-1,1); // peeks per second
waveFolder.add(wave,'offset',0,canvas.height);
waveFolder.add(wave,'amplitude',-100,100)
waveFolder.add(wave,'frequency',0.5,10); // frequency also defines wave length
// Value is fraction of canvas width
const strokeFolder = gui.addFolder('stroke')
strokeFolder.add(strokeColor,'width',0,18)
strokeFolder.add(strokeColor,'h',0,255)
strokeFolder.add(strokeColor,'s',0,100)
strokeFolder.add(strokeColor,'l',0,100)
const backgroundFolder = gui.addFolder('background')
backgroundFolder.add(backgroundColor,'r',0,255)
backgroundFolder.add(backgroundColor,'g',0,255)
backgroundFolder.add(backgroundColor,'b',0,255)
backgroundFolder.add(backgroundColor,'a',0,1)
let increment = 0
function drawSineWave() {
// smooth out changes
disFreq += disFreqChase = (disFreqChase += (wave.frequency - disFreq)*0.2)* 0.2;
disAmp += disAmpChase = (disAmpChase += (wave.amplitude - disAmp) * 0.2) * 0.2;
disY += disYChase = (disYChase += (wave.offset - disY) * 0.2) * 0.2;
disSp += disSpChase = (disSpChase += (wave.speed - disSp) * 0.2) * 0.2;
c.lineWidth = strokeColor.width;
c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},${backgroundColor.a})`
c.clearRect(0,0,canvas.width,canvas.height)
//increment++
increment += (disSp / 60);
c.beginPath()
const h = canvas.height;
for (let i=0;i<canvas.width;i++){
const a = (disAmp / 200) * h;
const x = ((i / canvas.width) * disFreq) % 1 + increment;
const p = x * Math.PI * 2;
c.lineTo(i, disY + Math.sin(p)*a);
}
c.strokeStyle = `hsl(${strokeColor.h},${strokeColor.s}%,${strokeColor.l}%)`
c.stroke()
c.lineTo(canvas.width,canvas.height)
c.lineTo(0, canvas.height)
c.fill()
}
function animate(){
requestAnimationFrame(animate)
drawSineWave()
}
animate()
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
canvas{
width: 100%;
height: 100%;
}
<canvas></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
<script src="index.js"></script>
</body>

Align circles and Balls are not bouncing

I am trying to place a big circle in the center of the screen and four other circles on the right side. How can I achieve this? Also, there should be balls bouncing inside the small circles on the right, and only one ball is inside each of them instead of several. An alert should be triggered by the big main circle. From the small circles, there should be no alert. How can I solve this problem? I am thankful for any help. Thanks in advance.
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;
//Adding fourcircles to the right
const ctx1 = document.getElementById("Canvas1").getContext("2d");
const ctx2 = document.getElementById("Canvas2").getContext("2d");
const ctx3 = document.getElementById("Canvas3").getContext("2d");
const ctx4 = document.getElementById("Canvas4").getContext("2d");
const containerR2 = 80;
const size2 = containerR2 * 2
ctx1.canvas.width = ctx1.canvas.height = size2;
ctx2.canvas.width = ctx2.canvas.height = size2;
ctx3.canvas.width = ctx3.canvas.height = size2;
ctx4.canvas.width = ctx4.canvas.height = size2;
ctx1.globalAlpha = 0.8;
ctx2.globalAlpha = 0.8;
ctx3.globalAlpha = 0.8;
ctx4.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, 0.1, 0.1, 8, "Green"),
getBall(size / 3, size - 50, 0.1, 0.1, 8, "Green"),
getBall(size / 4, size - 60, 0.1, 0.1, 8, "Green"),
getBall(size / 2, size / 5, 0.1, 0.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();
ctx1.beginPath();
ctx1.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx1.fillStyle = ball.collider ? "red" : ball.color;
ctx1.fill();
ctx1.closePath();
ctx2.beginPath();
ctx2.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx2.fillStyle = ball.collider ? "red" : ball.color;
ctx2.fill();
ctx2.closePath();
ctx3.beginPath();
ctx3.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx3.fillStyle = ball.collider ? "red" : ball.color;
ctx3.fill();
ctx3.closePath();
ctx4.beginPath();
ctx4.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, false);
ctx4.fillStyle = ball.collider ? "red" : ball.color;
ctx4.fill();
ctx4.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);
ctx1.clearRect(0, 0, ctx1.canvas.width, ctx1.canvas.height);
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx3.clearRect(0, 0, ctx3.canvas.width, ctx3.canvas.height);
ctx4.clearRect(0, 0, ctx4.canvas.width, ctx4.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('ALERT!!!!! Not Maintaining Distance')
.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;
}
.row {
display: flex;
}
.container1 {
display: flex;
flex-direction: column;
margin-left: 70%;
margin-top: 8%;
float: right;
}
#Canvas1, #Canvas2, #Canvas3, #Canvas4 {
background: #eee;
border-radius: 50%;
border: solid 1px #000;
margin: 4px;
}
<div class="column">
<canvas id="Canvas"></canvas>
</div>
<div class="container1">
<div class="row">
<canvas id="Canvas1"></canvas>
<div><p>abc</p></div>
<canvas id="Canvas2"></canvas>
<div><p>def</p></div>
</div>
<div class="row">
<canvas id="Canvas3"></canvas>
<div><p>hij</p></div>
<canvas id="Canvas4"></canvas>
<div><p>klm</p></div>
</div>
</div>
<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>
There are a lot of errors in the provided code:
beep() fonction is not defined, it prevents the alert system to work.
there is only one list of the same 4 balls for the 5 canvas, so you see the same 4 balls in all canvas.
updatePos uses containerR that is a global variable (radius of the big circle), calculations can only be done for the big circle.
drawBall draws the same single ball in the 5 contexts
Fixed code, with random initial positions for balls :
function PromptDialog(dialog) {
this.el = document.getElementById(dialog);
this.messageEl = this.el.querySelector('.message');
this.okButton = this.el.querySelector('button.ok');
this.attachDomEvents();
}
PromptDialog.prototype.attachDomEvents = function() {
this.okButton.addEventListener('click', () => {
this.hide();
//console.log('Ok clicked!!');
});
};
PromptDialog.prototype.show = function(message) {
this.messageEl.innerHTML = '' + message;
this.el.className = '';
};
PromptDialog.prototype.hide = function() {
this.el.className = 'hidden';
};
var prompt = new PromptDialog('dialog');
const getBall = (x, y, dx, dy, r, color) => ({x, y, dx, dy, r, color});
const drawBall = (ball, ctx) => {
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, containerR) => {
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);
}
}
function makeArea(domid, radius, ballsNumber, alerts) {
const ctx = document.getElementById(domid).getContext("2d");
const containerR = radius;
const size = radius * 2
ctx.canvas.width = ctx.canvas.height = size;
ctx.globalAlpha = 0.8;
const balls = [];
const speed = 0.1;
for(var i=0 ; i<ballsNumber ; ++i) {
const r = Math.random()*radius*0.5;
const t = Math.random()*Math.PI*2;
const t2 = Math.random()*Math.PI*2;
balls.push(
getBall(
radius + Math.cos(t)*r,
radius + Math.sin(t)*r,
Math.cos(t2)*speed,
Math.sin(t2)*speed,
8,
"Green")
);
}
return {
ctx: ctx,
radius: radius,
balls: balls,
alerts:alerts
}
}
const collides = (a, b) => (Math.hypot(Math.abs(a.x - b.x), Math.abs(a.y - b.y)) < (a.r + b.r));
const areas = [
makeArea("Canvas", 150, 4, true),
makeArea("Canvas1", 80, 4, false),
makeArea("Canvas2", 80, 4, false),
makeArea("Canvas3", 80, 4, false),
makeArea("Canvas4", 80, 4, false)
];
function engine() {
//console.clear(); // Clear console test messages
areas.forEach((area) =>{
const ctx = area.ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
area.balls.forEach((a, ai) => {
a.collider = undefined;
area.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 && area.alerts) { // If ball has a collider:
//beep();
prompt.show('ALERT!!!!! Not Maintaining Distance');
//console.log(`${a.color[0]} → ← ${a.collider.color[0]}`);
}
updatePos(a, area.radius);
drawBall(a, ctx);
});
});
requestAnimationFrame(engine);
}
engine();
canvas {
background: #eee;
margin: 0 auto;
border-radius: 50%;
box-shadow: 0 0 0 4px #000;
}
.row {
display: flex;
}
.hidden {
display:none;
}
#Canvas1, #Canvas2, #Canvas3, #Canvas4 {
background: #eee;
border-radius: 50%;
border: solid 1px #000;
margin: 4px;
}
<div class="row">
<div>
<canvas id="Canvas"></canvas>
</div>
<div>
<div class="row">
<canvas id="Canvas1"></canvas>
<div><p>abc</p></div>
<canvas id="Canvas2"></canvas>
<div><p>def</p></div>
</div>
<div class="row">
<canvas id="Canvas3"></canvas>
<div><p>hij</p></div>
<canvas id="Canvas4"></canvas>
<div><p>klm</p></div>
</div>
</div>
</div>
<div id="dialog" class="hidden">
<div class="message"></div>
<div>
<button class="ok">OK</button>
</div>
</div>

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>
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>
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>

Categories