SCanvasPaintEngine.ts 18 KB

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