Cursor.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import {MusicPartManagerIterator} from "../MusicalScore/MusicParts/MusicPartManagerIterator";
  2. import {MusicPartManager} from "../MusicalScore/MusicParts/MusicPartManager";
  3. import {VoiceEntry} from "../MusicalScore/VoiceData/VoiceEntry";
  4. import {VexFlowStaffEntry} from "../MusicalScore/Graphical/VexFlow/VexFlowStaffEntry";
  5. import {MusicSystem} from "../MusicalScore/Graphical/MusicSystem";
  6. import {OSMD} from "./OSMD";
  7. import {GraphicalMusicSheet} from "../MusicalScore/Graphical/GraphicalMusicSheet";
  8. /**
  9. * A cursor which can iterate through the music sheet.
  10. */
  11. export class Cursor {
  12. constructor(container: HTMLElement, osmd: OSMD) {
  13. this.container = container;
  14. this.osmd = osmd;
  15. let curs: HTMLElement = document.createElement("img");
  16. curs.style.position = "absolute";
  17. curs.style.zIndex = "-1";
  18. this.cursorElement = <HTMLImageElement>curs;
  19. this.container.appendChild(curs);
  20. }
  21. private container: HTMLElement;
  22. private osmd: OSMD;
  23. private manager: MusicPartManager;
  24. private iterator: MusicPartManagerIterator;
  25. private graphic: GraphicalMusicSheet;
  26. private hidden: boolean = true;
  27. private cursorElement: HTMLImageElement;
  28. public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {
  29. this.manager = manager;
  30. this.reset();
  31. this.graphic = graphic;
  32. this.hidden = true;
  33. this.hide();
  34. }
  35. /**
  36. * Make the cursor visible
  37. */
  38. public show(): void {
  39. this.hidden = false;
  40. this.update();
  41. // Forcing the sheet to re-render is not necessary anymore,
  42. // since the cursor is an HTML element.
  43. // this.osmd.render();
  44. }
  45. public update(): void {
  46. // Warning! This should NEVER call this.osmd.render()
  47. if (this.hidden) {
  48. return;
  49. }
  50. this.graphic.Cursors.length = 0;
  51. let iterator: MusicPartManagerIterator = this.iterator;
  52. if (iterator.EndReached || iterator.CurrentVoiceEntries === undefined || iterator.CurrentVoiceEntries.length === 0) {
  53. return;
  54. }
  55. let x: number = 0, y: number = 0, height: number = 0;
  56. let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[0];
  57. let measureIndex: number = voiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure.measureListIndex;
  58. let staffIndex: number = voiceEntry.ParentSourceStaffEntry.ParentStaff.idInMusicSheet;
  59. let gse: VexFlowStaffEntry =
  60. <VexFlowStaffEntry>this.graphic.findGraphicalStaffEntryFromMeasureList(staffIndex, measureIndex, voiceEntry.ParentSourceStaffEntry);
  61. x = gse.PositionAndShape.AbsolutePosition.x;
  62. let musicSystem: MusicSystem = gse.parentMeasure.parentMusicSystem;
  63. y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
  64. let endY: number = musicSystem.PositionAndShape.AbsolutePosition.y +
  65. musicSystem.StaffLines[musicSystem.StaffLines.length - 1].PositionAndShape.RelativePosition.y + 4.0;
  66. height = endY - y;
  67. // The following code is not necessary (for now, but it could come useful later):
  68. // it highlights the notes under the cursor.
  69. //let vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = gse.vfNotes;
  70. //for (let voiceId in vfNotes) {
  71. // if (vfNotes.hasOwnProperty(voiceId)) {
  72. // vfNotes[voiceId].setStyle({
  73. // fillStyle: "red",
  74. // strokeStyle: "red",
  75. // });
  76. // }
  77. //}
  78. // Update the graphical cursor
  79. // The following is the legacy cursor rendered on the canvas:
  80. // // let cursor: GraphicalLine = new GraphicalLine(new PointF2D(x, y), new PointF2D(x, y + height), 3, OutlineAndFillStyleEnum.PlaybackCursor);
  81. // This the current HTML Cursor:
  82. let cursorElement: HTMLImageElement = this.cursorElement;
  83. cursorElement.style.top = (y * 10.0 * this.osmd.zoom) + "px";
  84. cursorElement.style.left = ((x - 1.5) * 10.0 * this.osmd.zoom) + "px";
  85. cursorElement.height = (height * 10.0 * this.osmd.zoom);
  86. let newWidth: number = 3 * 10.0 * this.osmd.zoom;
  87. if (newWidth !== cursorElement.width) {
  88. cursorElement.width = newWidth;
  89. this.updateStyle(newWidth);
  90. }
  91. // Show cursor
  92. // // Old cursor: this.graphic.Cursors.push(cursor);
  93. this.cursorElement.style.display = "";
  94. }
  95. /**
  96. * Hide the cursor
  97. */
  98. public hide(): void {
  99. // Hide the actual cursor element
  100. this.cursorElement.style.display = "none";
  101. //this.graphic.Cursors.length = 0;
  102. // Forcing the sheet to re-render is not necessary anymore
  103. //if (!this.hidden) {
  104. // this.osmd.render();
  105. //}
  106. this.hidden = true;
  107. }
  108. /**
  109. * Go to next entry
  110. */
  111. public next(): void {
  112. this.iterator.moveToNext();
  113. if (!this.hidden) {
  114. this.show();
  115. }
  116. }
  117. /**
  118. * reset cursor to start
  119. */
  120. public reset(): void {
  121. this.iterator = this.manager.getIterator();
  122. this.iterator.moveToNext();
  123. this.update();
  124. }
  125. private updateStyle(width: number, color: string = "#33e02f"): void {
  126. // Create a dummy canvas to generate the gradient for the cursor
  127. // FIXME This approach needs to be improved
  128. let c: HTMLCanvasElement = document.createElement("canvas");
  129. c.width = this.cursorElement.width;
  130. c.height = 1;
  131. let ctx: CanvasRenderingContext2D = c.getContext("2d");
  132. ctx.globalAlpha = 0.5;
  133. // Generate the gradient
  134. let gradient: CanvasGradient = ctx.createLinearGradient(0, 0, this.cursorElement.width, 0);
  135. gradient.addColorStop(0, "white"); // it was: "transparent"
  136. gradient.addColorStop(0.2, color);
  137. gradient.addColorStop(0.8, color);
  138. gradient.addColorStop(1, "white"); // it was: "transparent"
  139. ctx.fillStyle = gradient;
  140. ctx.fillRect(0, 0, width, 1);
  141. // Set the actual image
  142. this.cursorElement.src = c.toDataURL("image/png");
  143. }
  144. }