SCanvasPaintEngine.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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._canvas.fillRect(rect.x, rect.y, rect.width, rect.height);
  95. this._canvas.strokeRect(rect.x, rect.y, rect.width, rect.height);
  96. } // Function drawRect()
  97. /**
  98. * 绘制圆形
  99. *
  100. * @param cx 圆心X坐标
  101. * @param cy 圆心X坐标
  102. * @param r 圆半径
  103. */
  104. drawCircle(cx: number, cy: number, r: number): void {
  105. this.setMatrix();
  106. this.setPen();
  107. this.setBrush();
  108. this.setComposite();
  109. this._canvas.beginPath();
  110. this._canvas.arc(cx, cy, r, 0, 2 * Math.PI, true);
  111. this._canvas.fill();
  112. this._canvas.stroke();
  113. } // Function drawCircle()
  114. /**
  115. * 绘制椭圆
  116. *
  117. * @param cx 圆点X坐标
  118. * @param cy 圆点Y坐标
  119. * @param rx 水平半径
  120. * @param ry 垂直半径
  121. */
  122. drawEllipse(cx: number, cy: number, rx: number, ry: number): void {
  123. this.setMatrix();
  124. this.setPen();
  125. this.setBrush();
  126. this.setComposite();
  127. this._canvas.beginPath();
  128. this._canvas.ellipse(cx, cy, rx, ry, 0.5, 0, 2 * Math.PI, true);
  129. } // Function drawEllipse()
  130. /**
  131. * 绘制椭圆弧
  132. *
  133. * @param x 椭圆所在矩形X坐标
  134. * @param y 椭圆所在矩形Y坐标
  135. * @param width 椭圆所在矩形宽度
  136. * @param height 椭圆所在矩形高度
  137. * @param startAngle 开始角度(单位弧度)
  138. * @param endAngle 结束角度(单位弧度)
  139. */
  140. drawArc(
  141. x: number,
  142. y: number,
  143. width: number,
  144. height: number,
  145. startAngle: number,
  146. endAngle: number
  147. ): void {
  148. this.setMatrix();
  149. this.setPen();
  150. this.setBrush();
  151. this.setComposite();
  152. let p = SPath2D.arc(x, y, width, height, startAngle, endAngle);
  153. let path = new Path2D(
  154. SPath2D.arc(x, y, width, height, startAngle, endAngle)
  155. );
  156. this._canvas.stroke(path);
  157. } // Function drawArc()
  158. /**
  159. * 绘制椭圆弦弧
  160. *
  161. * @param x 椭圆所在矩形X坐标
  162. * @param y 椭圆所在矩形Y坐标
  163. * @param width 椭圆所在矩形宽度
  164. * @param height 椭圆所在矩形高度
  165. * @param startAngle 开始角度(单位弧度)
  166. * @param endAngle 结束角度(单位弧度)
  167. */
  168. drawChord(
  169. x: number,
  170. y: number,
  171. width: number,
  172. height: number,
  173. startAngle: number,
  174. endAngle: number
  175. ): void {
  176. this.setMatrix();
  177. this.setPen();
  178. this.setBrush();
  179. this.setComposite();
  180. let path = new Path2D(
  181. SPath2D.chord(x, y, width, height, startAngle, endAngle)
  182. );
  183. this._canvas.fill(path);
  184. this._canvas.stroke(path);
  185. } // Function drawChord()
  186. /**
  187. * 绘制椭圆饼
  188. *
  189. * @param x 椭圆所在矩形X坐标
  190. * @param y 椭圆所在矩形Y坐标
  191. * @param width 椭圆所在矩形宽度
  192. * @param height 椭圆所在矩形高度
  193. * @param startAngle 开始角度(单位弧度)
  194. * @param endAngle 结束角度(单位弧度)
  195. */
  196. drawPie(
  197. x: number,
  198. y: number,
  199. width: number,
  200. height: number,
  201. startAngle: number,
  202. endAngle: number
  203. ): void {
  204. this.setMatrix();
  205. this.setPen();
  206. this.setBrush();
  207. this.setComposite();
  208. let path = new Path2D(
  209. SPath2D.pie(x, y, width, height, startAngle, endAngle)
  210. );
  211. this._canvas.fill(path);
  212. this._canvas.stroke(path);
  213. } // Function drawPie()
  214. /**
  215. * 绘制线段
  216. *
  217. * @param line 线段
  218. */
  219. drawLine(line: SLine): void {
  220. this.setMatrix();
  221. this.setPen();
  222. this.setBrush();
  223. this.setComposite();
  224. this._canvas.beginPath();
  225. this._canvas.moveTo(line.x1, line.y1);
  226. this._canvas.lineTo(line.x2, line.y2);
  227. this._canvas.stroke();
  228. } // Function drawLine()
  229. /**
  230. * 绘制折线
  231. *
  232. * @param points 折线折点
  233. */
  234. drawPolyline(points: SPoint[]): void {
  235. // 折线至少要有2个节点
  236. if (points.length < 2) {
  237. return;
  238. }
  239. this.setMatrix();
  240. this.setPen();
  241. this.setBrush();
  242. this.setComposite();
  243. this._canvas.beginPath();
  244. this._canvas.moveTo(points[0].x, points[0].y);
  245. for (let p of points) {
  246. this._canvas.lineTo(p.x, p.y);
  247. }
  248. this._canvas.stroke();
  249. } // Function drawPolyline()
  250. /**
  251. * 绘制多边形
  252. *
  253. * @param points 多边形顶点
  254. */
  255. drawPolygon(points: SPoint[]): void {
  256. // 多边形至少要有3个节点
  257. if (points.length < 3) {
  258. return;
  259. }
  260. this.setMatrix();
  261. this.setPen();
  262. this.setBrush();
  263. this.setComposite();
  264. this._canvas.beginPath();
  265. this._canvas.moveTo(points[0].x, points[0].y);
  266. for (let p of points) {
  267. this._canvas.lineTo(p.x, p.y);
  268. }
  269. this._canvas.closePath();
  270. this._canvas.fill();
  271. this._canvas.stroke();
  272. } // Function drawPolygon()
  273. /**
  274. * 绘制路径
  275. *
  276. * @param path 路径
  277. */
  278. drawPath(path: SPath2D): void {
  279. this.setMatrix();
  280. this.setPen();
  281. this.setBrush();
  282. this.setComposite();
  283. this._canvas.fill(path._path);
  284. this._canvas.stroke(path._path);
  285. } // Function drawPath()
  286. /**
  287. * 绘制文本
  288. *
  289. * @param text 文本内容
  290. * @param x X坐标
  291. * @param y Y坐标
  292. * @param maxWidth 最大宽度
  293. */
  294. drawText(text: string, x: number, y: number, maxWidth?: number): void {
  295. this.setMatrix();
  296. this.setPen();
  297. this.setBrush();
  298. this.setFont();
  299. this.setComposite();
  300. if (maxWidth == undefined) {
  301. this._canvas.fillText(text, x, y);
  302. } else {
  303. this._canvas.fillText(text, x, y, maxWidth);
  304. }
  305. } // Function drawText()
  306. /**
  307. * 绘制图片
  308. *
  309. * @param img 图片
  310. * @param x X坐标
  311. * @param y Y坐标
  312. * @param width 宽度
  313. * @param height 高度
  314. */
  315. drawImage(
  316. img: CanvasImageSource,
  317. x: number,
  318. y: number,
  319. width?: number,
  320. height?: number
  321. ): void {
  322. this.setMatrix();
  323. if (width == undefined) {
  324. this._canvas.drawImage(img, x, y);
  325. } else {
  326. this._canvas.drawImage(img, x, y, width, height as number);
  327. }
  328. } // Function drawImage()
  329. /**
  330. * 预测量文本宽度
  331. *
  332. * @param text 预测的文本
  333. * @return 文本长度像素
  334. * */
  335. textWidth(text: string): number {
  336. return this._canvas.measureText(text).width;
  337. } // Function textWidth()
  338. // /**
  339. // * 绘制带导角空心矩形
  340. // *
  341. // * @param x X坐标
  342. // * @param y Y坐标
  343. // * @param w 宽度
  344. // * @param h 高度
  345. // * @param r 导角半径
  346. // */
  347. // drawRoundedRect(x: number, y: number, w: number, h: number, r: number): void {
  348. // this.canvas.beginPath();
  349. // this.canvas.moveTo(x, y + r);
  350. // this.canvas.lineTo(x, y + h - r);
  351. // this.canvas.quadraticCurveTo(x, y + h, x + r, y + h);
  352. // this.canvas.lineTo(x + w - r,y + h);
  353. // this.canvas.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
  354. // this.canvas.lineTo(x + w, y + r);
  355. // this.canvas.quadraticCurveTo(x + w, y, x + w - r, y);
  356. // this.canvas.lineTo(x + r, y);
  357. // this.canvas.quadraticCurveTo(x, y, x,y + r);
  358. // this.canvas.fill();
  359. // this.canvas.stroke();
  360. // } // Function drawRoundedRect()
  361. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  362. // 私有函数
  363. /**
  364. * 设置画笔
  365. */
  366. private setPen(): void {
  367. //this._canvas.strokeStyle = this.state.pen.color.value;
  368. this._canvas.strokeStyle = `rgba(${this.state.pen.color.red}, ${
  369. this.state.pen.color.green
  370. }, ${this.state.pen.color.blue}, ${this.state.pen.color.alpha /
  371. 255.0})`;
  372. this._canvas.lineWidth = this.state.pen.lineWidth;
  373. this._canvas.miterLimit = this.state.pen.miterLimit;
  374. if (this.state.pen.lineDash != null) {
  375. this._canvas.setLineDash(this.state.pen.lineDash);
  376. this._canvas.lineDashOffset = this.state.pen.dashOffset;
  377. } else {
  378. this._canvas.setLineDash([]);
  379. this._canvas.lineDashOffset = 0;
  380. }
  381. this.setLineCapStyle(this.state.pen.lineCapStyle);
  382. } // Function setPen()
  383. /**
  384. * 线段端点风格
  385. *
  386. * @param style 风格
  387. */
  388. private setLineCapStyle(style: SLineCapStyle): void {
  389. // TODO: 找原因,去下边一行
  390. if (style == undefined) return;
  391. if (style == SLineCapStyle.Round) {
  392. this._canvas.lineCap = "round";
  393. } else if (style == SLineCapStyle.Square) {
  394. this._canvas.lineCap = "square";
  395. } else {
  396. this._canvas.lineCap = "butt";
  397. }
  398. } // Set lineCapStyle
  399. /**
  400. * 设置画刷
  401. */
  402. private setBrush(): void {
  403. // this._canvas.fillStyle = this.state.brush.color.value;
  404. if (this.state.brush.type == SBrushType.Color) {
  405. this._canvas.fillStyle = `rgba(${this.state.brush.color.red}, ${
  406. this.state.brush.color.green
  407. }, ${this.state.brush.color.blue}, ${this.state.brush.color.alpha /
  408. 255.0})`;
  409. } else if (this.state.brush.type == SBrushType.Gradient) {
  410. let gradient = this.state.brush.gradient,
  411. drawGradient: CanvasGradient;
  412. if (gradient instanceof SLinearGradient) {
  413. drawGradient = this._canvas.createLinearGradient(
  414. gradient.x1,
  415. gradient.y1,
  416. gradient.x2,
  417. gradient.y2
  418. );
  419. } else if (gradient instanceof SRadialGradient) {
  420. drawGradient = this._canvas.createRadialGradient(
  421. gradient.x1,
  422. gradient.y1,
  423. gradient.r1,
  424. gradient.x2,
  425. gradient.y2,
  426. gradient.r2
  427. );
  428. }
  429. // @ts-ignore
  430. if (gradient && drawGradient) {
  431. gradient.stopList.forEach((t: SGradientStop): void => {
  432. drawGradient.addColorStop(
  433. t.pos,
  434. `rgba(${t.color.red},${t.color.green},${t.color.blue},${t.color.alpha})`
  435. );
  436. });
  437. this._canvas.fillStyle = drawGradient;
  438. }
  439. }
  440. } // Function setBrush()
  441. /**
  442. * 设置融合
  443. */
  444. private setComposite(): void {
  445. if (
  446. this.state._composite >= 0 &&
  447. this.state._composite < SCanvasPaintEngine.gcoList.length
  448. ) {
  449. this._canvas.globalCompositeOperation =
  450. SCanvasPaintEngine.gcoList[this.state._composite];
  451. }
  452. } // Function setComposite()
  453. /**
  454. * 设置字体
  455. */
  456. private setFont(): void {
  457. this._canvas.font = `${this.state.font.size}px ${this.state.font.name}`;
  458. this.setTextAlign(this.state.font.textAlign);
  459. this.setBaseLine(this.state.font.textBaseLine);
  460. this.setTextDirection(this.state.font.textDirection);
  461. } // Function setFont()
  462. /**
  463. * 文本对齐选项
  464. *
  465. * @param value 对齐方式
  466. */
  467. private setTextAlign(value: STextAlign): void {
  468. if (value == undefined) {
  469. return;
  470. }
  471. if (value === STextAlign.Start) {
  472. this._canvas.textAlign = "start";
  473. } else if (value === STextAlign.End) {
  474. this._canvas.textAlign = "end";
  475. } else if (value === STextAlign.Left) {
  476. this._canvas.textAlign = "left";
  477. } else if (value === STextAlign.Center) {
  478. this._canvas.textAlign = "center";
  479. } else {
  480. this._canvas.textAlign = "right";
  481. }
  482. } // Function setTextAlign()
  483. /**
  484. * 设置文本基线对齐选项
  485. *
  486. * @param value 对齐方式
  487. */
  488. private setBaseLine(value: STextBaseLine): void {
  489. if (value == undefined) {
  490. return;
  491. }
  492. if (value == STextBaseLine.Alphabetic) {
  493. this._canvas.textBaseline = "alphabetic";
  494. } else if (value == STextBaseLine.Top) {
  495. this._canvas.textBaseline = "top";
  496. } else if (value == STextBaseLine.Hanging) {
  497. this._canvas.textBaseline = "hanging";
  498. } else if (value == STextBaseLine.Middle) {
  499. this._canvas.textBaseline = "middle";
  500. } else if (value == STextBaseLine.Ideographic) {
  501. this._canvas.textBaseline = "ideographic";
  502. } else {
  503. this._canvas.textBaseline = "bottom";
  504. }
  505. } // Set textBaseLine()
  506. /**
  507. * 设置文本方向选项
  508. *
  509. * @param value 文本方向
  510. */
  511. private setTextDirection(value: STextDirection): void {
  512. if (value == undefined) {
  513. return;
  514. }
  515. if (value == STextDirection.Inherit) {
  516. this._canvas.direction = "inherit";
  517. } else if (value == STextDirection.LTR) {
  518. this._canvas.direction = "ltr";
  519. } else {
  520. this._canvas.direction = "rtl";
  521. }
  522. } // Set textDirection
  523. /**
  524. * 设置变型矩阵
  525. */
  526. private setMatrix(): void {
  527. this._canvas.setTransform(
  528. this.state.matrix.a,
  529. this.state.matrix.b,
  530. this.state.matrix.c,
  531. this.state.matrix.d,
  532. this.state.matrix.e,
  533. this.state.matrix.f
  534. );
  535. } // Function setMatrix()
  536. } // class SPaintEngine