这种类型就是线段与圆的交点问题
724B5348538A85F0E7CE3AD597AD829A.png
设线段就是P1P2就是我们的线段,P1(x1,y1) , P2(x2,y2)
交点是P(x,y)
那么向量 OP = 向量O P1 + u(P1P2),u是一个系数
则
x = x1+u(x2−x1)
y = y1+u(y2−y1)
由于P也在圆上,所以
(x−x3)2+(y−y3)2=r2
带入方程,求解
Au2+Bu+C=0
A = (x2−x1)2+(y2−y1)2
B = 2((x2−x1)(x1−x3)+(y2−y1)(y1−y3))
C = (x3)2+(y3)2+(x1)2+(y1)2−2(x3x1+y3y1)−r2
解一元二次方程
u = (-B ±√(B2 - 4AC))/2A
B2 - 4AC < 0 没有解
B2 - 4AC = 0 只有一个解
B2 - 4AC > 0 有两个解
我们的要求的是找到两个不同的点,所以只有B2 - 4AC>0满足条件
求出的u可能大于等于1 或者小于等于0,都是不满足的
先定义我们的剪切线段
class CutLine {
sx = 0; //起始点x
sy = 0; //起始点y
ex = 0; //终点x
ey = 0; //终点y
strokeWidth = 0; //线条宽度
strokeColor = 0; //线条颜色
constructor(option) {
this.strokeWidth = option.strokeWidth;
this.strokeColor = option.strokeColor;
}
updateStartPoint(x, y) {
this.sx = x;
this.sy = y;
}
updateEndPoint(x, y) {
this.ex = x;
this.ey = y;
}
draw(ctx) {
ctx.beginPath();
ctx.lineWidth = this.strokeWidth;
ctx.strokeStyle = this.strokeColor;
ctx.moveTo(this.sx, this.sy);
ctx.lineTo(this.ex, this.ey);
ctx.stroke();
}
}
定义一个抽象的shape
class Shape {
cutLine = null;
isCut = false;
child = [];
hitChild = null;
oldX = 0;
oldY = 0;
constructor(option) {
this.cutLine = new CutLine({
strokeWidth: option.lineStrokeWidth,
strokeColor: option.lineStrokeColor
});
}
onTouchDown(event, ctx) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
this.oldX = event.x;
this.oldY = event.y;
if (!this.isCut) {
this.cutLine.updateStartPoint(event.x, event.y);
this.drawSelf(ctx);
} else {
this.hitChild = this.hitTest(event);
this.drawChild(ctx);
}
}
onTouchMove(event, ctx) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
if (this.isCut && this.hitChild) {
const x = event.x;
const y = event.y;
const deltaX = x - this.oldX;
const deltaY = y - this.oldY;
this.hitChild.x += deltaX;
this.hitChild.y += deltaY;
this.oldX = x;
this.oldY = y;
}
if (!this.isCut) {
this.cutLine.updateEndPoint(event.x, event.y);
this.cutLine.draw(ctx);
this.drawSelf(ctx);
} else {
this.drawChild(ctx);
}
}
onTouchUp(event, ctx) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}
drawChild(ctx) {
if (this.child) {
this.child.forEach((shape) => {
shape.draw(ctx);
});
}
}
cutShape(cutLine) {
}
drawSelf(ctx) {
}
hitTest(event) {
const x = event.x;
const y = event.y;
for (let i = this.child.length - 1; i >= 0; i--) {
const childShape = this.child[i];
if (x >= childShape.x && x <= childShape.x + childShape.w && y >= childShape.y && y <= childShape.y + childShape.h) {
const image = childShape.image;
if (!image) {
continue;
}
const pxData = Utils.getPixel(image, x - childShape.x, y - childShape.y, childShape.w, childShape.h);
if (pxData[3] < 1) {
continue;
}
return childShape;
}
}
}
}
用圆继承这个Shape
class CircleShape extends Shape {
cx = 0;
cy = 0;
cr = 0;
strokeColor = "#098231";
fillColor = "#098231";
cutFillColor = "#098231";
strokeWidth = 0;
constructor(option) {
super(option);
this.cx = option.cx;
this.cy = option.cy;
this.cr = option.cr;
this.strokeColor = option.strokeColor;
this.fillColor = option.fillColor;
this.cutFillColor = option.cutFillColor;
this.strokeWidth = option.strokeWidth;
}
reset(ctx) {
this.hitChild = null;
this.child.length = 0;
this.isCut = false;
this.drawSelf(ctx);
}
onTouchUp(event, ctx) {
super.onTouchUp(event, ctx);
this.hitChild = null;
if (!this.isCut) {
const thetas = this.getTheta(this.cx, this.cy, this.cr, this.cutLine.sx, this.cutLine.sy, this.cutLine.ex, this.cutLine.ey);
if (thetas) {
this.isCut = true;
this.cutShape(ctx, thetas);
this.drawChild(ctx);
} else {
this.drawCircle(ctx, this.cx, this.cy, this.cr, this.strokeWidth, this.strokeColor, this.fillColor);
}
} else {
this.drawChild(ctx);
}
}
drawSelf(ctx) {
ctx.beginPath();
ctx.fillStyle = this.fillColor;
ctx.arc(this.cx, this.cy, this.cr - this.strokeWidth / 2, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.lineWidth = this.strokeWidth;
ctx.strokeStyle = this.strokeColor;
ctx.arc(this.cx, this.cy, this.cr - this.strokeWidth / 2, 0, 2 * Math.PI);
ctx.stroke();
}
cutShape(ctx, thetas) {
const image1 = Utils.toCircleDataURL(this.cr, thetas[0], thetas[1], this.strokeColor, this.fillColor, this.strokeWidth, false);
const child1 = new ChildShape(this.cx - this.cr, this.cy - this.cr, 2 * this.cr, 2 * this.cr, image1);
this.child.push(child1);
const image2 = Utils.toCircleDataURL(this.cr, thetas[0], thetas[1], this.strokeColor, this.cutFillColor, this.strokeWidth, true);
const child2 = new ChildShape(this.cx - this.cr, this.cy - this.cr, 2 * this.cr, 2 * this.cr, image2);
this.child.push(child2);
}
getTheta(cx, cy, cr, x1, y1, x2, y2) {
if (Math.sqrt(Math.pow(cx - x1, 2) + Math.pow(cy - y1, 2)) <= cr
|| Math.sqrt(Math.pow(cx - x2, 2) + Math.pow(cy - y2, 2)) <= cr) {
return;
}
//设线段的两个端点分别是P1(x1,y1)和P2(x2,y2),圆的圆心在P3(x3,y3),半径为r,那么如果有交点P(x,y)的话
//向量P = 向量P1 +u(向量P2 − 向量P1)
// x = x1+u(x2−x1)
// y = y1+u(y2−y1)
//由于P也在圆上,所以
//(x−x3)^2+(y−y3)^2=r^2
// Au^2+Bu+C=0
// A = (x2−x1)^2+(y2−y1)^2
// B = 2((x2−x1)(x1−x3)+(y2−y1)(y1−y3))
// C = (x3)^2+(y3)^2+(x1)^2+(y1)^2−2(x3x1+y3y1)−r^2
// u = (-B ±√(B^2 - 4AC))/2A
const A = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2);
const B = 2 * ((x2 - x1) * (x1 - cx) + (y2 - y1) * (y1 - cy));
const C = Math.pow(cx, 2) + Math.pow(cy, 2) + Math.pow(x1, 2) + Math.pow(y1, 2) - 2 * (cx * x1 + cy * y1) - Math.pow(cr, 2);
const t = Math.pow(B, 2) - 4 * A * C;
if (t <= 0) {
//没有交点或者只有一个交点
return;
}
const u1 = (-B + Math.sqrt(t)) / 2 / A;
const u2 = (-B - Math.sqrt(t)) / 2 / A;
if (Math.abs(u1) >= 1 || Math.abs(u2) >= 1) {
return;
}
const p1X = x1 + u1 * (x2 - x1);
const p1Y = y1 + u1 * (y2 - y1);
const p2X = x1 + u2 * (x2 - x1);
const p2Y = y1 + u2 * (y2 - y1);
const theta1 = Math.atan2(p1Y - cy, p1X - cx);
const theta2 = Math.atan2(p2Y - cy, p2X - cx);
return [theta1, theta2];
}
}
定义一个工具类,将shape变为图片,每次点击时,获取图片位置的像素值,要是透明的就不接受事件
static getPixel(image, x, y, w, h) {
if (!Utils.cacheCanvas) {
Utils.cacheCanvas = document.createElement("canvas");
}
var ctx = Utils.cacheCanvas.getContext('2d');
Utils.cacheCanvas.width = w;
Utils.cacheCanvas.height = h;
ctx.drawImage(image, 0, 0);
return ctx.getImageData(x, y, 1, 1).data;
}
static toCircleDataURL(cr, angleStart, angleEnd, strokeColor, fillColor, lineWidth, anticlockwise = false) {
if (!Utils.cacheCanvas) {
Utils.cacheCanvas = document.createElement("canvas");
}
var ctx = Utils.cacheCanvas.getContext('2d');
ctx.clearRect(0, 0, Utils.cacheCanvas.width, Utils.cacheCanvas.height);
Utils.cacheCanvas.width = 2 * cr;
Utils.cacheCanvas.height = 2 * cr;
ctx.beginPath();
ctx.fillStyle = fillColor;
ctx.arc(cr, cr, cr - lineWidth / 2, angleStart, angleEnd, anticlockwise);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeColor;
ctx.arc(cr, cr, cr - lineWidth / 2, angleStart, angleEnd, anticlockwise);
ctx.closePath();
ctx.stroke();
return Utils.cacheCanvas.toDataURL("image/png");
}
网友评论