美文网首页
js 切割圆形

js 切割圆形

作者: CODERLIHAO | 来源:发表于2020-05-07 17:59 被阅读0次
    2020-05-07 17.16.55.gif
    这种类型就是线段与圆的交点问题
    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");
            }
    

    Demo

    相关文章

      网友评论

          本文标题:js 切割圆形

          本文链接:https://www.haomeiwen.com/subject/zonaghtx.html