如何在HTML画布中制作新月形状

时间:2017-07-26 14:05:25

标签: html5-canvas

我需要在HTML5画布中制作以下形状。我尝试过使用立方贝塞尔曲线并剪切两个圆圈。

我该如何塑造这个形状?

这是我正在进行的工作,只是不能正确

https://codepen.io/matt3224/pen/oeXbdg?editors=1010

var canvas = document.getElementById("canvas1");
var ctx1 = canvas.getContext("2d");

ctx1.lineWidth = 2;

ctx1.beginPath();
ctx1.bezierCurveTo(4, 42, 0, 0, 42, 4);
ctx1.moveTo(4, 42);
ctx1.bezierCurveTo(4, 42, 0, 84, 42, 84);
ctx1.stroke();

var canvas = document.getElementById("canvas2");
var ctx2 = canvas.getContext("2d");

ctx2.lineWidth = 2;

ctx2.beginPath();
ctx2.arc(55, 75, 50, 0, Math.PI * 2, true);
ctx2.moveTo(165, 75);
ctx2.arc(75, 75, 50, 0, Math.PI * 2, true);
ctx2.fill();

Crescent Moon

2 个答案:

答案 0 :(得分:1)

使用globalCompositeOperation

解决了问题

https://codepen.io/matt3224/pen/oeXbdg?editors=1010

答案 1 :(得分:0)

圆圈布尔运算。

如果任何人对编程解决方案感兴趣,下面的示例将找到两个圆的截距点,并使用这些点计算外圆和内圆的起点和终点角。

这比屏蔽解决方案更灵活,因为它为您提供了一条路径。

代码段显示圆圈,将鼠标移到圆圈上以查看新月形解决方案。不是使用掩蔽解决方案时无法使用的笔划。

const PI2 = Math.PI * 2;
const ctx = canvas.getContext("2d");
canvas.height = canvas.width = 400;

const mouse  = {x : 0, y : 0, button : false}
function mouseEvents(e){
  const m = mouse;
	const bounds = canvas.getBoundingClientRect();
	m.x = e.pageX - bounds.left - scrollX;
	m.y = e.pageY - bounds.top - scrollY;	
	m.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : m.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));



// generic circle circle intercept function. Returns undefined if
// no intercept. 
// Circle 1 is center x1,y1 and radius r1
// Circle 2 is center x2,y2 and radius r2
// If points found returns {x1,y1,x2,y2} as two points.
function circleCircleIntercept(x1,y1,r1,x2,y2,r2){
    var x = x2 - x1;
    var y = y2 - y1;
    var dist = Math.sqrt(x * x + y * y);
    if(dist > r1 + r2 || dist < Math.abs(r1-r2)){
        return;  // no intercept return undefined
    }
    var a = (dist * dist - r1 * r1 + r2 *r2) / ( 2 * dist);
    var b = Math.sqrt(r2 * r2 - a * a);
    a /= dist;
    x *= a;
    y *= a;
    var mx = x2 - x;
    var my = y2 - y;
    dist = b / Math.sqrt(x * x + y * y);
    x *= dist;
    y *= dist;
    return {
       x1 : mx-y,
       y1 : my+x,
       x2 : mx+y,
       y2 : my-x,
    };
}
// draws a crescent from two circles if possible
// If not then just draws the first circle
function drawCrescent(x1,y1,r1,x2,y2,r2){
    // The circle circle intercept finds points
    // but finding the angle of the points does not consider
    // the rotation direction and you end up having to do a lot of
    // checking (if statments) to determin the correct way to draw each circle
    // the following normalises the direction the circle are from each other
    // thus making the logic a lot easier
    
    var dist = Math.hypot(x2-x1,y2-y1);
    var ang = Math.atan2(y2-y1,x2-x1);
    var intercepts = circleCircleIntercept(x1,y1,r1,x1 + dist,y1,r2);    
    if(intercepts === undefined){
        ctx.beginPath();
        ctx.arc(x1, y1, r1, 0, PI2);
        if(dist < r1){
            ctx.moveTo(x2 + r2, y2);
            ctx.arc(x2, y2, r2, 0, PI2, true);
        }
       
        ctx.fill();
        ctx.stroke();
        return;
    }
    // get the start end angles for outer then inner circles
    const p = intercepts;
    var startA1 = Math.atan2(p.y1 - y1, p.x1 - x1) + ang; 
    var endA1 = Math.atan2(p.y2 - y1, p.x2 - x1) + ang; 
    var startA2 = Math.atan2(p.y1 - y1, p.x1 - (x1 + dist)) + ang; 
    var endA2 = Math.atan2(p.y2 - y1, p.x2 - (x1 + dist)) + ang; 

    ctx.beginPath();
    if(endA1 < startA1){
        ctx.arc(x1, y1, r1, startA1, endA1);
        ctx.arc(x2, y2, r2, endA2, startA2, true);
    }else{
        ctx.arc(x2, y2, r2, endA2, startA2);    
        ctx.arc(x1, y1, r1, startA1, endA1,true);
    }
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
}


const outerRadius = 100;
const innerRadius = 80;

var w = canvas.width;
var h = canvas.height;
var cw = w / 2;  // center 
var ch = h / 2;
var globalTime;
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.lineJoin = "round";
ctx.lineWidth = 8;
ctx.strokeStyle = "#999";


// main update function
function mainLoop(timer){
    globalTime = timer;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.fillStyle = "black";
		ctx.fillRect(0,0,w,h);
    ctx.fillStyle = "white";
		
    ctx.fillText("Move mouse over circle",cw,40);    
    drawCrescent(cw, ch-40, outerRadius, mouse.x, mouse.y, innerRadius);

    requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas>