SCanvasPaintEngine.ts 20 KB

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