带有圆角的html5画布三角形 [英] html5 canvas triangle with rounded corners

查看:59
本文介绍了带有圆角的html5画布三角形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是HTML5 Canvas的新手,我想绘制一个带有圆角的三角形。



我已经尝试

  ctx.lineJoin = round; 
ctx.lineWidth = 20;

但它们都不起作用。



这是我的代码:



  var ctx = document.querySelector( canvas ).getContext('2d'); ctx.scale(5,5); var x = 18/2; var y = 0; var triangleWidth = 18; var triangleHeight = 8; //如何对这个三角形取整?? ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo( x +三角形宽度/ 2,y +三角形高度); ctx.lineTo(x-三角形宽度/ 2,y +三角形高度); ctx.closePath(); ctx.fillStyle =#009688; ctx.fill(); ctx.fillStyle =#8BC34A; ctx.fillRect(0,triangleHeight,9,126); ctx.fillStyle =#CDDC39; ctx.fillRect(9,triangleHeight,9,126);  

 <画布width = 800 height = 600>< / canvas>  



您能帮我吗?

解决方案

圆角


倒圆角是我经常使用的无价函数。它需要一组描述多边形顶点的2D点,并添加圆弧以圆角化。


圆角化并保持在多边形区域约束范围内的问题是您不能总是适合具有特定半径的圆角。


在这种情况下,您可以忽略该角并将其保留为尖角,或者可以减小倒圆半径以最佳地适合该角


以下功能将调整拐角倒圆半径的大小,以适合拐角,如果拐角太尖锐并且拐角处的线不够长而无法获得所需的半径。

p>

注意如果您想知道发生了什么,代码中的注释将引用下面的数学部分。


roundedPoly (ctx,点,半径)


  // ctx是将路径添加到
的上下文//点是点数组[{x:?,y:?},...
//半径是最大舍入半径
//这会创建一个封闭的pol ygon。
//要绘制,必须在
之间调用// ctx.beginPath();
// roundedPoly(ctx,points,radius);
// ctx.stroke();
// ctx.fill();
//仅添加路径而不渲染。
函数roundedPoly(ctx,points,radiusAll){
var i,x,y,len,p1,p2,p3,v1,v2,sinA,sinA90,radDirection,drawDirection,angle,halfAngle,cRadius ,lenOut,radius;
//将2个点转换为矢量形式,极坐标形式和标准化的
var asVec = function(p,pp,v){
v.x = pp.x-p.x;
v.y = pp.y-p.y;
v.len = Math.sqrt(v.x * v.x + v.y * v.y);
v.nx = v.x / v.len;
v.ny = v.y / v.len;
v.ang = Math.atan2(v.ny,v.nx);
}
radius = radiusAll;
v1 = {};
v2 = {};
len = points.length;
p1 =积分[len-1];
// //每个点
for(i = 0; i< len; i ++){
p2 = points [(i)%len];
p3 = points [(i + 1)%len];
// -----------------------------------------
//第1部分
asVec(p2,p1,v1);
asVec(p2,p3,v2);
sinA = v1.nx * v2.ny-v1.ny * v2.nx;
sinA90 = v1.nx * v2.nx-v1.ny * -v2.ny;
angle = Math.asin(sinA< -1?-1:sinA> 1?1:sinA);
// -----------------------------------------
radDirection = 1;
drawDirection = false;
if(sinA90< 0){
if(angle< 0){
angle = Math.PI + angle;
} else {
angle = Math.PI-angle;
radDirection = -1;
drawDirection = true;
}
} else {
if(angle> 0){
radDirection = -1;
drawDirection = true;
}
}
if(p2.radius!== undefined){
radius = p2.radius;
} else {
radius = radiusAll;
}
// --------------------------------------- -
//第2部分
halfAngle =角度/ 2;
// -----------------------------------------

// -----------------------------------------
//第3部分
lenOut = Math.abs(Math.cos(halfAngle)* radius / Math.sin(halfAngle));
// -----------------------------------------

// -----------------------------------------
//特殊部分A
if(lenOut> Math.min(v1.len / 2,v2.len / 2)){
lenOut = Math.min(v1.len / 2, v2.len / 2);
cRadius = Math.abs(lenOut * Math.sin(halfAngle)/ Math.cos(halfAngle));
} else {
cRadius =半径;
}
// --------------------------------------- -
//第4部分
x = p2.x + v2.nx * lenOut;
y = p2.y + v2.ny * lenOut;
// -----------------------------------------
//第5部分
x + = -v2.ny * cRadius * radDirection;
y + = v2.nx * cRadius * radDirection;
// -----------------------------------------
//第6部分
ctx.arc(x,y,cRadius,v1.ang + Math.PI / 2 * radDirection,v2.ang-Math.PI / 2 * radDirection,drawDirection);
// -----------------------------------------
p1 = p2;
p2 = p3;
}
ctx.closePath();
}

您可能希望在每个点上添加半径,例如 {x: 10,y:10,radius:20} 会设置该点的最大半径。半径为零将不会四舍五入。


数学


以下错误说明了两种可能性之一,适合的角度小于90度,另一种情况(大于90)只有一些小的计算差异(请参见代码)。


该角由红色 A 中的三个点定义> B C 。圆半径为 r ,我们需要找到圆心的 F 绿点以及 D E 将定义圆弧的开始和结束角度。


首先,我们从 B,A B,C 找到直线之间的角度strong>这是通过对两条线的向量进行归一化并获得叉积来完成的。 注释为第1部分我们还找到了 BC 线与与 BA 成90度角的线的夹角,


现在我们有了线之间的角度,我们知道那个角度的一半定义了圆的中心坐在 F ,但我们不知道该点与 B 已被评论为第2部分 p>

有两个直角三角形 BDF BEF 。我们的角度为 B ,并且知道边 DF EF 等于圆的半径 r ,因此我们可以求解三角形以从 B


获取到 F 的距离F 解决了 BD 注释为第3部分的问题,因为我将沿着 BC 行进>保持该距离已注释为第4部分,然后旋转90度并向上移动至 F 已注释为第5部分在此过程中,给出点 D 并沿 BA 线移动到 E


我们使用点 D E 和圆心 F (以抽象形式)来计算起点和终点圆弧的末端角度。 在arc函数的第6部分中完成


其余的代码与移动方向有关。


代码段(特殊部分A )使用两条线的长度 BA BC ,并将其与距 BD 的距离进行比较(如果该距离大于线长的一半),我们知道圆弧无法容纳。然后,如果线 BD BA BC <的最短线的长度的一半,则我求解三角形以找到半径 DF / strong>


示例用法。


该代码段是上述功能的一个简单示例。单击以将点添加到画布(至少需要3个点才能创建多边形)。您可以拖动点并查看拐角半径如何适应尖角或短线。代码段正在运行时的更多信息。要重新启动,请重新运行该代码段。 (有很多额外的代码可以忽略)


拐角半径设置为30。


  const ctx = canvas.getContext( 2d); 
const鼠标= {
x:0,
y:0,
按钮:false,
drag:false,
dragStart:false,
dragEnd:否,
dragStartX:0,
dragStartY:0
}

函数mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
const lb = mouse.button;
mouse.button = e.type === mousedown吗? true:e.type === mouseup吗? false:鼠标按钮;
if(lb!== mouse.button){
if(mouse.button){
mouse.drag = true;
mouse.dragStart = true;
mouse.dragStartX = mouse.x;
mouse.dragStartY = mouse.y;
} else {
mouse.drag = false;
mouse.dragEnd = true;
}
}
}
[ down, up, move]。forEach(name => document.addEventListener( mouse + name,mouseEvents ));

const pointOnLine = {x:0,y:0};
函数distFromLines(x,y,minDist){
var index = -1;
const v1 = {};
const v2 = {};
const v3 = {};
常数点= P2(x,y);
eachOf(polygon,(p,i)=> {
const p1 =多边形[(i + 1)%polygon.length];
v1.x = p1.x-px ;
v1.y = p1.y-py;
v2.x = point.x-px;
v2.y = point.y-py;
const u = (v2.x * v1.x + v2.y * v1.y)/(v1.y * v1.y + v1.x * v1.x);
if(u> = 0&& ; u&=; 1){
v3.x = px + v1.x * u;
v3.y = py + v1.y * u;
dist = Math.hypot( v3.y-point.y,v3.x-point.x);
if(dist< minDist){
minDist = dist;
index = i;
pointOnLine .x = v3.x;
pointOnLine.y = v3.y;
}
}
})
返回索引;

}



函数roundedPoly(ctx,points,radius){
var i,x,y,len,p1, p2,p3,v1,v2,sinA,sinA90,radDirection,drawDirection,angle,halfAngle,cRadius,lenOut;
var asVec = function(p,pp,v){
v.x = pp.x-p.x;
v.y = pp.y-p.y;
v.len = Math.sqrt(v.x * v.x + v.y * v.y);
v.nx = v.x / v.len;
v.ny = v.y / v.len;
v.ang = Math.atan2(v.ny,v.nx);
}
v1 = {};
v2 = {};
len = points.length;
p1 =积分[len-1];
for(i = 0; i p2 = points [[i)%len];
p3 = points [(i + 1)%len];
asVec(p2,p1,v1);
asVec(p2,p3,v2);
sinA = v1.nx * v2.ny-v1.ny * v2.nx;
sinA90 = v1.nx * v2.nx-v1.ny * -v2.ny;
angle = Math.asin(sinA);
radDirection = 1;
drawDirection = false;
if(sinA90< 0){
if(angle< 0){
angle = Math.PI + angle;
} else {
angle = Math.PI-angle;
radDirection = -1;
drawDirection = true;
}
} else {
if(angle> 0){
radDirection = -1;
drawDirection = true;
}
}
halfAngle =角度/ 2;
lenOut = Math.abs(Math.cos(halfAngle)* radius / Math.sin(halfAngle));
if(lenOut> Math.min(v1.len / 2,v2.len / 2)){
lenOut = Math.min(v1.len / 2,v2.len / 2);
cRadius = Math.abs(lenOut * Math.sin(halfAngle)/ Math.cos(halfAngle));
} else {
cRadius =半径;
}
x = p2.x + v2.nx * lenOut;
y = p2.y + v2.ny * lenOut;
x + = -v2.ny * cRadius * radDirection;
y + = v2.nx * cRadius * radDirection;
ctx.arc(x,y,cRadius,v1.ang + Math.PI / 2 * radDirection,v2.ang-Math.PI / 2 * radDirection,drawDirection);
p1 = p2;
p2 = p3;
}
ctx.closePath();
}
const eachOf =(array,callback)=> {var i = 0; while(i< array.length&& callback(array [i],i ++)!== true); };
const P2 =(x = 0,y = 0)=> ({x,y});
const多边形= [];

函数findClosestPointIndex(x,y,minDist){
var index = -1;
eachOf(polygon,(p,i)=> {
const dist = Math.hypot(x-px,y-py);
if(dist< minDist){
minDist = dist;
index = i;
}
});
返回指数;
}


//快捷方式vars
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; //中心
var ch = h / 2;
var dragPoint;
var globalTime;
var最近索引= -1;
var最近的行索引= -1;
var cursor =默认;
const lineDist = 10;
const pointDist = 20;
var toolTip =;
//主要更新函数
函数update(timer){
globalTime = timer;
cursor =十字线;
toolTip =;
ctx.setTransform(1,0,0,1,0,0); //重置转换
ctx.globalAlpha = 1; //重置alpha
if(w!== innerWidth-4 || h!== innerHeight-4){
cw =(w = canvas.width = innerWidth-4)/ 2;
ch =(h = canvas.height = innerHeight-4)/ 2;
} else {
ctx.clearRect(0,0,w,h);
}
if(mouse.drag){
if(mouse.dragStart){
mouse.dragStart = false;
最近索引= findClosestPointIndex(mouse.x,mouse.y,pointDist);
if(closestIndex === -1){
closelineLineIndex = distFromLines(mouse.x,mouse.y,lineDist);
if(closestLineIndex === -1){
多边形.push(dragPoint = P2(mouse.x,mouse.y));
} else {
多边形.splice(closestLineIndex + 1,0,dragPoint = P2(mouse.x,mouse.y));
}

} else {
dragPoint = gonal [closestIndex];
}
}
dragPoint.x = mouse.x;
dragPoint.y = mouse.y
cursor = none;
} else {
最近的索引= findClosestPointIndex(mouse.x,mouse.y,pointDist);
if(closestIndex === -1){
closelineLineIndex = distFromLines(mouse.x,mouse.y,lineDist);
if(closestLineIndex> -1){
toolTip =单击以剪切线和/或拖动以移动。 ;
}
}其他{
toolTip =单击拖动以移动点。;
最近的行索引= -1;
}
}
ctx.lineWidth = 4;
ctx.fillStyle =#09F;
ctx.strokeStyle =#000;
ctx.beginPath();
roundedPoly(ctx,多边形,30);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.strokeStyle =红色;
ctx.lineWidth = 0.5;
eachOf(多边形,p => ctx.lineTo(p.x,p.y));
ctx.closePath();
ctx.stroke();
ctx.strokeStyle =橙色;
ctx.lineWidth = 1;
eachOf(多边形,p => ctx.strokeRect(p.x-2,p.y-2,4,4));
if(closestIndex> -1){
ctx.strokeStyle = red;
ctx.lineWidth = 4;
dragPoint =多边形[closestIndex];
ctx.strokeRect(dragPoint.x-4,dragPoint.y-4,8,8);
cursor = move;
}否则if(closestLineIndex> -1){
ctx.strokeStyle = red;
ctx.lineWidth = 4;
var p =多边形[closestLineIndex];
var p1 =多边形[(closestLineIndex +1)%多边形。长度];
ctx.beginPath();
ctx.lineTo(p.x,p.y);
ctx.lineTo(p1.x,p1.y);
ctx.stroke();
ctx.strokeRect(pointOnLine.x-4,pointOnLine.y-4,8,8);
cursor = pointer;


}

if(toolTip ===&& polygon.length< 3){
toolTip =点击添加多边形的一角。;
}
canvas.title = toolTip;
canvas.style.cursor =游标;
requestAnimationFrame(update);
}
requestAnimationFrame(update);

 画布{
边框:2px纯黑色;
头寸:绝对;
top:0像素;
左:0px;
}

 < canvas id = canvas >< / canvas>  


I'm new to HTML5 Canvas and I'm trying to draw a triangle with rounded corners.

I have tried

ctx.lineJoin = "round";
ctx.lineWidth = 20;

but none of them are working.

Here's my code:

var ctx = document.querySelector("canvas").getContext('2d');

ctx.scale(5, 5);
    
var x = 18 / 2;
var y = 0;
var triangleWidth = 18;
var triangleHeight = 8;

// how to round this triangle??
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + triangleWidth / 2, y + triangleHeight);
ctx.lineTo(x - triangleWidth / 2, y + triangleHeight);
ctx.closePath();
ctx.fillStyle = "#009688";
ctx.fill();
    
ctx.fillStyle = "#8BC34A";
ctx.fillRect(0, triangleHeight, 9, 126);
ctx.fillStyle = "#CDDC39";
ctx.fillRect(9, triangleHeight, 9, 126);

<canvas width="800" height="600"></canvas>

Could you help me?

解决方案

Rounding corners

An invaluable function I use a lot is rounded polygon. It takes a set of 2D points that describe a polygon's vertices and adds arcs to round the corners.

The problem with rounding corners and keeping within the constraint of the polygons area is that you can not always fit a round corner that has a particular radius.

In these cases you can either ignore the corner and leave it as pointy or, you can reduce the rounding radius to fit the corner as best possible.

The following function will resize the corner rounding radius to fit the corner if the corner is too sharp and the lines from the corner not long enough to get the desired radius in.

Note the code has comments that refer to the Maths section below if you want to know what is going on.

roundedPoly(ctx, points, radius)

// ctx is the context to add the path to
// points is a array of points [{x :?, y: ?},...
// radius is the max rounding radius 
// this creates a closed polygon.
// To draw you must call between 
//    ctx.beginPath();
//    roundedPoly(ctx, points, radius);
//    ctx.stroke();
//    ctx.fill();
// as it only adds a path and does not render. 
function roundedPoly(ctx, points, radiusAll) {
  var i, x, y, len, p1, p2, p3, v1, v2, sinA, sinA90, radDirection, drawDirection, angle, halfAngle, cRadius, lenOut,radius;
  // convert 2 points into vector form, polar form, and normalised 
  var asVec = function(p, pp, v) {
    v.x = pp.x - p.x;
    v.y = pp.y - p.y;
    v.len = Math.sqrt(v.x * v.x + v.y * v.y);
    v.nx = v.x / v.len;
    v.ny = v.y / v.len;
    v.ang = Math.atan2(v.ny, v.nx);
  }
  radius = radiusAll;
  v1 = {};
  v2 = {};
  len = points.length;
  p1 = points[len - 1];
  // for each point
  for (i = 0; i < len; i++) {
    p2 = points[(i) % len];
    p3 = points[(i + 1) % len];
    //-----------------------------------------
    // Part 1
    asVec(p2, p1, v1);
    asVec(p2, p3, v2);
    sinA = v1.nx * v2.ny - v1.ny * v2.nx;
    sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny;
    angle = Math.asin(sinA < -1 ? -1 : sinA > 1 ? 1 : sinA);
    //-----------------------------------------
    radDirection = 1;
    drawDirection = false;
    if (sinA90 < 0) {
      if (angle < 0) {
        angle = Math.PI + angle;
      } else {
        angle = Math.PI - angle;
        radDirection = -1;
        drawDirection = true;
      }
    } else {
      if (angle > 0) {
        radDirection = -1;
        drawDirection = true;
      }
    }
    if(p2.radius !== undefined){
        radius = p2.radius;
    }else{
        radius = radiusAll;
    }
    //-----------------------------------------
    // Part 2
    halfAngle = angle / 2;
    //-----------------------------------------

    //-----------------------------------------
    // Part 3
    lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));
    //-----------------------------------------

    //-----------------------------------------
    // Special part A
    if (lenOut > Math.min(v1.len / 2, v2.len / 2)) {
      lenOut = Math.min(v1.len / 2, v2.len / 2);
      cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
    } else {
      cRadius = radius;
    }
    //-----------------------------------------
    // Part 4
    x = p2.x + v2.nx * lenOut;
    y = p2.y + v2.ny * lenOut;
    //-----------------------------------------
    // Part 5
    x += -v2.ny * cRadius * radDirection;
    y += v2.nx * cRadius * radDirection;
    //-----------------------------------------
    // Part 6
    ctx.arc(x, y, cRadius, v1.ang + Math.PI / 2 * radDirection, v2.ang - Math.PI / 2 * radDirection, drawDirection);
    //-----------------------------------------
    p1 = p2;
    p2 = p3;
  }
  ctx.closePath();
}

You may wish to add to each point a radius eg {x :10,y:10,radius:20} this will set the max radius for that point. A radius of zero will be no rounding.

The maths

The following illistration shows one of two possibilities, the angle to fit is less than 90deg, the other case (greater than 90) just has a few minor calculation differences (see code).

The corner is defined by the three points in red A, B, and C. The circle radius is r and we need to find the green points F the circle center and D and E which will define the start and end angles of the arc.

First we find the angle between the lines from B,A and B,C this is done by normalising the vectors for both lines and getting the cross product. (Commented as Part 1) We also find the angle of line BC to the line at 90deg to BA as this will help determine which side of the line to put the circle.

Now we have the angle between the lines, we know that half that angle defines the line that the center of the circle will sit F but we do not know how far that point is from B (Commented as Part 2)

There are two right triangles BDF and BEF which are identical. We have the angle at B and we know that the side DF and EF are equal to the radius of the circle r thus we can solve the triangle to get the distance to F from B

For convenience rather than calculate to F is solve for BD (Commented as Part 3) as I will move along the line BC by that distance (Commented as Part 4) then turn 90deg and move up to F (Commented as Part 5) This in the process gives the point D and moving along the line BA to E

We use points D and E and the circle center F (in their abstract form) to calculate the start and end angles of the arc. (done in the arc function part 6)

The rest of the code is concerned with the directions to move along and away from lines and which direction to sweep the arc.

The code section (special part A) uses the lengths of both lines BA and BC and compares them to the distance from BD if that distance is greater than half the line length we know the arc can not fit. I then solve the triangles to find the radius DF if the line BD is half the length of shortest line of BA and BC

Example use.

The snippet is a simple example of the above function in use. Click to add points to the canvas (needs a min of 3 points to create a polygon). You can drag points and see how the corner radius adapts to sharp corners or short lines. More info when snippet is running. To restart rerun the snippet. (there is a lot of extra code that can be ignored)

The corner radius is set to 30.

const ctx = canvas.getContext("2d");
const mouse = {
  x: 0,
  y: 0,
  button: false,
  drag: false,
  dragStart: false,
  dragEnd: false,
  dragStartX: 0,
  dragStartY: 0
}

function mouseEvents(e) {
  mouse.x = e.pageX;
  mouse.y = e.pageY;
  const lb = mouse.button;
  mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
  if (lb !== mouse.button) {
    if (mouse.button) {
      mouse.drag = true;
      mouse.dragStart = true;
      mouse.dragStartX = mouse.x;
      mouse.dragStartY = mouse.y;
    } else {
      mouse.drag = false;
      mouse.dragEnd = true;
    }
  }
}
["down", "up", "move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));

const pointOnLine = {x:0,y:0};
function distFromLines(x,y,minDist){   
  var index = -1;
  const v1 = {};
  const v2 = {};
  const v3 = {};
  const point = P2(x,y);
  eachOf(polygon,(p,i)=>{
    const p1 = polygon[(i + 1) % polygon.length];
    v1.x = p1.x - p.x;
    v1.y = p1.y - p.y;
    v2.x = point.x - p.x;
    v2.y = point.y - p.y;
    const u = (v2.x * v1.x + v2.y * v1.y)/(v1.y * v1.y + v1.x * v1.x);
    if(u >= 0 && u <= 1){
      v3.x = p.x + v1.x * u;
      v3.y = p.y + v1.y * u;
      dist = Math.hypot(v3.y - point.y, v3.x - point.x);
      if(dist < minDist){
        minDist = dist;
        index = i;
        pointOnLine.x = v3.x;
        pointOnLine.y = v3.y;
      }
    }
  })
  return index;
  
}



function roundedPoly(ctx, points, radius) {
  var i, x, y, len, p1, p2, p3, v1, v2, sinA, sinA90, radDirection, drawDirection, angle, halfAngle, cRadius, lenOut;
  var asVec = function(p, pp, v) {
    v.x = pp.x - p.x;
    v.y = pp.y - p.y;
    v.len = Math.sqrt(v.x * v.x + v.y * v.y);
    v.nx = v.x / v.len;
    v.ny = v.y / v.len;
    v.ang = Math.atan2(v.ny, v.nx);
  }
  v1 = {};
  v2 = {};
  len = points.length;
  p1 = points[len - 1];
  for (i = 0; i < len; i++) {
    p2 = points[(i) % len];
    p3 = points[(i + 1) % len];
    asVec(p2, p1, v1);
    asVec(p2, p3, v2);
    sinA = v1.nx * v2.ny - v1.ny * v2.nx;
    sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny;
    angle = Math.asin(sinA);
    radDirection = 1;
    drawDirection = false;
    if (sinA90 < 0) {
      if (angle < 0) {
        angle = Math.PI + angle;
      } else {
        angle = Math.PI - angle;
        radDirection = -1;
        drawDirection = true;
      }
    } else {
      if (angle > 0) {
        radDirection = -1;
        drawDirection = true;
      }
    }
    halfAngle = angle / 2;
    lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));
    if (lenOut > Math.min(v1.len / 2, v2.len / 2)) {
      lenOut = Math.min(v1.len / 2, v2.len / 2);
      cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
    } else {
      cRadius = radius;
    }
    x = p2.x + v2.nx * lenOut;
    y = p2.y + v2.ny * lenOut;
    x += -v2.ny * cRadius * radDirection;
    y += v2.nx * cRadius * radDirection;
    ctx.arc(x, y, cRadius, v1.ang + Math.PI / 2 * radDirection, v2.ang - Math.PI / 2 * radDirection, drawDirection);
    p1 = p2;
    p2 = p3;
  }
  ctx.closePath();
}
const eachOf = (array, callback) => { var i = 0; while (i < array.length && callback(array[i], i++) !== true); };
const P2 = (x = 0, y = 0) => ({x, y});
const polygon = [];

function findClosestPointIndex(x, y, minDist) {
  var index = -1;
  eachOf(polygon, (p, i) => {
    const dist = Math.hypot(x - p.x, y - p.y);
    if (dist < minDist) {
      minDist = dist;
      index = i;
    }
  });
  return index;
}


// short cut vars 
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center 
var ch = h / 2;
var dragPoint;
var globalTime;
var closestIndex = -1;
var closestLineIndex = -1;
var cursor = "default";
const lineDist = 10;
const pointDist = 20;
var toolTip = "";
// main update function
function update(timer) {
  globalTime = timer;
  cursor = "crosshair";
  toolTip = "";
  ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
  ctx.globalAlpha = 1; // reset alpha
  if (w !== innerWidth - 4 || h !== innerHeight - 4) {
    cw = (w = canvas.width = innerWidth - 4) / 2;
    ch = (h = canvas.height = innerHeight - 4) / 2;
  } else {
    ctx.clearRect(0, 0, w, h);
  }
  if (mouse.drag) {
    if (mouse.dragStart) {
      mouse.dragStart = false;
      closestIndex = findClosestPointIndex(mouse.x,mouse.y, pointDist);
      if(closestIndex === -1){        
        closestLineIndex = distFromLines(mouse.x,mouse.y,lineDist);
        if(closestLineIndex === -1){
          polygon.push(dragPoint = P2(mouse.x, mouse.y));
        }else{
          polygon.splice(closestLineIndex+1,0,dragPoint = P2(mouse.x, mouse.y));
        }
        
      }else{
        dragPoint = polygon[closestIndex];
      }
    }
    dragPoint.x = mouse.x;
    dragPoint.y = mouse.y
    cursor = "none";
  }else{
    closestIndex = findClosestPointIndex(mouse.x,mouse.y, pointDist);
    if(closestIndex === -1){
      closestLineIndex = distFromLines(mouse.x,mouse.y,lineDist);
      if(closestLineIndex > -1){
        toolTip = "Click to cut line and/or drag to move.";
      }
    }else{
      toolTip = "Click drag to move point.";
      closestLineIndex = -1;
    }
  }
  ctx.lineWidth = 4;
  ctx.fillStyle = "#09F";
  ctx.strokeStyle = "#000";
  ctx.beginPath();
  roundedPoly(ctx, polygon, 30);
  ctx.stroke();
  ctx.fill();
  ctx.beginPath();
  ctx.strokeStyle = "red";
  ctx.lineWidth = 0.5;
  eachOf(polygon, p => ctx.lineTo(p.x,p.y) );
  ctx.closePath();
  ctx.stroke();
  ctx.strokeStyle = "orange";
  ctx.lineWidth = 1;
  eachOf(polygon, p => ctx.strokeRect(p.x-2,p.y-2,4,4) );
  if(closestIndex > -1){
     ctx.strokeStyle = "red";
     ctx.lineWidth = 4;
     dragPoint = polygon[closestIndex];
     ctx.strokeRect(dragPoint.x-4,dragPoint.y-4,8,8);
     cursor = "move";
  }else if(closestLineIndex > -1){
     ctx.strokeStyle = "red";
     ctx.lineWidth = 4;
     var p = polygon[closestLineIndex];
     var p1 = polygon[(closestLineIndex + 1) % polygon.length];
     ctx.beginPath();
     ctx.lineTo(p.x,p.y);
     ctx.lineTo(p1.x,p1.y);
     ctx.stroke();
     ctx.strokeRect(pointOnLine.x-4,pointOnLine.y-4,8,8);
     cursor = "pointer";     
  
  
  }

  if(toolTip === "" && polygon.length < 3){
    toolTip = "Click to add a corners of a polygon.";
  }
  canvas.title = toolTip;
  canvas.style.cursor = cursor;
  requestAnimationFrame(update);
}
requestAnimationFrame(update);

canvas {
  border: 2px solid black;
  position: absolute;
  top: 0px;
  left: 0px;
}

<canvas id="canvas"></canvas>

这篇关于带有圆角的html5画布三角形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆