CanvasVexFlowBackend.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import Vex = require("vexflow");
  2. import {VexFlowBackend} from "./VexFlowBackend";
  3. import {FontStyles} from "../../../Common/Enums/FontStyles";
  4. import {Fonts} from "../../../Common/Enums/Fonts";
  5. import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
  6. import {PointF2D} from "../../../Common/DataObjects/PointF2D";
  7. import {VexFlowConverter} from "./VexFlowConverter";
  8. import {BackendType} from "../../../OpenSheetMusicDisplay";
  9. import {EngravingRules} from "../EngravingRules";
  10. import {GraphicalMusicPage} from "../GraphicalMusicPage";
  11. export class CanvasVexFlowBackend extends VexFlowBackend {
  12. public getVexflowBackendType(): Vex.Flow.Renderer.Backends {
  13. return Vex.Flow.Renderer.Backends.CANVAS;
  14. }
  15. public getOSMDBackendType(): BackendType {
  16. return BackendType.Canvas;
  17. }
  18. public initialize(container: HTMLElement): void {
  19. this.canvas = document.createElement("canvas");
  20. if (!this.graphicalMusicPage) {
  21. this.graphicalMusicPage = new GraphicalMusicPage(undefined);
  22. this.graphicalMusicPage.PageNumber = 1;
  23. }
  24. this.canvas.id = "osmdCanvasVexFlowBackendCanvas" + this.graphicalMusicPage.PageNumber; // needed to extract image buffer from js
  25. this.inner = document.createElement("div");
  26. this.inner.style.position = "relative";
  27. this.canvas.style.zIndex = "0";
  28. this.inner.appendChild(this.canvas);
  29. container.appendChild(this.inner);
  30. this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
  31. this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
  32. }
  33. /**
  34. * Initialize a canvas without attaching it to a DOM node. Can be used to draw in background
  35. * @param width Width of the canvas
  36. * @param height Height of the canvas
  37. */
  38. public initializeHeadless(width: number = 300, height: number = 300): void {
  39. if (!this.graphicalMusicPage) {
  40. // not needed here yet, but just for future safety, make sure the page isn't undefined
  41. this.graphicalMusicPage = new GraphicalMusicPage(undefined);
  42. this.graphicalMusicPage.PageNumber = 1;
  43. }
  44. this.canvas = document.createElement("canvas");
  45. (this.canvas as any).width = width;
  46. (this.canvas as any).height = height;
  47. this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
  48. this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
  49. }
  50. public getContext(): Vex.Flow.CanvasContext {
  51. return this.ctx;
  52. }
  53. public clear(): void {
  54. (<any>this.ctx).clearRect(0, 0, (<any>this.canvas).width, (<any>this.canvas).height);
  55. // set background color if not transparent
  56. if (EngravingRules.Rules.PageBackgroundColor !== undefined) {
  57. this.ctx.save();
  58. this.ctx.setFillStyle(EngravingRules.Rules.PageBackgroundColor);
  59. this.ctx.fillRect(0, 0, (this.canvas as any).width, (this.canvas as any).height);
  60. this.ctx.restore();
  61. }
  62. }
  63. public scale(k: number): void {
  64. this.ctx.scale(k, k);
  65. }
  66. public translate(x: number, y: number): void {
  67. this.CanvasRenderingCtx.translate(x, y);
  68. }
  69. public renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
  70. heightInPixel: number, screenPosition: PointF2D, color: string = undefined): void  {
  71. const old: string = this.CanvasRenderingCtx.font;
  72. this.CanvasRenderingCtx.save();
  73. this.CanvasRenderingCtx.font = VexFlowConverter.font(
  74. fontHeight,
  75. fontStyle,
  76. font
  77. );
  78. this.CanvasRenderingCtx.fillStyle = color;
  79. this.CanvasRenderingCtx.strokeStyle = color;
  80. this.CanvasRenderingCtx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
  81. this.CanvasRenderingCtx.restore();
  82. this.CanvasRenderingCtx.font = old;
  83. }
  84. public renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number = 1): void {
  85. const old: string | CanvasGradient | CanvasPattern = this.CanvasRenderingCtx.fillStyle;
  86. this.CanvasRenderingCtx.fillStyle = VexFlowConverter.style(styleId);
  87. this.CanvasRenderingCtx.globalAlpha = alpha;
  88. this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
  89. this.CanvasRenderingCtx.fillStyle = old;
  90. this.CanvasRenderingCtx.globalAlpha = 1;
  91. }
  92. public renderLine(start: PointF2D, stop: PointF2D, color: string = "#FF0000FF", lineWidth: number= 2): void {
  93. const oldStyle: string | CanvasGradient | CanvasPattern = this.CanvasRenderingCtx.strokeStyle;
  94. this.CanvasRenderingCtx.strokeStyle = color;
  95. this.CanvasRenderingCtx.beginPath();
  96. this.CanvasRenderingCtx.moveTo(start.x, start.y);
  97. this.CanvasRenderingCtx.lineTo(stop.x, stop.y);
  98. this.CanvasRenderingCtx.stroke();
  99. this.CanvasRenderingCtx.strokeStyle = oldStyle;
  100. }
  101. public renderCurve(points: PointF2D[]): void {
  102. this.ctx.beginPath();
  103. this.ctx.moveTo(points[0].x, points[0].y);
  104. this.ctx.bezierCurveTo(
  105. points[1].x,
  106. points[1].y,
  107. points[2].x,
  108. points[2].y,
  109. points[3].x,
  110. points[3].y
  111. );
  112. this.ctx.lineTo(points[7].x, points[7].y);
  113. this.ctx.bezierCurveTo(
  114. points[6].x,
  115. points[6].y,
  116. points[5].x,
  117. points[5].y,
  118. points[4].x,
  119. points[4].y
  120. );
  121. this.ctx.lineTo(points[0].x, points[0].y);
  122. //this.ctx.stroke();
  123. this.ctx.closePath();
  124. this.ctx.fill();
  125. }
  126. private ctx: Vex.Flow.CanvasContext;
  127. public get CanvasRenderingCtx(): CanvasRenderingContext2D {
  128. // This clusterfuck is only there to counter act my favorite vexflow line:
  129. // ctx.vexFlowCanvasContext = ctx;
  130. // No idea why they are saving the context but we wrap the types here
  131. return <CanvasRenderingContext2D>(this.ctx as any).vexFlowCanvasContext;
  132. }
  133. }