SCanvasPaintEngine.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. import { SPaintEngine } from "./SPaintEngine";
  2. import {
  3. SBrushType,
  4. SGradientStop,
  5. SLine,
  6. SLinearGradient,
  7. SLineCapStyle,
  8. SPaintEngineType,
  9. SPoint,
  10. SRadialGradient,
  11. SRect,
  12. STextAlign,
  13. STextBaseLine,
  14. STextDirection
  15. } from "..";
  16. import { SPath2D } from "../SPath2D";
  17. /**
  18. * Canvas绘制引擎基类
  19. *
  20. * @author 庞利祥(sybotan@126.com)
  21. */
  22. export class SCanvasPaintEngine extends SPaintEngine {
  23. /** 画布对象 */
  24. private readonly _canvas: CanvasRenderingContext2D;
  25. /** 融合类型 */
  26. static gcoList = [
  27. "copy",
  28. "destination-atop",
  29. "destination-in",
  30. "destination-out",
  31. "destination-over",
  32. "lighter",
  33. "source-atop",
  34. "source-in",
  35. "source-out",
  36. "source-over",
  37. "xor"
  38. ];
  39. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  40. // 属性定义
  41. /**
  42. * 绘制引擎类型
  43. *
  44. * @return 返回Canvas绘制引擎类型
  45. */
  46. get type(): SPaintEngineType {
  47. return SPaintEngineType.Canvas;
  48. } // Get type
  49. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  50. // 构造函数
  51. /**
  52. * 构造函数
  53. *
  54. * @param canvas canvas对象
  55. */
  56. constructor(canvas: CanvasRenderingContext2D) {
  57. super();
  58. this._canvas = canvas;
  59. this._canvas.imageSmoothingEnabled = true;
  60. // this._canvas.imageSmoothingQuality;
  61. } // Constructor()
  62. // =================================================================================================================
  63. // 绘制图形
  64. /**
  65. * 设置裁剪路径
  66. *
  67. * @param path 裁剪路径
  68. */
  69. setClip(path: Path2D): void {
  70. this.setMatrix();
  71. // this._canvas.stroke(path);
  72. this._canvas.fill(path);
  73. this._canvas.clip();
  74. } // Function setClip()
  75. /**
  76. * 清空矩形区域
  77. *
  78. * @param rect 矩形
  79. */
  80. clearRect(rect: SRect): void {
  81. this.setMatrix();
  82. this._canvas.clearRect(rect.x, rect.y, rect.width, rect.height);
  83. } // Function clearRect()
  84. /**
  85. * 绘制空心矩形
  86. *
  87. * @param rect 矩形
  88. */
  89. drawRect(rect: SRect): void {
  90. this.setMatrix();
  91. this.setPen();
  92. this.setBrush();
  93. this.setComposite();
  94. this.setShadow();
  95. this._canvas.fillRect(rect.x, rect.y, rect.width, rect.height);
  96. this._canvas.strokeRect(rect.x, rect.y, rect.width, rect.height);
  97. } // Function drawRect()
  98. /**
  99. * 绘制圆形
  100. *
  101. * @param cx 圆心X坐标
  102. * @param cy 圆心X坐标
  103. * @param r 圆半径
  104. */
  105. drawCircle(cx: number, cy: number, r: number): void {
  106. this.setMatrix();
  107. this.setPen();
  108. this.setBrush();
  109. this.setComposite();
  110. this.setShadow();
  111. this._canvas.beginPath();
  112. this._canvas.arc(cx, cy, r, 0, 2 * Math.PI, true);
  113. this._canvas.fill();
  114. this._canvas.stroke();
  115. } // Function drawCircle()
  116. /**
  117. * 绘制椭圆
  118. *
  119. * @param cx 圆点X坐标
  120. * @param cy 圆点Y坐标
  121. * @param rx 水平半径
  122. * @param ry 垂直半径
  123. */
  124. drawEllipse(cx: number, cy: number, rx: number, ry: number): void {
  125. this.setMatrix();
  126. this.setPen();
  127. this.setBrush();
  128. this.setComposite();
  129. this.setShadow();
  130. this._canvas.beginPath();
  131. this._canvas.ellipse(cx, cy, rx, ry, 0.5, 0, 2 * Math.PI, true);
  132. } // Function drawEllipse()
  133. /**
  134. * 绘制椭圆弧
  135. *
  136. * @param x 椭圆所在矩形X坐标
  137. * @param y 椭圆所在矩形Y坐标
  138. * @param width 椭圆所在矩形宽度
  139. * @param height 椭圆所在矩形高度
  140. * @param startAngle 开始角度(单位弧度)
  141. * @param endAngle 结束角度(单位弧度)
  142. */
  143. drawArc(
  144. x: number,
  145. y: number,
  146. width: number,
  147. height: number,
  148. startAngle: number,
  149. endAngle: number
  150. ): void {
  151. this.setMatrix();
  152. this.setPen();
  153. this.setBrush();
  154. this.setComposite();
  155. this.setShadow();
  156. let p = SPath2D.arc(x, y, width, height, startAngle, endAngle);
  157. let path = new Path2D(
  158. SPath2D.arc(x, y, width, height, startAngle, endAngle)
  159. );
  160. this._canvas.stroke(path);
  161. } // Function drawArc()
  162. /**
  163. * 绘制椭圆弦弧
  164. *
  165. * @param x 椭圆所在矩形X坐标
  166. * @param y 椭圆所在矩形Y坐标
  167. * @param width 椭圆所在矩形宽度
  168. * @param height 椭圆所在矩形高度
  169. * @param startAngle 开始角度(单位弧度)
  170. * @param endAngle 结束角度(单位弧度)
  171. */
  172. drawChord(
  173. x: number,
  174. y: number,
  175. width: number,
  176. height: number,
  177. startAngle: number,
  178. endAngle: number
  179. ): void {
  180. this.setMatrix();
  181. this.setPen();
  182. this.setBrush();
  183. this.setComposite();
  184. this.setShadow();
  185. let path = new Path2D(
  186. SPath2D.chord(x, y, width, height, startAngle, endAngle)
  187. );
  188. this._canvas.fill(path);
  189. this._canvas.stroke(path);
  190. } // Function drawChord()
  191. /**
  192. * 绘制椭圆饼
  193. *
  194. * @param x 椭圆所在矩形X坐标
  195. * @param y 椭圆所在矩形Y坐标
  196. * @param width 椭圆所在矩形宽度
  197. * @param height 椭圆所在矩形高度
  198. * @param startAngle 开始角度(单位弧度)
  199. * @param endAngle 结束角度(单位弧度)
  200. */
  201. drawPie(
  202. x: number,
  203. y: number,
  204. width: number,
  205. height: number,
  206. startAngle: number,
  207. endAngle: number
  208. ): void {
  209. this.setMatrix();
  210. this.setPen();
  211. this.setBrush();
  212. this.setComposite();
  213. this.setShadow();
  214. let path = new Path2D(
  215. SPath2D.pie(x, y, width, height, startAngle, endAngle)
  216. );
  217. this._canvas.fill(path);
  218. this._canvas.stroke(path);
  219. } // Function drawPie()
  220. /**
  221. * 绘制线段
  222. *
  223. * @param line 线段
  224. */
  225. drawLine(line: SLine): void {
  226. this.setMatrix();
  227. this.setPen();
  228. this.setBrush();
  229. this.setComposite();
  230. this.setShadow();
  231. this._canvas.beginPath();
  232. this._canvas.moveTo(line.x1, line.y1);
  233. this._canvas.lineTo(line.x2, line.y2);
  234. this._canvas.stroke();
  235. } // Function drawLine()
  236. /**
  237. * 绘制折线
  238. *
  239. * @param points 折线折点
  240. */
  241. drawPolyline(points: SPoint[]): void {
  242. // 折线至少要有2个节点
  243. if (points.length < 2) {
  244. return;
  245. }
  246. this.setMatrix();
  247. this.setPen();
  248. this.setBrush();
  249. this.setComposite();
  250. this.setShadow();
  251. this._canvas.beginPath();
  252. this._canvas.moveTo(points[0].x, points[0].y);
  253. for (let p of points) {
  254. this._canvas.lineTo(p.x, p.y);
  255. }
  256. this._canvas.stroke();
  257. } // Function drawPolyline()
  258. /**
  259. * 绘制多边形
  260. *
  261. * @param points 多边形顶点
  262. */
  263. drawPolygon(points: SPoint[]): void {
  264. // 多边形至少要有3个节点
  265. if (points.length < 3) {
  266. return;
  267. }
  268. this.setMatrix();
  269. this.setPen();
  270. this.setBrush();
  271. this.setComposite();
  272. this.setShadow();
  273. this._canvas.beginPath();
  274. this._canvas.moveTo(points[0].x, points[0].y);
  275. for (let p of points) {
  276. this._canvas.lineTo(p.x, p.y);
  277. }
  278. this._canvas.closePath();
  279. this._canvas.fill();
  280. this._canvas.stroke();
  281. } // Function drawPolygon()
  282. /**
  283. * 绘制路径
  284. *
  285. * @param path 路径
  286. */
  287. drawPath(path: SPath2D): void {
  288. this.setMatrix();
  289. this.setPen();
  290. this.setBrush();
  291. this.setComposite();
  292. this.setShadow();
  293. this._canvas.fill(path._path);
  294. this._canvas.stroke(path._path);
  295. } // Function drawPath()
  296. /**
  297. * 绘制文本
  298. *
  299. * @param text 文本内容
  300. * @param x X坐标
  301. * @param y Y坐标
  302. * @param maxWidth 最大宽度
  303. */
  304. drawText(text: string, x: number, y: number, maxWidth?: number): void {
  305. this.setMatrix();
  306. this.setPen();
  307. this.setBrush();
  308. this.setFont();
  309. this.setComposite();
  310. this.setShadow();
  311. if (maxWidth == undefined) {
  312. this._canvas.fillText(text, x, y);
  313. } else {
  314. this._canvas.fillText(text, x, y, maxWidth);
  315. }
  316. } // Function drawText()
  317. /**
  318. * 绘制图片
  319. *
  320. * @param img 图片
  321. * @param x X坐标
  322. * @param y Y坐标
  323. * @param width 宽度
  324. * @param height 高度
  325. */
  326. drawImage(
  327. img: CanvasImageSource,
  328. x: number,
  329. y: number,
  330. width?: number,
  331. height?: number
  332. ): void {
  333. this.setMatrix();
  334. if (width == undefined) {
  335. this._canvas.drawImage(img, x, y);
  336. } else {
  337. this._canvas.drawImage(img, x, y, width, height as number);
  338. }
  339. } // Function drawImage()
  340. /**
  341. * 预测量文本宽度
  342. *
  343. * @param text 预测的文本
  344. * @return 文本长度像素
  345. * */
  346. textWidth(text: string): number {
  347. return this._canvas.measureText(text).width;
  348. } // Function textWidth()
  349. // /**
  350. // * 绘制带导角空心矩形
  351. // *
  352. // * @param x X坐标
  353. // * @param y Y坐标
  354. // * @param w 宽度
  355. // * @param h 高度
  356. // * @param r 导角半径
  357. // */
  358. // drawRoundedRect(x: number, y: number, w: number, h: number, r: number): void {
  359. // this.canvas.beginPath();
  360. // this.canvas.moveTo(x, y + r);
  361. // this.canvas.lineTo(x, y + h - r);
  362. // this.canvas.quadraticCurveTo(x, y + h, x + r, y + h);
  363. // this.canvas.lineTo(x + w - r,y + h);
  364. // this.canvas.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
  365. // this.canvas.lineTo(x + w, y + r);
  366. // this.canvas.quadraticCurveTo(x + w, y, x + w - r, y);
  367. // this.canvas.lineTo(x + r, y);
  368. // this.canvas.quadraticCurveTo(x, y, x,y + r);
  369. // this.canvas.fill();
  370. // this.canvas.stroke();
  371. // } // Function drawRoundedRect()
  372. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  373. // 私有函数
  374. /**
  375. * 设置画笔
  376. */
  377. private setPen(): void {
  378. //this._canvas.strokeStyle = this.state.pen.color.value;
  379. this._canvas.strokeStyle = `rgba(${this.state.pen.color.red}, ${
  380. this.state.pen.color.green
  381. }, ${this.state.pen.color.blue}, ${this.state.pen.color.alpha /
  382. 255.0})`;
  383. this._canvas.lineWidth = this.state.pen.lineWidth;
  384. this._canvas.miterLimit = this.state.pen.miterLimit;
  385. if (this.state.pen.lineDash != null) {
  386. this._canvas.setLineDash(this.state.pen.lineDash);
  387. this._canvas.lineDashOffset = this.state.pen.dashOffset;
  388. } else {
  389. this._canvas.setLineDash([]);
  390. this._canvas.lineDashOffset = 0;
  391. }
  392. this.setLineCapStyle(this.state.pen.lineCapStyle);
  393. } // Function setPen()
  394. /**
  395. * 线段端点风格
  396. *
  397. * @param style 风格
  398. */
  399. private setLineCapStyle(style: SLineCapStyle): void {
  400. if (style == undefined) return;
  401. if (style == SLineCapStyle.Round) {
  402. this._canvas.lineCap = "round";
  403. } else if (style == SLineCapStyle.Square) {
  404. this._canvas.lineCap = "square";
  405. } else {
  406. this._canvas.lineCap = "butt";
  407. }
  408. } // Set lineCapStyle
  409. /**
  410. * 设置画刷
  411. */
  412. private setBrush(): void {
  413. // this._canvas.fillStyle = this.state.brush.color.value;
  414. if (this.state.brush.type == SBrushType.Color) {
  415. this._canvas.fillStyle = `rgba(${this.state.brush.color.red}, ${
  416. this.state.brush.color.green
  417. }, ${this.state.brush.color.blue}, ${this.state.brush.color.alpha /
  418. 255.0})`;
  419. } else if (this.state.brush.type == SBrushType.Gradient) {
  420. let gradient = this.state.brush.gradient,
  421. drawGradient: CanvasGradient;
  422. if (gradient instanceof SLinearGradient) {
  423. drawGradient = this._canvas.createLinearGradient(
  424. gradient.x1,
  425. gradient.y1,
  426. gradient.x2,
  427. gradient.y2
  428. );
  429. } else if (gradient instanceof SRadialGradient) {
  430. drawGradient = this._canvas.createRadialGradient(
  431. gradient.x1,
  432. gradient.y1,
  433. gradient.r1,
  434. gradient.x2,
  435. gradient.y2,
  436. gradient.r2
  437. );
  438. }
  439. // @ts-ignore
  440. if (gradient && drawGradient) {
  441. gradient.stopList.forEach((t: SGradientStop): void => {
  442. drawGradient.addColorStop(
  443. t.pos,
  444. `rgba(${t.color.red},${t.color.green},${
  445. t.color.blue
  446. },${t.color.alpha / 255.0})`
  447. );
  448. });
  449. this._canvas.fillStyle = drawGradient;
  450. }
  451. }
  452. } // Function setBrush()
  453. /**
  454. * 设置融合
  455. */
  456. private setComposite(): void {
  457. this._canvas.globalCompositeOperation =
  458. SCanvasPaintEngine.gcoList[this.state._composite];
  459. } // Function setComposite()
  460. /**
  461. * 设置阴影
  462. */
  463. private setShadow(): void {
  464. if (this.state.shadow.shadowBlur > 0 && this.state.shadow.shadowColor) {
  465. this._canvas.shadowBlur = this.state.shadow.shadowBlur;
  466. this._canvas.shadowColor = `rgba(${
  467. this.state.shadow.shadowColor.red
  468. },${this.state.shadow.shadowColor.green}, ${
  469. this.state.shadow.shadowColor.blue
  470. }, ${this.state.shadow.shadowColor.alpha / 255.0})`;
  471. this._canvas.shadowOffsetX = this.state.shadow.shadowOffsetX;
  472. this._canvas.shadowOffsetY = this.state.shadow.shadowOffsetY;
  473. } else {
  474. this._canvas.shadowBlur = 0;
  475. this._canvas.shadowColor = "";
  476. this._canvas.shadowOffsetX = 0;
  477. this._canvas.shadowOffsetY = 0;
  478. }
  479. } // Function setShadow()
  480. /**
  481. * 设置字体
  482. */
  483. private setFont(): void {
  484. this._canvas.font = `${this.state.font.size}px ${this.state.font.name}`;
  485. this.setTextAlign(this.state.font.textAlign);
  486. this.setBaseLine(this.state.font.textBaseLine);
  487. this.setTextDirection(this.state.font.textDirection);
  488. } // Function setFont()
  489. /**
  490. * 文本对齐选项
  491. *
  492. * @param value 对齐方式
  493. */
  494. private setTextAlign(value: STextAlign): void {
  495. if (value == undefined) {
  496. return;
  497. }
  498. if (value === STextAlign.Start) {
  499. this._canvas.textAlign = "start";
  500. } else if (value === STextAlign.End) {
  501. this._canvas.textAlign = "end";
  502. } else if (value === STextAlign.Left) {
  503. this._canvas.textAlign = "left";
  504. } else if (value === STextAlign.Center) {
  505. this._canvas.textAlign = "center";
  506. } else {
  507. this._canvas.textAlign = "right";
  508. }
  509. } // Function setTextAlign()
  510. /**
  511. * 设置文本基线对齐选项
  512. *
  513. * @param value 对齐方式
  514. */
  515. private setBaseLine(value: STextBaseLine): void {
  516. if (value == undefined) {
  517. return;
  518. }
  519. if (value == STextBaseLine.Alphabetic) {
  520. this._canvas.textBaseline = "alphabetic";
  521. } else if (value == STextBaseLine.Top) {
  522. this._canvas.textBaseline = "top";
  523. } else if (value == STextBaseLine.Hanging) {
  524. this._canvas.textBaseline = "hanging";
  525. } else if (value == STextBaseLine.Middle) {
  526. this._canvas.textBaseline = "middle";
  527. } else if (value == STextBaseLine.Ideographic) {
  528. this._canvas.textBaseline = "ideographic";
  529. } else {
  530. this._canvas.textBaseline = "bottom";
  531. }
  532. } // Set textBaseLine()
  533. /**
  534. * 设置文本方向选项
  535. *
  536. * @param value 文本方向
  537. */
  538. private setTextDirection(value: STextDirection): void {
  539. if (value == undefined) {
  540. return;
  541. }
  542. if (value == STextDirection.Inherit) {
  543. this._canvas.direction = "inherit";
  544. } else if (value == STextDirection.LTR) {
  545. this._canvas.direction = "ltr";
  546. } else {
  547. this._canvas.direction = "rtl";
  548. }
  549. } // Set textDirection
  550. /**
  551. * 设置变型矩阵
  552. */
  553. private setMatrix(): void {
  554. this._canvas.setTransform(
  555. this.state.matrix.a,
  556. this.state.matrix.b,
  557. this.state.matrix.c,
  558. this.state.matrix.d,
  559. this.state.matrix.e,
  560. this.state.matrix.f
  561. );
  562. } // Function setMatrix()
  563. } // class SPaintEngine