• 软件项目技术点(7)——在canvas上绘制自定义图形


    AxeSlide软件项目梳理   canvas绘图系列知识点整理

    图形种类

    目前我们软件可以绘制出来的形状有如下这几种,作为开发者我们一直想支持用户可以拖拽的类似word里面图形库,但目前还没有找到比较合适的库。

    下图中所有的图形都是我们自己写代码在canvas上绘制方法出来的,可以改变实心/空心,改变颜色,大小宽高,线条弯曲度,透明度等。

    父类shape类

    实现的代码如下:所有的图形均继承自shape类,而shape类继承自CommonElement,shape中的所有图形添加到画布上都是一个元素。

    这个类里面有很多功能函数:

    prepareStyle() { //绘制准备context绘制属性

    setPro() { }//绘制前准备参数

    public thiner() {//变细

    public thicker() {//加粗

    changeColor(color: string, callBack: Function) {//改变绘制颜色

    changeAlpha(alpha: number, callBack: Function) {//改变透明度

    changeFill(isFill: boolean) {//切换实心空心

    drawPath() { }//绘制图形路径,所有图形都会重写该方法

    draw() {//绘制调用入口

     1  export class Shape extends CommonElement {
     2         shapeParameter: ShapeParameter;
     3         tempFill: boolean;
     4         tempStroke: boolean;
     5         constructor(id: string, config: Config, context: CanvasRenderingContext2D, shapeParameter: ShapeParameter, typeName) {
     6             super(id, config, context, typeName);
     7             this.shapeParameter = shapeParameter;
     8         }
     9         prepareStyle() { //绘制准备context绘制属性
    10             if (this.shapeParameter.computeLineWidth && this.shapeParameter.isStroke) {
    11                 this.shapeParameter.lineWidth = Math.max(2, Math.min(this.config.width * editor.canvas.canvasImp.scale, this.config.height * editor.canvas.canvasImp.scale) / this.shapeParameter.lineWidthScale);
    12 
    13             }
    14             var ctx = this.context;
    15             ctx.fillStyle = Common.Util.makeRGBA(this.shapeParameter.fillStyle, this.shapeParameter.alpha);
    16             ctx.strokeStyle = Common.Util.makeRGBA(this.shapeParameter.strokeStyle, this.shapeParameter.alpha);
    17             ctx.lineWidth = this.shapeParameter.lineWidth / editor.canvas.canvasImp.scale;
    18 
    19         }
    20         public setPro() { }//绘制前准备参数
    21         public drawPath() { }//绘制图形路径,所有图形都会重写该方法
    22         public draw() {//绘制调用入口
    23             //保存context状态
    24             this.context.save();
    25             this.rotate();//将画布旋转到位
    26             this.setPro();
    27             this.prepareStyle();
    28             this.drawPath();
    29            //恢复context状态
    30             this.context.restore();
    31         }
    32         

    所有图形类都有的一个属性ShapeParameter 具体内容如下,每个属性值都有各自的作用

     1   export class ShapeParameter {//图形属性参数
     2         lineWidth: number;
     3         isFill: boolean;
     4         isStroke: boolean
     5         fillStyle: string;
     6         strokeStyle: string
     7         lineWidthScale: number;
     8         isAutiClockwise: boolean;
     9         computeLineWidth: boolean;
    10         alpha: number;
    11         lineDash: any;
    12         constructor() {
    13             this.lineWidth = 1;
    14             this.isStroke = true;
    15             this.isFill = false;
    16             this.fillStyle = editor.currentShapeColor || editor.canvas.canvasImp.theme.shapeColor;
    17             this.strokeStyle = editor.currentShapeColor || editor.canvas.canvasImp.theme.shapeColor;
    18             this.lineWidthScale = lineWidthScale;
    19             this.isAutiClockwise = false;
    20             this.computeLineWidth = true;
    21             this.alpha = editor.currentShapAlpha || 1;
    22             this.lineDash = [10, 15];
    23         }
    24     }

     特殊图形绘制计算方法

    下面介绍几种绘制各种图形用到的特殊计算方法

    如下图中的一个线条,我们可以拖拽中间控制点形成一个弧,这个弧形是某个圆的一部分。

    我们定义左侧的第一个点为startPoint,中间的点为controlPoint,右侧的点为endPoint。

    我们在绘制出这个图形时,需要知道如何求这三个点确定的圆的圆心坐标怎么计算,还有通过两点计算(x1,y1)为圆心,圆上某点(x2,y2)的角度的计算方法

     

     1         GetCenter(): Point {//获取三个点,确定的一个圆的中心点坐标
     2             var p1 = this.startPoint;
     3             var p2 = this.controlPoint;
     4             var p3 = this.endPoint;
     5             var C1 = new Point((p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0);
     6             var C2 = new Point((p2.x + p3.x) / 2.0, (p2.y + p3.y) / 2.0);
     7             var I = new Point(0, 0);;
     8             var k1: number, k2: number;
     9             if ((p2.y - p1.y) == 0.0) {
    10                 if ((p3.y - p2.y) == 0.0) {
    11                     return null;
    12                 } else {
    13                     k2 = -1 * (p3.x - p2.x) / (p3.y - p2.y);
    14                     I.x = C1.x; I.y = k2 * (I.x - C2.x) + C2.y;
    15                 }
    16             } else {
    17                 k1 = -1 * (p2.x - p1.x) / (p2.y - p1.y);
    18                 if ((p3.y - p2.y) == 0.0) {
    19                     I.x = C2.x;
    20                     I.y = k1 * (I.x - C1.x) + C1.y;
    21                 } else {
    22                     k2 = -1 * (p3.x - p2.x) / (p3.y - p2.y);
    23                     if (k1 == k2) {
    24                         return null;
    25                     } else {
    26                         I.x = (C2.y - C1.y + k1 * C1.x - k2 * C2.x) / (k1 - k2); I.y = k1 * (I.x - C1.x) + C1.y;
    27                     }
    28                 }
    29             }
    30             return I;
    31         }
     1         //通过两点计算x1,y1为圆心,圆上某点(x2,y2)的角度(按照arc的角度计算)
     2         static computeAng(x1, y1, x2, y2) {
     3             var ang = (x1 - x2) / (y1 - y2);
     4             ang = Math.atan(ang);
     5             if (x1 == x2 && y2 > y1) {
     6                 return 0.5 * Math.PI;
     7             }
     8             if (x1 == x2 && y2 < y1) {
     9                 return 1.5 * Math.PI;
    10             }
    11             if (y1 == y2 && x2 > x1) {
    12                 return 0;
    13             }
    14             if (y1 == y2 && x2 < x1) {
    15                 return Math.PI;
    16             }
    17             if (x2 > x1 && y2 > y1) {
    18                 return Math.PI / 2 - ang;
    19             }
    20             else if (x2 < x1 && y2 > y1) {
    21                 return Math.PI / 2 - ang;
    22             }
    23             else if (x2 < x1 && y2 < y1) {
    24                 return 3 * Math.PI / 2 - ang;
    25             }
    26             else if (x2 > x1 && y2 < y1) {
    27                 return 3 * Math.PI / 2 - ang;
    28             }
    29         }

     另外再介绍一下我们的这个箭头是如何实现的

     1         drawIsosceles(context: CanvasRenderingContext2D) {//画等腰箭头
     2             var triangleSide = this.triangleSide;
     3             //First the center of the triangle base (where we start to draw the triangle)
     4             var centerBaseArrowX = this.basePoint.x ;
     5             var centerBaseArrowY = this.basePoint.y;
     6 
     7             //Let's calculate the first point, easy!
     8             var ax = centerBaseArrowX + (triangleSide / 2) * Math.cos(this.centerAngle);
     9             var ay = centerBaseArrowY + (triangleSide / 2) * Math.sin(this.centerAngle);
    10 
    11             //Now time to get mad: the farest triangle point from the arrow body
    12             var bx = centerBaseArrowX + (1/ 2/Math.sqrt(3) ) * triangleSide * (Math.sin(-this.centerAngle));
    13             var by = centerBaseArrowY + ( 1/ 2/Math.sqrt(3)) * triangleSide * (Math.cos(-this.centerAngle));
    14 
    15             //Easy , like the a point
    16             var cx = centerBaseArrowX - (triangleSide / 2) * Math.cos(this.centerAngle);
    17             var cy = centerBaseArrowY - (triangleSide / 2) * Math.sin(this.centerAngle);
    18 
    19             context.beginPath();
    20             //We move to the center of the base
    21             context.moveTo(centerBaseArrowX, centerBaseArrowY);
    22             context.lineTo(ax, ay);
    23             context.lineTo(bx, by);
    24             context.lineTo(cx, cy);
    25             context.lineTo(centerBaseArrowX, centerBaseArrowY);
    26             context.fill();
    27         }

    在canvas上绘制多边形的方法

            drawPath(context: CanvasRenderingContext2D) {
                for (var ixVertex = 0; ixVertex <= this.nPoints; ++ixVertex) {
                    var angle = ixVertex * 2 * Math.PI / this.nPoints - this.startAngle;
                    var point = new Point(this.centerPoint.x + this.radius * Math.cos(angle), this.centerPoint.y + this.radius * Math.sin(angle));
                    context.lineTo(point.x, point.y);
                }
            }

    在canvas上绘制五角星等多角形的方法

    1         drawPath(context:CanvasRenderingContext2D) {
    2             for (var ixVertex = 0; ixVertex <= 2 * this.nPoints; ++ixVertex) {
    3                 var angle = ixVertex * Math.PI / this.nPoints - Math.PI / 2;
    4                 var radius = ixVertex % 2 == 0 ? this.outerRadius : this.innerRadius;
    5                 context.lineTo(this.centerPoint.x + radius * Math.cos(angle), this.centerPoint.y + radius * Math.sin(angle));
    6             }
    7         }
  • 相关阅读:
    与你的领导意见不一致时你会怎么做?
    调用caffe脚本将图片转换为了lmdb格式
    第04组 Alpha冲刺(4/4)
    第04组 Alpha冲刺(3/4)
    第04组 Alpha冲刺(2/4)
    第04组 Alpha冲刺(1/4)
    2019 SDN上机第3次作业
    2019 SDN阅读作业
    2019 SDN上机第2次作业
    2019 SDN上机第1次作业
  • 原文地址:https://www.cnblogs.com/fangsmile/p/6273699.html
Copyright © 2020-2023  润新知