|
@@ -0,0 +1,490 @@
|
|
|
+import { SPaintEngine } from "./SPaintEngine";
|
|
|
+import {
|
|
|
+ SLine,
|
|
|
+ SLineCapStyle,
|
|
|
+ SPaintEngineType,
|
|
|
+ SPoint,
|
|
|
+ SRect,
|
|
|
+ STextAlign,
|
|
|
+ STextBaseLine,
|
|
|
+ STextDirection
|
|
|
+} from "..";
|
|
|
+import { SPath2D } from "../SPath2D";
|
|
|
+
|
|
|
+/**
|
|
|
+ * Canvas绘制引擎基类
|
|
|
+ *
|
|
|
+ * @author 庞利祥(sybotan@126.com)
|
|
|
+ */
|
|
|
+export class SCanvasPaintEngine extends SPaintEngine {
|
|
|
+ /** 画布对象 */
|
|
|
+ private readonly _canvas: CanvasRenderingContext2D;
|
|
|
+
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
+ // 属性定义
|
|
|
+ /**
|
|
|
+ * 绘制引擎类型
|
|
|
+ *
|
|
|
+ * @return 返回Canvas绘制引擎类型
|
|
|
+ */
|
|
|
+ get type(): SPaintEngineType {
|
|
|
+ return SPaintEngineType.Canvas;
|
|
|
+ } // Get type
|
|
|
+
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
+ // 构造函数
|
|
|
+ /**
|
|
|
+ * 构造函数
|
|
|
+ *
|
|
|
+ * @param canvas canvas对象
|
|
|
+ */
|
|
|
+ constructor(canvas: CanvasRenderingContext2D) {
|
|
|
+ super();
|
|
|
+ this._canvas = canvas;
|
|
|
+ this._canvas.imageSmoothingEnabled = true;
|
|
|
+ // this._canvas.imageSmoothingQuality;
|
|
|
+ } // Constructor()
|
|
|
+
|
|
|
+ // =================================================================================================================
|
|
|
+ // 绘制图形
|
|
|
+ /**
|
|
|
+ * 设置裁剪路径
|
|
|
+ *
|
|
|
+ * @param path 裁剪路径
|
|
|
+ */
|
|
|
+ setClip(path: Path2D): void {
|
|
|
+ this.setMatrix();
|
|
|
+ // this._canvas.stroke(path);
|
|
|
+ this._canvas.fill(path);
|
|
|
+ this._canvas.clip();
|
|
|
+ } // Function setClip()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清空矩形区域
|
|
|
+ *
|
|
|
+ * @param rect 矩形
|
|
|
+ */
|
|
|
+ clearRect(rect: SRect): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this._canvas.clearRect(rect.x, rect.y, rect.width, rect.height);
|
|
|
+ } // Function clearRect()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制空心矩形
|
|
|
+ *
|
|
|
+ * @param rect 矩形
|
|
|
+ */
|
|
|
+ drawRect(rect: SRect): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ this._canvas.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
|
+ this._canvas.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
|
|
+ } // Function drawRect()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制圆形
|
|
|
+ *
|
|
|
+ * @param cx 圆心X坐标
|
|
|
+ * @param cy 圆心X坐标
|
|
|
+ * @param r 圆半径
|
|
|
+ */
|
|
|
+ drawCircle(cx: number, cy: number, r: number): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ this._canvas.beginPath();
|
|
|
+ this._canvas.arc(cx, cy, r, 0, 2 * Math.PI, true);
|
|
|
+ this._canvas.fill();
|
|
|
+ this._canvas.stroke();
|
|
|
+ } // Function drawCircle()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制椭圆
|
|
|
+ *
|
|
|
+ * @param cx 圆点X坐标
|
|
|
+ * @param cy 圆点Y坐标
|
|
|
+ * @param rx 水平半径
|
|
|
+ * @param ry 垂直半径
|
|
|
+ */
|
|
|
+ drawEllipse(cx: number, cy: number, rx: number, ry: number): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ this._canvas.beginPath();
|
|
|
+ this._canvas.ellipse(cx, cy, rx, ry, 0.5, 0, 2 * Math.PI, true);
|
|
|
+ } // Function drawEllipse()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制椭圆弧
|
|
|
+ *
|
|
|
+ * @param x 椭圆所在矩形X坐标
|
|
|
+ * @param y 椭圆所在矩形Y坐标
|
|
|
+ * @param width 椭圆所在矩形宽度
|
|
|
+ * @param height 椭圆所在矩形高度
|
|
|
+ * @param startAngle 开始角度(单位弧度)
|
|
|
+ * @param endAngle 结束角度(单位弧度)
|
|
|
+ */
|
|
|
+ drawArc(
|
|
|
+ x: number,
|
|
|
+ y: number,
|
|
|
+ width: number,
|
|
|
+ height: number,
|
|
|
+ startAngle: number,
|
|
|
+ endAngle: number
|
|
|
+ ): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ let p = SPath2D.arc(x, y, width, height, startAngle, endAngle);
|
|
|
+ let path = new Path2D(
|
|
|
+ SPath2D.arc(x, y, width, height, startAngle, endAngle)
|
|
|
+ );
|
|
|
+ this._canvas.stroke(path);
|
|
|
+ } // Function drawArc()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制椭圆弦弧
|
|
|
+ *
|
|
|
+ * @param x 椭圆所在矩形X坐标
|
|
|
+ * @param y 椭圆所在矩形Y坐标
|
|
|
+ * @param width 椭圆所在矩形宽度
|
|
|
+ * @param height 椭圆所在矩形高度
|
|
|
+ * @param startAngle 开始角度(单位弧度)
|
|
|
+ * @param endAngle 结束角度(单位弧度)
|
|
|
+ */
|
|
|
+ drawChord(
|
|
|
+ x: number,
|
|
|
+ y: number,
|
|
|
+ width: number,
|
|
|
+ height: number,
|
|
|
+ startAngle: number,
|
|
|
+ endAngle: number
|
|
|
+ ): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ let path = new Path2D(
|
|
|
+ SPath2D.chord(x, y, width, height, startAngle, endAngle)
|
|
|
+ );
|
|
|
+
|
|
|
+ this._canvas.fill(path);
|
|
|
+ this._canvas.stroke(path);
|
|
|
+ } // Function drawChord()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制椭圆饼
|
|
|
+ *
|
|
|
+ * @param x 椭圆所在矩形X坐标
|
|
|
+ * @param y 椭圆所在矩形Y坐标
|
|
|
+ * @param width 椭圆所在矩形宽度
|
|
|
+ * @param height 椭圆所在矩形高度
|
|
|
+ * @param startAngle 开始角度(单位弧度)
|
|
|
+ * @param endAngle 结束角度(单位弧度)
|
|
|
+ */
|
|
|
+ drawPie(
|
|
|
+ x: number,
|
|
|
+ y: number,
|
|
|
+ width: number,
|
|
|
+ height: number,
|
|
|
+ startAngle: number,
|
|
|
+ endAngle: number
|
|
|
+ ): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ let path = new Path2D(
|
|
|
+ SPath2D.pie(x, y, width, height, startAngle, endAngle)
|
|
|
+ );
|
|
|
+ this._canvas.fill(path);
|
|
|
+ this._canvas.stroke(path);
|
|
|
+ } // Function drawPie()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制线段
|
|
|
+ *
|
|
|
+ * @param line 线段
|
|
|
+ */
|
|
|
+ drawLine(line: SLine): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ this._canvas.beginPath();
|
|
|
+ this._canvas.moveTo(line.x1, line.y1);
|
|
|
+ this._canvas.lineTo(line.x2, line.y2);
|
|
|
+ this._canvas.stroke();
|
|
|
+ } // Function drawLine()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制折线
|
|
|
+ *
|
|
|
+ * @param points 折线折点
|
|
|
+ */
|
|
|
+ drawPolyline(points: SPoint[]): void {
|
|
|
+ // 折线至少要有2个节点
|
|
|
+ if (points.length < 2) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+
|
|
|
+ this._canvas.beginPath();
|
|
|
+ this._canvas.moveTo(points[0].x, points[0].y);
|
|
|
+ for (let p of points) {
|
|
|
+ this._canvas.lineTo(p.x, p.y);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._canvas.stroke();
|
|
|
+ } // Function drawPolyline()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制多边形
|
|
|
+ *
|
|
|
+ * @param points 多边形顶点
|
|
|
+ */
|
|
|
+ drawPolygon(points: SPoint[]): void {
|
|
|
+ // 多边形至少要有3个节点
|
|
|
+ if (points.length < 3) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+
|
|
|
+ this._canvas.beginPath();
|
|
|
+ this._canvas.moveTo(points[0].x, points[0].y);
|
|
|
+ for (let p of points) {
|
|
|
+ this._canvas.lineTo(p.x, p.y);
|
|
|
+ }
|
|
|
+ this._canvas.closePath();
|
|
|
+
|
|
|
+ this._canvas.fill();
|
|
|
+ this._canvas.stroke();
|
|
|
+ } // Function drawPolygon()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制路径
|
|
|
+ *
|
|
|
+ * @param path 路径
|
|
|
+ */
|
|
|
+ drawPath(path: SPath2D): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+
|
|
|
+ this._canvas.fill(path._path);
|
|
|
+ this._canvas.stroke(path._path);
|
|
|
+ } // Function drawPath()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制文本
|
|
|
+ *
|
|
|
+ * @param text 文本内容
|
|
|
+ * @param x X坐标
|
|
|
+ * @param y Y坐标
|
|
|
+ * @param maxWidth 最大宽度
|
|
|
+ */
|
|
|
+ drawText(text: string, x: number, y: number, maxWidth?: number): void {
|
|
|
+ this.setMatrix();
|
|
|
+ this.setPen();
|
|
|
+ this.setBrush();
|
|
|
+ this.setFont();
|
|
|
+
|
|
|
+ if (maxWidth == undefined) {
|
|
|
+ this._canvas.fillText(text, x, y);
|
|
|
+ } else {
|
|
|
+ this._canvas.fillText(text, x, y, maxWidth);
|
|
|
+ }
|
|
|
+ } // Function drawText()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制图片
|
|
|
+ *
|
|
|
+ * @param img 图片
|
|
|
+ * @param x X坐标
|
|
|
+ * @param y Y坐标
|
|
|
+ * @param width 宽度
|
|
|
+ * @param height 高度
|
|
|
+ */
|
|
|
+ drawImage(
|
|
|
+ img: CanvasImageSource,
|
|
|
+ x: number,
|
|
|
+ y: number,
|
|
|
+ width?: number,
|
|
|
+ height?: number
|
|
|
+ ): void {
|
|
|
+ this.setMatrix();
|
|
|
+ if (width == undefined) {
|
|
|
+ this._canvas.drawImage(img, x, y);
|
|
|
+ } else {
|
|
|
+ this._canvas.drawImage(img, x, y, width, height as number);
|
|
|
+ }
|
|
|
+ } // Function drawImage()
|
|
|
+
|
|
|
+ // /**
|
|
|
+ // * 绘制带导角空心矩形
|
|
|
+ // *
|
|
|
+ // * @param x X坐标
|
|
|
+ // * @param y Y坐标
|
|
|
+ // * @param w 宽度
|
|
|
+ // * @param h 高度
|
|
|
+ // * @param r 导角半径
|
|
|
+ // */
|
|
|
+ // drawRoundedRect(x: number, y: number, w: number, h: number, r: number): void {
|
|
|
+ // this.canvas.beginPath();
|
|
|
+ // this.canvas.moveTo(x, y + r);
|
|
|
+ // this.canvas.lineTo(x, y + h - r);
|
|
|
+ // this.canvas.quadraticCurveTo(x, y + h, x + r, y + h);
|
|
|
+ // this.canvas.lineTo(x + w - r,y + h);
|
|
|
+ // this.canvas.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
|
|
|
+ // this.canvas.lineTo(x + w, y + r);
|
|
|
+ // this.canvas.quadraticCurveTo(x + w, y, x + w - r, y);
|
|
|
+ // this.canvas.lineTo(x + r, y);
|
|
|
+ // this.canvas.quadraticCurveTo(x, y, x,y + r);
|
|
|
+ // this.canvas.fill();
|
|
|
+ // this.canvas.stroke();
|
|
|
+ // } // Function drawRoundedRect()
|
|
|
+
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
+ // 私有函数
|
|
|
+ /**
|
|
|
+ * 设置画笔
|
|
|
+ */
|
|
|
+ private setPen(): void {
|
|
|
+ //this._canvas.strokeStyle = this.state.pen.color.value;
|
|
|
+ this._canvas.strokeStyle = `rgba(${this.state.pen.color.red}, ${
|
|
|
+ this.state.pen.color.green
|
|
|
+ }, ${this.state.pen.color.blue}, ${this.state.pen.color.alpha /
|
|
|
+ 255.0})`;
|
|
|
+
|
|
|
+ this._canvas.lineWidth = this.state.pen.lineWidth;
|
|
|
+ this._canvas.miterLimit = this.state.pen.miterLimit;
|
|
|
+
|
|
|
+ if (this.state.pen.lineDash != null) {
|
|
|
+ this._canvas.setLineDash(this.state.pen.lineDash);
|
|
|
+ this._canvas.lineDashOffset = this.state.pen.dashOffset;
|
|
|
+ } else {
|
|
|
+ this._canvas.setLineDash([]);
|
|
|
+ this._canvas.lineDashOffset = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setLineCapStyle(this.state.pen.lineCapStyle);
|
|
|
+ } // Function setPen()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 线段端点风格
|
|
|
+ *
|
|
|
+ * @param canvas canvas对象
|
|
|
+ * @param style 风格
|
|
|
+ */
|
|
|
+ private setLineCapStyle(style: SLineCapStyle): void {
|
|
|
+ // TODO: 找原因,去下边一行
|
|
|
+ if (style == undefined) return;
|
|
|
+ if (style == SLineCapStyle.Round) {
|
|
|
+ this._canvas.lineCap = "round";
|
|
|
+ } else if (style == SLineCapStyle.Square) {
|
|
|
+ this._canvas.lineCap = "square";
|
|
|
+ } else {
|
|
|
+ this._canvas.lineCap = "butt";
|
|
|
+ }
|
|
|
+ } // Set lineCapStyle
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置画刷
|
|
|
+ */
|
|
|
+ private setBrush(): void {
|
|
|
+ // this._canvas.fillStyle = this.state.brush.color.value;
|
|
|
+ this._canvas.fillStyle = `rgba(${this.state.brush.color.red}, ${
|
|
|
+ this.state.brush.color.green
|
|
|
+ }, ${this.state.brush.color.blue}, ${this.state.brush.color.alpha /
|
|
|
+ 255.0})`;
|
|
|
+ } // Function setBrush()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置字体
|
|
|
+ */
|
|
|
+ private setFont(): void {
|
|
|
+ this._canvas.font = `${this.state.font.size}px ${this.state.font.name}`;
|
|
|
+ this.setTextAlign(this.state.font.textAlign);
|
|
|
+ this.setBaseLine(this.state.font.textBaseLine);
|
|
|
+ this.setTextDirection(this.state.font.textDirection);
|
|
|
+ } // Function setFont()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文本对齐选项
|
|
|
+ *
|
|
|
+ * @param style 对齐方式
|
|
|
+ */
|
|
|
+ private setTextAlign(value: STextAlign): void {
|
|
|
+ // TODO: 找原因,去下边一行
|
|
|
+ if (value == undefined) return;
|
|
|
+ if (value === STextAlign.Start) {
|
|
|
+ this._canvas.textAlign = "start";
|
|
|
+ } else if (value === STextAlign.End) {
|
|
|
+ this._canvas.textAlign = "end";
|
|
|
+ } else if (value === STextAlign.Left) {
|
|
|
+ this._canvas.textAlign = "left";
|
|
|
+ } else if (value === STextAlign.Center) {
|
|
|
+ this._canvas.textAlign = "center";
|
|
|
+ } else {
|
|
|
+ this._canvas.textAlign = "right";
|
|
|
+ }
|
|
|
+ } // Function setTextAlign()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置文本基线对齐选项
|
|
|
+ *
|
|
|
+ * @param value 对齐方式
|
|
|
+ */
|
|
|
+ private setBaseLine(value: STextBaseLine): void {
|
|
|
+ // TODO: 找原因,去下边一行
|
|
|
+ if (value == undefined) return;
|
|
|
+ if (value == STextBaseLine.Alphabetic) {
|
|
|
+ this._canvas.textBaseline = "alphabetic";
|
|
|
+ } else if (value == STextBaseLine.Top) {
|
|
|
+ this._canvas.textBaseline = "top";
|
|
|
+ } else if (value == STextBaseLine.Hanging) {
|
|
|
+ this._canvas.textBaseline = "hanging";
|
|
|
+ } else if (value == STextBaseLine.Middle) {
|
|
|
+ this._canvas.textBaseline = "middle";
|
|
|
+ } else if (value == STextBaseLine.Ideographic) {
|
|
|
+ this._canvas.textBaseline = "ideographic";
|
|
|
+ } else {
|
|
|
+ this._canvas.textBaseline = "bottom";
|
|
|
+ }
|
|
|
+ } // Set textBaseLine()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置文本方向选项
|
|
|
+ *
|
|
|
+ * @param value 文本方向
|
|
|
+ */
|
|
|
+ private setTextDirection(value: STextDirection): void {
|
|
|
+ // TODO: 找原因,去下边一行
|
|
|
+ if (value == undefined) return;
|
|
|
+ if (value == STextDirection.Inherit) {
|
|
|
+ this._canvas.direction = "inherit";
|
|
|
+ } else if (value == STextDirection.LTR) {
|
|
|
+ this._canvas.direction = "ltr";
|
|
|
+ } else {
|
|
|
+ this._canvas.direction = "rtl";
|
|
|
+ }
|
|
|
+ } // Set textDirection
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置变型矩阵
|
|
|
+ */
|
|
|
+ private setMatrix(): void {
|
|
|
+ this._canvas.setTransform(
|
|
|
+ this.state.matrix.a,
|
|
|
+ this.state.matrix.b,
|
|
|
+ this.state.matrix.c,
|
|
|
+ this.state.matrix.d,
|
|
|
+ this.state.matrix.e,
|
|
|
+ this.state.matrix.f
|
|
|
+ );
|
|
|
+ } // Function setMatrix()
|
|
|
+} // class SPaintEngine
|