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>
Related
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>
I want to display a windmill on js canvas. For now, I display a green line on a cube. I have a problem with rotating the line around it's middle point. Also, I don't want my cube to move. Save() doesn't seem to work? I don't know what I'm doing wrong. I tried looking the answer online but they don't seem to work or I don't understand them. Elements in my canvas somehow disappear.
var x = 600;
function init()
{
window.requestAnimationFrame(draw);
}
function draw()
{
var ctx = document.getElementById('canvas').getContext('2d');
ctx.clearRect(0, 0, 600, 600);
// sciana przednia
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.strokeRect(x/2,x/2,x/4,x/4);
//sciana gorna
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x/2,x/2);
ctx.lineTo(x-x/3,x/4+x/6);
ctx.lineTo(x-x/8,x/4+x/6);
ctx.lineTo(x/2+x/4,x/2);
ctx.closePath();
ctx.stroke();
//sciana prawa
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x/2+x/4,x/2+x/4);
ctx.lineTo(x-x/8,x/2+x/7);
ctx.lineTo(x-x/8,x/4+x/6);
ctx.stroke();
ctx.closePath();
//raczka
ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = "#808080";
ctx.moveTo(x/2+x/5,x/2-x/5);
ctx.lineTo(x/2+x/5,x/2-x/8+50);
ctx.stroke();
ctx.closePath();
ctx.save();
//smiglo
ctx.beginPath();
ctx.translate(x/2, x/2);
ctx.rotate( (Math.PI / 180) * 25);
ctx.translate(-x/2, -x/2);
ctx.fillStyle = "#00cc00";
ctx.fillRect(x/2+x/5-100,x/2-x/5,200,10);
ctx.closePath();
ctx.restore();
window.requestAnimationFrame(draw);
}
init();
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d');
var x = 600;
function init() {
window.requestAnimationFrame(draw);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// sciana przednia
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.strokeRect(x / 2, x / 2, x / 4, x / 4);
//sciana gorna
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x / 2, x / 2);
ctx.lineTo(x - x / 3, x / 4 + x / 6);
ctx.lineTo(x - x / 8, x / 4 + x / 6);
ctx.lineTo(x / 2 + x / 4, x / 2);
ctx.closePath();
ctx.stroke();
//sciana prawa
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x / 2 + x / 4, x / 2 + x / 4);
ctx.lineTo(x - x / 8, x / 2 + x / 7);
ctx.lineTo(x - x / 8, x / 4 + x / 6);
ctx.stroke();
ctx.closePath();
//raczka
ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = "#808080";
ctx.moveTo(x / 2 + x / 5, x / 2 - x / 5);
ctx.lineTo(x / 2 + x / 5, x / 2 - x / 8 + 50);
ctx.stroke();
ctx.closePath();
ctx.save();
//smiglo
ctx.beginPath();
ctx.translate(x / 2, x / 2);
ctx.rotate((Math.PI / 180) * 25);
ctx.translate(-x / 2, -x / 2);
ctx.fillStyle = "#00cc00";
ctx.fillRect(x / 2 + x / 5 - 100, x / 2 - x / 5, 200, 10);
ctx.closePath();
ctx.restore();
window.requestAnimationFrame(draw);
}
init();
<canvas id="canvas" width=600 height=600></canvas>
Edit: I understand now how to rotate around a certain point. I still don't know how to rotate only the line not the whole thing.
Talking about the rotation, I think that you did well: the green line is rotated by 25 degrees in your example. You just need to rotate its middle point.
But to do so, I think it's better if you make other changes in your code: the part that draws the cube is difficult to handle, whenever you want to edit your code, this part will cause issues. I suggest to isolate it in a drawCube() function and to use proper (x,y) coordinates.
According to me, it should look like this:
function draw() {
angle += 5;
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(Math.PI / 180 * angle);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
// It rotates
drawLine();
ctx.restore();
// It doesn't rotate
drawCube();
}
Then, your code will work. All you will have to do is to make the line rotate around its middle point, but you said that you know how to do it.
Good luck!
EDIT: I added a snippet with an working example, and with a cube like yours as well, may help.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var btnExample = document.getElementById('btnExample');
var btnCube = document.getElementById('btnCube');
var angle = 0;
var fps = 1000 / 60;
var propellerWidth = 100;
var propellerHeight = 10;
var towerWidth = 2;
var towerHeight = 100;
var mode = {
CUBE: 'CUBE',
EXAMPLE: 'EXAMPLE'
}
var currentMode = mode.EXAMPLE;
btnExample.onclick = function() {
currentMode = mode.EXAMPLE;
}
btnCube.onclick = function() {
currentMode = mode.CUBE;
}
setInterval(function() {
angle += 3;
draw(canvas, ctx, (canvas.width - propellerWidth) / 2, (canvas.height - propellerWidth) / 2, angle);
}, fps);
function draw(canvas, ctx, cx, cy, angle) {
var towerX = cx + propellerWidth / 2 - towerWidth / 2;
var towerY = cy + propellerWidth / 2;
var propellerX = (canvas.width - propellerWidth) / 2;
var propellerY = (canvas.height - propellerHeight) / 2;
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw other things that don't rotate
if (currentMode === mode.EXAMPLE) {
drawHelp(canvas, ctx);
}
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(Math.PI / 180 * angle);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
// Draw things that rotate
drawPropeller(ctx, propellerX, propellerY, propellerWidth, propellerHeight);
ctx.restore();
// Draw other things that don't rotate
if (currentMode === mode.EXAMPLE) {
drawTower(ctx, towerX, towerY, towerWidth, towerHeight);
} else if (currentMode === mode.CUBE) {
drawCube(ctx, towerX, towerY, 30);
}
}
function drawPropeller(ctx, propellerX, propellerY, propellerWidth, propellerHeight) {
ctx.fillStyle = 'black';
ctx.fillRect(propellerX, propellerY, propellerWidth, propellerHeight);
}
function drawTower(ctx, towerX, towerY, towerWidth, towerHeight) {
ctx.fillStyle = 'red';
ctx.fillRect(towerX, towerY, towerWidth, towerHeight);
}
function drawCube(ctx, x, y, size) {
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x, y + size);
ctx.closePath();
ctx.stroke();
var x1 = x - size + (size / 4) + (size / 16);
var y1 = y + (size * 1.25);
var x2 = x1 + (size / 3);
var y2 = y1 - (size * 2 / 3);
var x3 = x2 + size;
var y3 = y2;
var x4 = x3 - (size / 3);
var y4 = y1;
var x5 = x4;
var y5 = y4 + size;
var x6 = x3;
var y6 = y3 + size;
ctx.strokeRect(x1, y1, size, size);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x3, y3);
ctx.lineTo(x4, y4);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x3, y3);
ctx.lineTo(x6, y6);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x5, y5);
ctx.lineTo(x6, y6);
ctx.closePath();
ctx.stroke();
}
function drawHelp(canvas, ctx) {
ctx.globalAlpha = 0.2;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width, canvas.height);
ctx.moveTo(canvas.width, 0);
ctx.lineTo(0, canvas.height);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, propellerWidth / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, propellerWidth / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.globalAlpha = 1;
}
canvas {
border: 1px solid black;
}
<canvas id="canvas" width=200 height=200></canvas>
<br>
<button id="btnExample">Example</button>
<button id="btnCube">Cube</button>
I'd like to draw an ellipse given a cx and cy position-property and a width and height property of the ellipse itself.
Below you can find some working code for this setup:
But now I want to generate a kind of "progress display" by painting a percentage (from 0 to 100) of the ellipse instead of the complete ellipse.
I have attached a graphic here to illustrate the whole thing:
I don't really have a clear idea how to do that. I would prefer a solution where I can do without resizing the canvas - just for performance reasons and I hope someone has a good idea how to solve my problem.
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 280;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height)
let ellipse = function(cx, cy, w, h) {
let lx = cx - w / 2,
rx = cx + w / 2,
ty = cy - h / 2,
by = cy + h / 2;
let magic = 0.551784;
let xmagic = magic * w / 2,
ymagic = h * magic / 2;
let region = new Path2D();
region.moveTo(cx, ty);
region.bezierCurveTo(cx + xmagic, ty, rx, cy - ymagic, rx, cy);
region.bezierCurveTo(rx, cy + ymagic, cx + xmagic, by, cx, by);
region.bezierCurveTo(cx - xmagic, by, lx, cy + ymagic, lx, cy);
region.bezierCurveTo(lx, cy - ymagic, cx - xmagic, ty, cx, ty);
ctx.strokeStyle = "red";
ctx.lineWidth = "10";
region.closePath();
ctx.stroke(region);
}
ellipse(canvas.width / 2, canvas.height / 2, 300, 120)
<canvas id="canvas"></canvas>
You can use the built-in function ctx.ellipse - first we draw the green line as a full ellipse. Next, draw the red partial ellipse on top:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 280;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height)
function ellipse(ctx, color, x,y, w, h, thickness, angle) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.ellipse(canvas.width / 2, canvas.height / 2, h/2,w/2, Math.PI*3/2, 0, angle);
ctx.lineWidth = thickness;
ctx.stroke();
}
function ell(percent) {
let x= canvas.width / 2;
let y= canvas.height / 2;
let w=300;
let h=120;
let th = 10; // example thickness 10px
ellipse(ctx, '#608a32', x,y, w, h, th, Math.PI*2);
ellipse(ctx, '#ed3833', x,y , w, h, th+.3, 2*Math.PI*percent/100);
}
ell(90); // here we start draw for 90%
<canvas id="canvas"></canvas>
You can draw the ellipse with a bit of trigonometry
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 170;
let ellipse = function(cx, cy, ds, de, w, h, color) {
for (var i = ds; i < de; i ++) {
var angle = i * ((Math.PI * 2) / 360);
var x = Math.cos(angle) * w;
var y = Math.sin(angle) * h;
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(cx+ x, cy+y, 6, 0, 2 * Math.PI);
ctx.fill();
}
}
let draw = function(cx, cy, ds, de, w, h, color) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
delta += 10
if (delta > 350) delta = 40
hw = canvas.width / 2
hh = canvas.height / 2
ellipse(hw, hh, 0, 360, 150, 60, "red")
ellipse(hw, hh, 0, delta, 150, 60, "blue")
ctx.font = "80px Arial";
ctx.fillStyle = "green";
ctx.fillText(Math.round(delta/3.6) + "%", hw-70, hh+30);
}
delta = 90
setInterval(draw, 100)
<canvas id="canvas"></canvas>
Once you have a nice function you can animate it
I have drawn the curved line using below lines of code, I need to draw an arrowhead.For this I need to draw 2 lines wth some angle and rotate it some some angle. It is very confusing to draw. I am following the post present in the link provided for arrowhead.
.html
<canvas id = "canvas" width = "100px" height = "120px"></canvas>
.ts
arrow({ x: 10, y: 10 }, { x: 100, y: 140 }, 15); //function called on reload.
function arrow(p1, p2, size) {
var angle = Math.atan2((p2.y - p1.y), (p2.x - p1.x));
//curve line
ctx.strokeStyle = 'white';
ctx.beginPath();
ctx.lineWidth=3;
ctx.moveTo(40,0);
ctx.bezierCurveTo(30, 0, -70, 75, 100, 150);
ctx.lineTo(100,120)
ctx.stroke();
//to draw a triangle ??
}
I tried look a like
arrow({ x: 10, y: 10 }, { x: 100, y: 140 }, 15); //function called on reload.
function arrow(p1, p2, size) {
var angle = Math.atan2((p2.y - p1.y), (p2.x - p1.x));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//curve line
ctx.fillStyle = "";
ctx.fillRect(0,0,200,200);
ctx.strokeStyle = 'white';
ctx.beginPath();
ctx.lineWidth=3;
ctx.moveTo(40,20);
ctx.bezierCurveTo(30,40, -0,110, 100, 149.5);
ctx.moveTo(100,150.6);
ctx.lineTo(82,133);
ctx.stroke();
ctx.moveTo(100,149.7);
ctx.lineTo(76,146);
ctx.stroke();
//to draw a triangle ??
}
<canvas id = "canvas" width = "150px" height = "300px"></canvas>
You need some mathematics for this. This javascript method will draw a simple arrow head at the end of a line using canvas. It doesn't matter from which angle you come into the "arrow head".
//..come to the end of the line at some point
ctx.lineTo(tox, toy);
//draw the arrow head
ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 5), toy - headlen * Math.sin(angle + Math.PI / 5));
ctx.moveTo(tox, toy);
ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 5), toy - headlen * Math.sin(angle - Math.PI / 5));
I am trying to draw an annulus sector in QML using the Canvas object.
First, I have written the javascript code, and I have verified that it is correct by executing it in a browser.
Here it is:
var can = document.getElementById('myCanvas');
var ctx=can.getContext("2d");
var center = {
x: can.width / 2,
y: can.height / 2
};
var minRad = 100;
var maxRad = 250;
var startAngle = toRad(290);
var endAngle = toRad(310);
drawAxis();
drawSector();
function drawSector() {
var p1 = {
x: maxRad * Math.cos(startAngle),
y: maxRad * Math.sin(startAngle)
}
p1 = toCanvasSpace(p1);
var p2 = {
x: minRad * Math.cos(startAngle),
y: minRad * Math.sin(startAngle)
}
p2 = toCanvasSpace(p2);
var p3 = {
x: minRad * Math.cos(endAngle),
y: minRad * Math.sin(endAngle)
}
p3 = toCanvasSpace(p3);
var p4 = {
x: maxRad * Math.cos(endAngle),
y: maxRad * Math.sin(endAngle)
}
p4 = toCanvasSpace(p4);
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.arc(center.x, center.y, maxRad, startAngle, endAngle);
ctx.lineTo(p3.x, p3.y);
ctx.arc(center.x, center.y, minRad, endAngle, startAngle, true);
ctx.closePath();
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
}
function drawAxis() {
ctx.beginPath();
ctx.moveTo(can.width / 2, 0);
ctx.lineTo(can.width / 2, can.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, can.height / 2);
ctx.lineTo(can.width, can.height / 2);
ctx.stroke();
}
function toRad(degrees) {
return degrees * Math.PI / 180;
}
function toCanvasSpace(p) {
var ret = {};
ret.x = p.x + can.width / 2;
ret.y = p.y + can.height / 2;
return ret;
}
Here you can run the code above.
The output is this:
Next, I moved the same code into a Canvas object in Qml.
See here the main.qml containing the Canvas:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 500
height: 500
x:500
Canvas
{
id: can
anchors.fill: parent
antialiasing: true
onPaint: {
var ctx=can.getContext("2d");
var center = {
x: can.width / 2,
y: can.height / 2
};
var minRad = 100;
var maxRad = 250;
var startAngle = toRad(290);
var endAngle = toRad(310);
drawAxis();
drawSector();
function drawSector() {
var p1 = {
x: maxRad * Math.cos(startAngle),
y: maxRad * Math.sin(startAngle)
}
p1=toCanvasSpace(p1);
var p2 = {
x: minRad * Math.cos(startAngle),
y: minRad * Math.sin(startAngle)
}
p2=toCanvasSpace(p2);
var p3 = {
x: minRad * Math.cos(endAngle),
y: minRad * Math.sin(endAngle)
}
p3=toCanvasSpace(p3);
var p4 = {
x: maxRad * Math.cos(endAngle),
y: maxRad * Math.sin(endAngle)
}
p4=toCanvasSpace(p4);
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.arc(center.x, center.y, maxRad, startAngle, endAngle);
ctx.lineTo(p3.x, p3.y);
ctx.arc(center.x, center.y, minRad, endAngle, startAngle, true);
ctx.closePath();
ctx.strokeStyle="blue";
ctx.lineWidth=2;
ctx.stroke();
}
function drawAxis() {
ctx.beginPath();
ctx.moveTo(can.width / 2, 0);
ctx.lineTo(can.width / 2, can.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, can.height / 2);
ctx.lineTo(can.width, can.height / 2);
ctx.stroke();
}
function toRad(degrees) {
return degrees * Math.PI / 180;
}
function toCanvasSpace(p) {
var ret = {};
ret.x = p.x + can.width / 2;
ret.y = p.y + can.height / 2;
return ret;
}
}
}
}
In this case I get this output:
As you can see there is an imperfection at the bottom.
I really don't understand why there is that imperfection; moreover I don't understand why the same code gives different output.
Any help is appreciated!
Thanks
The lineTo p3 is not needed, because when an arc segment is drawn, the connecting line is drawn automatically, according to the Canvas specifications:
The arc() method is equivalent to the ellipse() method in the case
where the two radii are equal. [...]
When the ellipse() method is invoked, it must proceed as follows.
First, if the object's path has any subpaths, then the method must add
a straight line from the last point in the subpath to the start point
of the arc.
Also, the moveTo p1 is not needed, because it would be done as part of the first arc.
As to why the extra line is being drawn further than the start of the second arc, it could be a bug in Qt (maybe a division by 0 issue - just guessing here), or maybe you simply did not compute its position correctly.