在javascript/typescript中绘制形状并填充颜色
我正在用typescript/javascript在画布上绘制图像。 我得到的坐标来自直线和圆弧,我从数据库中读取这些数据(直线和圆弧的顺序是随机的)。 这幅画画得很好,但我需要用颜色填充画出的形状。使用moveTo和lineTo执行此操作,然后调用fill不起作用。因为直线和圆弧的随机顺序。第二,因为弧线也可以向内,所以它给“房间”的外面涂上了颜色 示例图片: 我的绘图代码片段:在javascript/typescript中绘制形状并填充颜色,javascript,typescript,canvas,graphics,drawing,Javascript,Typescript,Canvas,Graphics,Drawing,我正在用typescript/javascript在画布上绘制图像。 我得到的坐标来自直线和圆弧,我从数据库中读取这些数据(直线和圆弧的顺序是随机的)。 这幅画画得很好,但我需要用颜色填充画出的形状。使用moveTo和lineTo执行此操作,然后调用fill不起作用。因为直线和圆弧的随机顺序。第二,因为弧线也可以向内,所以它给“房间”的外面涂上了颜色 示例图片: 我的绘图代码片段: let canvas = <HTMLCanvasElement>document.getEl
let canvas = <HTMLCanvasElement>document.getElementById("canvas-view");
canvas.setAttribute('width', '500');
canvas.setAttribute('height', '500');
let ctx = canvas.getContext("2d");
if (ctx != null)
{
for (let count = 0; count < image.lines.length; count++)
{
ctx.beginPath();
ctx.moveTo(image.lines[count].startPoint.x, image.lines[count].startPoint.y);
ctx.lineTo(image.lines[count].endPoint.x, image.lines[count].endPoint.y);
ctx.stroke();
}
for (let count = 0; count < image.arcs.length; count++)
{
ctx.beginPath();
ctx.arc(image.arcs[count].center.x, image.arcs[count].center.y, image.arcs[count].radius, this.DegreesToRadians(image.arcs[count].startAngle), this.DegreesToRadians(image.arcs[count].endAngle));
ctx.stroke();
}
}
let canvas=document.getElementById(“画布视图”);
canvas.setAttribute('width','500');
canvas.setAttribute('height','500');
设ctx=canvas.getContext(“2d”);
如果(ctx!=null)
{
for(让count=0;count
有没有办法给这个形状的内部上色?对随机线和弧进行排序
如何从直线和圆弧段的随机列表中创建连续路径,每条直线和圆弧的方向都是随机的
步骤1圆弧端点
要填充形状,您需要等待,直到拥有所有直线和圆弧。然后你必须对它们进行排序,这样它们才能形成一个连续的轮廓
为此,您需要起点和终点,这是直线的起点和终点,但不是圆弧的坐标,因此您需要计算它们
此函数用于向圆弧添加起点和终点
function arcEnds(arc){
const d2r = d => d * Math.PI / 180; // deg to rad
arc.startPoint = {
x : Math.cos(d2r(arc.startAngle)) * arc.radius + arc.center.x,
y : Math.sin(d2r(arc.startAngle)) * arc.radius + arc.center.y
}
arc.endPoint = {
x : Math.cos(d2r(arc.endAngle)) * arc.radius + arc.center.x,
y : Math.sin(d2r(arc.endAngle)) * arc.radius + arc.center.y
}
return arc;
}
步骤2创建单个阵列
要优化直线的构造,需要创建一个同时包含直线和圆弧的阵列。执行此操作时,还可以计算圆弧端点
function createSegmentArray(lines, arcs) {
return [...lines, ...arcs.map(arcEnds)]; // add lines and end point calculated arcs
}
步骤3创建匹配点的函数
由于在端点计算中可能会出现浮点错误,因此需要有一个公差,以便确定两点是否相等
有两种方法可以查看两点是否位于同一位置。我将使用距离的平方,因为它有点整洁
// 1 pixel tolerance
const isSame = (p1,p2) => (((p1.x - p2.x) ** 2) + ((p1.y - p2.y) ** 2)) < 1;
创建第二个数组以容纳已排序的段
const orderedSegs = [];
步骤5按匹配的终点或起点排序
然后从第一段开始,将其从segs阵列中移除,并找到具有匹配的结束或开始的段(我们还不知道方向)。如果未找到匹配的端点,则形状未闭合且无法填充,因此抛出一个错误以指示此情况。您必须添加一个catch处理程序。或者,您可能更愿意中断循环,只标记一个错误
您还需要反转方向错误的线段。对于直线,您只需交换端点,对于圆,您需要交换端点和端点角度,并添加标志以指示方向已反转
var current = segs.shift();
while(seg.length > 0){
let reverse; // if true the segment needs to be reversed
const index = segs.findIndex(seg => { // find segment with matching end or start
if(isSame(current.endPoint, seg.startPoint)){
reverse = false;
return true
}
if(isSame(current.endPoint, seg.endPoint)){
reverse = true;
return true
}
return false;
})
if(index === -1){ throw new Error("The shape is not closed and can not be filled") }
orderedSegs.push(current);
current = segs.splice(index,1)[0]; // get the connected seg
if(reverse){
if(current.center){ // is a circle
const t = current.endPoint;
current.endPoint = current.startPoint;
current.startPoint = t;
const t1 = current.endAngle;
current.endAngle= current.startAngle;
current.startAngle = t1;
current.reversed = true;
}else{
const t = current.endPoint;
current.endPoint = current.startPoint;
current.startPoint = t;
}
}
// loop until no more segments
}
// push the last seg onto the array
orderedSegs.push(current);
步骤6渲染结果。
现在,所有点都按连接顺序进行了渲染,但必须反转圆弧的方向
const d2r = d => d * Math.PI / 180; // deg to rad
var i;
ctx.beginPath();
// arcs have start points now so dont have to check type for first point
ctx.moveTo(orderedSegs[0].startPoint.x, orderedSegs[0].startPoint.y);
for(i = 0; i < orderedSegs.length; i++){
var seg = orderedSegs[i];
if(seg.center){ // is a arc
ctx.arc(
seg.center.x, seg.center.y, seg.radius,
d2r(seg.startAngle), d2r(seg.endAngle), seg.reverse
);
}else{
ctx.lineTo(seg.endPoint.x, seg.endPoint.y);
}
}
ctx.fill();
ctx.stroke();
constd2r=d=>d*Math.PI/180;//度至拉德
var i;
ctx.beginPath();
//圆弧现在有起点,所以不必检查第一个点的类型
ctx.moveTo(orderedSegs[0].startPoint.x,orderedSegs[0].startPoint.y);
对于(i=0;i
这就是过程
警告
orderedSegs
数组中第一段的起点是否与isSame
数组中最后一段的终点相同arcEnds
函数中执行此操作 const d2r = d => d * Math.PI / 180; // deg to rad
var i;
ctx.beginPath();
// arcs have start points now so dont have to check type for first point
ctx.moveTo(orderedSegs[0].startPoint.x, orderedSegs[0].startPoint.y);
for(i = 0; i < orderedSegs.length; i++){
var seg = orderedSegs[i];
if(seg.center){ // is a arc
ctx.arc(
seg.center.x, seg.center.y, seg.radius,
d2r(seg.startAngle), d2r(seg.endAngle), seg.reverse
);
}else{
ctx.lineTo(seg.endPoint.x, seg.endPoint.y);
}
}
ctx.fill();
ctx.stroke();