How to animate the SVG path for an arc? - javascript

I want to animate SVG path based on approved answer - How to calculate the SVG Path for an arc (of a circle)
I copied code from that answer and added only setInterval function. But animation is wrong. My goal is to draw (animate) path as normal circle.
Fiddle - https://jsfiddle.net/alexcat/64w2hc31/
Thanks.
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function describeArc(x, y, radius, startAngle, endAngle){
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
return d;
}
var i = 0;
setInterval(function(){
i++
document.getElementById("arc1").setAttribute("d", describeArc(150, 150, 100, i, 270));
if (i === 360) {
i = 0;
}
}, 10);
svg {
height: 1000px;
width: 1000px;
}
<svg>
<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />
</svg>
<script src="https://d3js.org/d3.v3.min.js"></script>

there is kind of a problem in describeArc(150, 150, 100, i, 270) and change it to describeArc(150, 150, 100, 0, i) and call clearInterval when it's done.
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function describeArc(x, y, radius, startAngle, endAngle){
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
return d;
}
var i = 0;
var intervalId = setInterval(function(){
i++
document.getElementById("arc1").setAttribute("d", describeArc(150, 150, 100, 0, i));
if (i === 360) {
clearInterval(intervalId);
}
}, 10);
svg {
height: 1000px;
width: 1000px;
}
<svg>
<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />
</svg>
<script src="https://d3js.org/d3.v3.min.js"></script>

describeArc(150, 150, 100, i, 270) change this to describeArc(150, 150, 100, i, 360)
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function describeArc(x, y, radius, startAngle, endAngle){
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
return d;
}
var i = 0;
setInterval(function(){
i++
document.getElementById("arc1").setAttribute("d", describeArc(150, 150, 100, i, 360));
if (i === 360) {
i = 0;
}
}, 10);
svg {
height: 1000px;
width: 1000px;
}
<svg>
<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />
</svg>
<script src="https://d3js.org/d3.v3.min.js"></script>

Related

Using Angle in degrees to draw arrow in the direction in Javascript on Canvas

So I looked at the answer here: Draw arrow head in canvas using Angle
But it didn't seem to do it the way I wanted. I definitely do not want to use rotate, what I would like, is based on an Angle in degrees (0 being up on the screen, 180 being down, etc) is draw an arrow pointing in that direction.
Now I slept through trig in highschool so the correct usage of Rads, Sin and Cos are... well, they elude me :(.
Anyways, I have the angle already computed, and based on that I want to draw like the following:
The top one is at 0 degrees in my computation, the lower one 90 degrees.
I'm using a 2d canvas as my draw surface.
Inspired by the trigonometry of this answer I made a line + two smaller lines for the arrow part.
const size = 200;
var canvas = document.querySelector("canvas")
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, size, size);
ctx.strokeStyle = "red"
function lineToAngle(ctx, x1, y1, length, angle) {
angle = (angle - 90) * Math.PI / 180;
var x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.fill();
return {
x: x2,
y: y2
};
}
function draw_arrow(ctx, x1, y1, length, angle) {
var pos = lineToAngle(ctx, x1, y1, length, angle);
lineToAngle(ctx, pos.x, pos.y, 10, angle - 135);
lineToAngle(ctx, pos.x, pos.y, 10, angle + 135);
}
var pos = draw_arrow(ctx, 50, 50, 50, 30);
ctx.strokeStyle = "blue"
for (var angle = 0; angle <= 360; angle += 30) {
draw_arrow(ctx, 100, 100, 60, angle);
}
<canvas></canvas>
Overkill maybe?
This may help or may not.
Rather than create the arrow (shape) with a set of draw calls you can create a
Path2D to hold the shape with the helper function Path (in demo code below) that converts a set of arrays (each array a sub path) to a Path2D object. For example. const arrow = Path([[0, 0, 180, 0], [160, -10, 180, 0, 160, 10]]); a simple line arrow. To draw the path call ctx.stroke(arrow) or ctx.fill(arrow);
Define a style using Style eg const red = Style(...["#F00", , 2, "round"])
Position, rotate, and scale
Then the function drawPath(ctx, path, style, centerX, centerY, deg, scale) where...
ctx context to draw on.
path path to draw
style style to use,
centerX, centerY point to rotate around
deg angle in degrees from 0 deg pointing up, and positive values moving clockwize
scale lets you set the scale and defaults to 1.
Example use
// draw red arrow pointing up
const arrow = Path([[0, 0, 180, 0], [160, -10, 180, 0, 160, 10]]);
const red = Style(...["#F00", , 2, "round"]);
drawPath(ctx, arrow, red, 200, 200, 0);
Demo
Working example draw 6 arrows using 3 styles rotating around center of canvas.
const Path = (paths) => {
var j = 0, xx, yy, path = new Path2D();;
for (const subPath of paths) {
j = 0;
while (j < subPath.length) {
const [x, y] = [subPath[j++], subPath[j++]];
j === 2 ?
path.moveTo(...([xx, yy] = [x, y])) :
xx === x && yy === y ? path.closePath() : path.lineTo(x, y);
}
}
return path;
}
const Style = (strokeStyle, fillStyle, lineWidth, lineJoin = "bevel") => ({strokeStyle, fillStyle, lineWidth, lineJoin});
const styles = {
redLine: Style(...["#F00", , 6, "round"]),
greenFill: Style(...[ , "#0A0"]),
fillAndLine: Style(...["#000", "#0AF", 2, "round"]),
};
const paths = {
lineArrow: Path([ [10, 0, 180, 0], [160, -10, 180, 0, 160, 10] ]),
fatArrow: Path([ [10, -5, 180, -5, 170, -15, 200, 0, 170, 15, 180, 5, 10, 5, 10, -5] ]),
diamondArrow: Path([ [60, 0, 100, -15, 150, 0, 100, 15, 60, 0] ]),
};
requestAnimationFrame(mainLoop);
const [W, H, ctx] = [can.width, can.height, can.getContext("2d")];
const DEG2RAD = Math.PI / 180, DEG_0_OFFSET = -90;
function drawPath(ctx, path, style, centerX, centerY, deg, scale = 1) {
const rad = (deg + DEG_0_OFFSET) * DEG2RAD;
const [ax, ay] = [Math.cos(rad) * scale, Math.sin(rad) * scale];
ctx.setTransform(ax, ay, -ay, ax, centerX, centerY);
Object.assign(ctx, style);
style.fillStyle && ctx.fill(path);
style.strokeStyle && ctx.stroke(path);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
function mainLoop(time) {
ctx.clearRect(0, 0, W, H);
drawPath(ctx, paths.fatArrow, styles.fillAndLine, W * 0.5, H * 0.5, (time / 15000) * 360, 0.5);
drawPath(ctx, paths.fatArrow, styles.greenFill, W * 0.5, H * 0.5, (time / 20000) * 360);
drawPath(ctx, paths.diamondArrow, styles.fillAndLine, W * 0.5, H * 0.5, (time / 30000) * 360, 0.5);
drawPath(ctx, paths.diamondArrow, styles.greenFill, W * 0.5, H * 0.5, (time / 60000) * 360);
drawPath(ctx, paths.lineArrow, styles.redLine, W * 0.5, H * 0.5, (time / 5000) * 360, 0.9);
drawPath(ctx, paths.lineArrow, styles.redLine, W * 0.5, H * 0.5, (time / 10000) * 360);
requestAnimationFrame(mainLoop);
}
<canvas id="can" width="400" height="400"></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 do i make a canvas element face the mouse pointer

I'm currently learning about the html5 canvas, and I was making a program that rotates a triangle to face wherever the mouse is. it partially works but skips half of it, and i was wondering if there was a better way to do it and also what is wrong with my current code
thanks!
const ctx = document.getElementById("canvas").getContext("2d")
ctx.canvas.style.backgroundColor = "#303030"
ctx.canvas.width = 500
ctx.canvas.height = 500
const w = ctx.canvas.width
const h = ctx.canvas.height
let x = 0;
let y = 0;
let degrees = 0;
triAngle = 60
ctx.canvas.addEventListener("mousemove", mouseMove, false)
function mouseMove(evt) {
x = evt.clientX
y = evt.clientY
let diffX = x - w / 2;
let diffY = y - h / 2;
console.log(diffX, diffY)
degrees = Math.floor(Math.atan(diffY / diffX) * 57.2958);
//Math.atan(diffY/ diffX)
console.log(degrees)
}
function draw() {
debugger ;
ctx.clearRect(0, 0, w, h)
ctx.fillStyle = "#fff";
ctx.save()
ctx.translate(w / 2, h / 2)
ctx.rotate(degree(degrees + triAngle / 2))
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(0, 100)
ctx.rotate(degree(triAngle))
ctx.lineTo(0, 100)
ctx.closePath()
ctx.fill()
ctx.restore()
requestAnimationFrame(draw)
}
function degree(input) {
return Math.PI / 180 * input
}
draw()
https://jsfiddle.net/tus5nxpb/
Math.atan2
The reason that Math.atan skips half the directions is because of the sign of the fraction. The circle has 4 quadrants, the lines from {x: 0, y: 0} to {x: 1, y: 1}, {x: -1, y: 1}, {x: -1, y: -1} and {x: 1, y: -1} result in only two values (1, and -1) if you divide y by x eg 1/1 === 1, 1/-1 === -1, -1/1 === -1, and -1/-1 === 1 thus there is no way to know which one of the 2 quadrants each value 1 and -1 is in.
You can use Math.atan2 to get the angle from a point to another point in radians. In the range -Math.PI to Math.PI (-180deg to 180deg)
BTW there is no need to convert radians to deg as all math functions in JavaScript use radians
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d")
canvas.height = canvas.width = 300;
canvas.style.backgroundColor = "#303030";
const mouse = {x: 0, y: 0};
canvas.addEventListener("mousemove", e => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
const shape = {
color: "lime",
x: 150,
y: 150,
size: 50,
path: [1, 0, -0.5, 0.7, -0.5, -0.7],
};
function draw(shape) {
var i = 0;
const s = shape.size, p = shape.path;
ctx.fillStyle = shape.color;
const rot = Math.atan2(mouse.y - shape.y, mouse.x - shape.x);
const xa = Math.cos(rot);
const ya = Math.sin(rot);
ctx.setTransform(xa, ya, -ya, xa, shape.x, shape.y);
ctx.beginPath();
while (i < p.length) { ctx.lineTo(p[i++] * s, p[i++] * s) }
ctx.fill();
}
function mainLoop() {
ctx.setTransform(1, 0, 0, 1, 0, 0); // set default transform
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw(shape);
requestAnimationFrame(mainLoop);
}
body { margin: 0px; }
canvas { position: absolute; top: 0px; left: 0px; }
<canvas id="canvas"></canvas>

How to make a small triangle in a rectangle shape in canvas?

I want to add a small triangle similar to this example:
As you can see from the image there is a triangle in the middle of the side, I would like to have that in canvas.
This is what I have so far
// JavaScript for drawing on canvas
// applying colors + three triangles
function draw() {
// canvas with id="myCanvas"
var canvas = document.getElementById('myCanvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
roundRect(ctx, 100, 100, 180, 60, 10, true);
}
}
function roundRect(ctx, x, y, width, height, radius, fill, stroke) {
if (typeof stroke == "undefined") {
stroke = true;
}
if (typeof radius === "undefined") {
radius = 5;
}
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
if (stroke) {
ctx.stroke();
}
if (fill) {
ctx.fill();
}
}
draw();
<canvas id="myCanvas" width="700" height="410">
<p>Some default content can appear here.</p>
</canvas>
<p>Triangles!</p>
4 arcs for a rounded box plus a triangle to fit..
You can create a rounded box with just the rounded corners. Adding the triangle is just a matter of setting out the 3 points between drawing two corners.
The example draws two types of quote boxes, left and right.
The size, corner radius, triangle size and position are all set as arguments.
The animation is just to show the variations possible.
const ctx = canvas.getContext("2d");
// r is radius of corners in pixels
// quoteSize in pixels
// quotePos as fraction of avalible space 0-1
function roundedQuoteBoxLeft(x, y, w, h, r, quoteSize, quotePos) {
// draw 4 corners of box from top, left, top right , bottom right, bottom left
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
// make sure trianle fits
if (quoteSize > h - r * 2) { quoteSize = h - r * 2 }
// get triangle position
var qy = (h - (r * 2 + quoteSize)) * quotePos + r + y;
// draw triangle
ctx.lineTo(x, qy + quoteSize);
ctx.lineTo(x - quoteSize, qy + quoteSize / 2);
ctx.lineTo(x, qy);
// and add the last line back to start
ctx.closePath();
}
function roundedQuoteBoxRight(x, y, w, h, r, quoteSize, quotePos) {
// draw top arcs from left to right
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
// make sure trianle fits
if (quoteSize > h - r * 2) { quoteSize = h - r * 2 }
// get pos of triangle
var qy = (h - (r * 2 + quoteSize)) * quotePos + r + y;
// draw triangle
ctx.lineTo(x + w, qy);
ctx.lineTo(x + w + quoteSize, qy + quoteSize / 2);
ctx.lineTo(x + w, qy + quoteSize);
// draw remaining arcs
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
// and add the last line back to start
ctx.closePath();
}
function sin(time) {
return Math.sin(time) * 0.5 + 0.5;
}
requestAnimationFrame(drawIt)
function drawIt(time) {
ctx.clearRect(0, 0, 500, 250);
// get some sizes
var width = sin(time / 1000) * 100 + 100;
var height = sin(time / 1300) * 50 + 100;
var radius = sin(time / 900) * 20 + 5;
var x = sin(time / 1900) * 20 + 20;
var y = sin(time / 1400) * 50 + 10;
var quotePos = sin(time / 700)
var quoteSize = x - 10;
// set up box render
ctx.lineJoin = "round";
ctx.strokeStyle = "#8D4";
ctx.lineWidth = 4;
ctx.fillStyle = "#482";
// draw left quote box
ctx.beginPath();
roundedQuoteBoxLeft(x, y, width, height, radius, quoteSize, quotePos);
ctx.fill();
ctx.stroke()
x += width + 10;
width = 500 - x - quoteSize;
// draw right quote box
ctx.beginPath();
roundedQuoteBoxRight(x, y, width, height, radius, quoteSize, quotePos);
ctx.fill();
ctx.stroke()
/// animate
requestAnimationFrame(drawIt)
}
<canvas id="canvas" width="500" height="250"></canvas>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Your Canvas</title>
<style type="text/css"><!--
#container { position: relative; }
#imageTemp { position: absolute; top: 1px; left: 1px; }
--></style>
</head>
<body>
<canvas id="imageView" width="600" height="500"></canvas>
<script type="text/javascript">
var canvas, context, canvaso, contexto;
canvaso = document.getElementById('imageView');
context = canvaso.getContext('2d');
context.lineWidth = 5;
context.strokeStyle = '#000000';
context.beginPath();
context.moveTo(143, 224);
context.lineTo(168, 191);
context.stroke();
context.closePath();
context.strokeStyle = '#000000';
context.beginPath();
context.moveTo(143, 221);
context.lineTo(169, 247);
context.stroke();
context.closePath();
context.strokeStyle = '#000000';
context.strokeRect(168, 124, 242, 182);
</script>
</body>
</html>

Javascript unwanted lines connecting moving arcs

So i have this basic script which makes 4 circles move around a centre, and I have put in two circles which are the route which the circles follow.. The problem is that there's odd unwanted lines joining the circles to the stationary one's. I think it may be because the line for the circles ctx.arc(cx, cy, 200, 200, 0, 2 * Math.PI); is within the draw function. Does anyone know how to solve this? (Placing the stationary circles outside the draw function causes them to disappear when the canvas is cleared.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var w = canvas.width;
var h = canvas.height;
var dd = 3;
var dd2 = 3;
var dd3 = 3;
var dd4 = 3;
var angle = 0;
var angle2 = 0;
var angle3 = 0;
var angle4 = 0;
var cx = 1000;
var cy = 1000;
var radius = 200;
var radius2 = 300;
var radius3 = 400;
var radius4 = 500;
var fps = 100;
ctx.fillStyle = "yellow";
ctx.strokeStyle = "skyblue";
(function () {
"use strict";
function draw(x, y) {
ctx.save();
ctx.clearRect(0, 0, w, h);
ctx.beginPath();
ctx.arc(cx, cy, 75, 75, 0, 2 * Math.PI);
ctx.fillStyle = "lightgray";
ctx.arc((x - 50 / 2) + 25, (y - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.arc(cx, cy, 200, 200, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function draw1(x1, y1) {
ctx.save();
ctx.beginPath();
ctx.fillStyle = "orange";
ctx.arc((x1 - 50 / 2) + 25, (y1 - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.arc(cx, cy, 300, 300, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function draw2(x2, y2) {
ctx.save();
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.arc((x2 - 50 / 2) + 25, (y2 - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.arc(cx, cy, 400, 400, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function draw3(x3, y3) {
ctx.save();
ctx.beginPath();
ctx.fillStyle = "red";
ctx.arc((x3 - 50 / 2) + 25, (y3 - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.arc(cx, cy, 500, 500, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
window.requestAnimFrame = (function (callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / fps);
};
}());
function animate() {
setTimeout(function () {
requestAnimFrame(animate);
ctx.beginPath();
angle += Math.acos(1 - Math.pow(dd / radius, 2) / 2);
var newX = cx + radius * Math.cos(angle),
newY = cy + radius * Math.sin(angle),
newX1 = cx + radius2 * Math.cos(angle2),
newY1 = cy + radius2 * Math.sin(angle2),
newX2 = cx + radius3 * Math.cos(angle3),
newY2 = cy + radius3 * Math.sin(angle3),
newX3 = cx + radius4 * Math.cos(angle4),
newY3 = cy + radius4 * Math.sin(angle4);
draw(newX, newY);
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.closePath();
ctx.beginPath();
angle2 += Math.acos(1 - Math.pow(dd2 / radius2, 2) / 2);
draw1(newX1, newY1);
ctx.arc(cx, cy, radius2, 0, 2 * Math.PI);
ctx.closePath();
ctx.beginPath();
angle3 += Math.acos(1 - Math.pow(dd3 / radius3, 2) / 2);
draw2(newX2, newY2);
ctx.arc(cx, cy, radius3, 0, 2 * Math.PI);
ctx.closePath();
ctx.beginPath();
angle4 += Math.acos(1 - Math.pow(dd4 / radius4, 2) / 2);
draw3(newX3, newY3);
ctx.arc(cx, cy, radius4, 0, 2 * Math.PI);
ctx.closePath();
}, 1000 / fps);
}
animate();
}());
You have to use ctx.beginPath(), ctx.closePath() and ctx.stroke() for EVERY arc you create, else it will connect them with lines (as you yourself also would have to, if you were drawing and were not allowed to take the pencil off the paper):
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var w = canvas.width;
var h = canvas.height;
var dd = 3;
var dd2 = 3;
var dd3 = 3;
var dd4 = 3;
var angle = 0;
var angle2 = 0;
var angle3 = 0;
var angle4 = 0;
var cx = 1000;
var cy = 1000;
var radius = 200;
var radius2 = 300;
var radius3 = 400;
var radius4 = 500;
var fps = 100;
ctx.fillStyle = "yellow";
ctx.strokeStyle = "skyblue";
(function () {
"use strict";
function draw(x, y) {
ctx.save();
ctx.clearRect(0, 0, w, h);
ctx.beginPath();
ctx.arc(cx, cy, 75, 75, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "lightgray";
ctx.beginPath();
ctx.arc((x - 50 / 2) + 25, (y - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx, cy, 200, 200, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
function draw1(x1, y1) {
ctx.save();
ctx.fillStyle = "orange";
ctx.beginPath();
ctx.arc((x1 - 50 / 2) + 25, (y1 - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx, cy, 300, 300, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
function draw2(x2, y2) {
ctx.save();
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc((x2 - 50 / 2) + 25, (y2 - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx, cy, 400, 400, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
function draw3(x3, y3) {
ctx.save();
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc((x3 - 50 / 2) + 25, (y3 - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx, cy, 500, 500, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
window.requestAnimFrame = (function (callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / fps);
};
}());
function animate() {
setTimeout(function () {
requestAnimFrame(animate);
angle += Math.acos(1 - Math.pow(dd / radius, 2) / 2);
var newX = cx + radius * Math.cos(angle),
newY = cy + radius * Math.sin(angle),
newX1 = cx + radius2 * Math.cos(angle2),
newY1 = cy + radius2 * Math.sin(angle2),
newX2 = cx + radius3 * Math.cos(angle3),
newY2 = cy + radius3 * Math.sin(angle3),
newX3 = cx + radius4 * Math.cos(angle4),
newY3 = cy + radius4 * Math.sin(angle4);
draw(newX, newY);
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
angle2 += Math.acos(1 - Math.pow(dd2 / radius2, 2) / 2);
draw1(newX1, newY1);
ctx.arc(cx, cy, radius2, 0, 2 * Math.PI);
angle3 += Math.acos(1 - Math.pow(dd3 / radius3, 2) / 2);
draw2(newX2, newY2);
ctx.arc(cx, cy, radius3, 0, 2 * Math.PI);
angle4 += Math.acos(1 - Math.pow(dd4 / radius4, 2) / 2);
draw3(newX3, newY3);
ctx.arc(cx, cy, radius4, 0, 2 * Math.PI);
}, 1000 / fps);
}
animate();
}());
<canvas id="canvas" width="2000" height="2000"></canvas>
E.g. I changed the following:
ctx.beginPath();
ctx.arc(cx, cy, 75, 75, 0, 2 * Math.PI);
ctx.fillStyle = "lightgray";
ctx.arc((x - 50 / 2) + 25, (y - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.arc(cx, cy, 200, 200, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
Into this:
ctx.beginPath();
ctx.arc(cx, cy, 75, 75, 0, 2 * Math.PI);
ctx.closePath(); //Added
ctx.stroke(); //Added
ctx.fillStyle = "lightgray";
ctx.beginPath(); //Added
ctx.arc((x - 50 / 2) + 25, (y - 30 / 2) + 15, 25, 25, 0, 2 * Math.PI);
ctx.closePath(); //Added
ctx.stroke(); //Added
ctx.beginPath(); //Added
ctx.arc(cx, cy, 200, 200, 0, 2 * Math.PI);
ctx.closePath(); //Reversed order (doesn't really matter, but looks better IMO)
ctx.stroke(); //Reversed order (doesn't really matter, but looks better IMO)

Categories