Procházet zdrojové kódy

fix(resize): handling, set autoResize at runtime, add clear, demo null checks (#428)

* fix(resize): can disable resize during runtime, demo: add clear button, remove resize code

osmd: autoResize defaults to true, add clear(), isReadyToRender()
osmd: fix autoResize handling: don't attach listeners multiple times, don't set timeouts it autoResize disabled

index.html: add clear button, improve re-render button size/layout
index.js: null-check buttons for public demo, remove resize code (handled by osmd), add re-render ready check

In the demo, try combinations of clear, re-render and this in the console:
osmd.setOptions({autoResize: false}) (or true)

* refactor: osmd method placement (remove non-setters from set region), remove css from demo index.html

* doc: EngravingRules: add some docstrings

closes #403 (null-checks for github.io demo which has less buttons. better solved here)
closes #387 (autoResize code in osmd and index.js isn't inconsistent and duplicate anymore)
Simon před 6 roky
rodič
revize
cf2111e2bf

+ 4 - 0
demo/demo.css

@@ -12,6 +12,10 @@ select {
   width: 320px;
 }
 
+#selectBounding {
+  width: 80%;
+}
+
 .bignum {
   width: 1em;
   text-align: center;

+ 8 - 2
demo/index.html

@@ -111,8 +111,14 @@
     </div>
     <div class="column">
         <h3 class="ui header">Debug controls:</h3>
-        <div class="ui vertical buttons">
-            <div class="ui button" id="debug-re-render-btn">Re-render</div>
+        <div>
+            <div class="ui vertical buttons">
+                <div class="ui button" id="debug-re-render-btn">Re-render</div>
+                
+            </div>
+            <div class="ui vertical buttons">
+                <div class="ui button" id="debug-clear-btn"">Clear</div>
+            </div>
         </div>
     </div>
 </div>

+ 29 - 65
demo/index.js

@@ -4,8 +4,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
 (function () {
     "use strict";
     var openSheetMusicDisplay;
-    var sampleLoaded = false;
-    // folder of the sample files
     var sampleFolder = process.env.STATIC_FILES_SUBFOLDER ? process.env.STATIC_FILES_SUBFOLDER + "/" : "",
     samples = {
         "Beethoven, L.v. - An die ferne Geliebte": "Beethoven_AnDieFerneGeliebte.xml",
@@ -59,7 +57,8 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     showCursorBtn,
     hideCursorBtn,
     backendSelect,
-    debugReRenderBtn;
+    debugReRenderBtn,
+    debugClearBtn;
 
     // Initialization code
     function init() {
@@ -82,7 +81,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         hideCursorBtn = document.getElementById("hide-cursor-btn");
         backendSelect = document.getElementById("backend-select");
         debugReRenderBtn = document.getElementById("debug-re-render-btn");
-
+        debugClearBtn = document.getElementById("debug-clear-btn");
 
         // Hide error
         error();
@@ -97,7 +96,9 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             selectSample.appendChild(option);
         }
         selectSample.onchange = selectSampleOnChange;
-        selectBounding.onchange = selectBoundingOnChange;
+        if (selectBounding) {
+            selectBounding.onchange = selectBoundingOnChange;
+        }
 
         // Pre-select default music piece
 
@@ -113,16 +114,28 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             scale();
         };
 
-        skylineDebug.onclick = function() {
-            openSheetMusicDisplay.DrawSkyLine = !openSheetMusicDisplay.DrawSkyLine;
+        if (skylineDebug) {
+            skylineDebug.onclick = function() {
+                openSheetMusicDisplay.DrawSkyLine = !openSheetMusicDisplay.DrawSkyLine;
+            }
+        }
+
+        if (bottomlineDebug) {
+            bottomlineDebug.onclick = function() {
+                openSheetMusicDisplay.DrawBottomLine = !openSheetMusicDisplay.DrawBottomLine;
+            }
         }
 
-        bottomlineDebug.onclick = function() {
-            openSheetMusicDisplay.DrawBottomLine = !openSheetMusicDisplay.DrawBottomLine;
+        if (debugReRenderBtn) {
+            debugReRenderBtn.onclick = function() {
+                rerender();
+            }
         }
 
-        debugReRenderBtn.onclick = function() {
-            rerender();
+        if (debugClearBtn) {
+            debugClearBtn.onclick = function() {
+                openSheetMusicDisplay.clear();
+            }
         }
 
         // Create OSMD object and canvas
@@ -146,31 +159,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         openSheetMusicDisplay.setLogLevel('info');
         document.body.appendChild(canvas);
 
-        // Set resize event handler
-        new Resize(
-            function(){
-                if (!sampleLoaded) {
-                    return;
-                }
-
-                disable();
-                },
-            function(){
-                if (!sampleLoaded) {
-                    return;
-                }
-
-                var width = document.body.clientWidth;
-                canvas.width = width;
-                try {
-                    openSheetMusicDisplay.render();
-                } catch (e) {
-                    errorLoadingOrRenderingSheet(e, "rendering");
-                }
-                enable();
-            }
-        );
-
         window.addEventListener("keydown", function(e) {
             var event = window.event ? window.event : e;
             if (event.keyCode === 39) {
@@ -213,33 +201,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         });
     }
 
-    function Resize(startCallback, endCallback) {
-      var rtime;
-      var timeout = false;
-      var delta = 200;
-
-      function resizeEnd() {
-        timeout = window.clearTimeout(timeout);
-        if (new Date() - rtime < delta) {
-          timeout = setTimeout(resizeEnd, delta);
-        } else {
-          endCallback();
-        }
-      }
-
-      window.addEventListener("resize", function () {
-        rtime = new Date();
-        if (!timeout) {
-          startCallback();
-          rtime = new Date();
-          timeout = window.setTimeout(resizeEnd, delta);
-        }
-      });
-
-      window.setTimeout(startCallback, 0);
-      window.setTimeout(endCallback, 1);
-    }
-
     function selectBoundingOnChange(evt) {
         var value = evt.target.value;
         openSheetMusicDisplay.DrawBoundingBox = value;
@@ -283,7 +244,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     }
 
     function onLoadingEnd(isCustom) {
-        sampleLoaded = true;
         // Remove option from select
         if (!isCustom && custom.parentElement === selectSample) {
             selectSample.removeChild(custom);
@@ -308,7 +268,11 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     function rerender() {
         disable();
         window.setTimeout(function(){
-            openSheetMusicDisplay.render();
+            if (openSheetMusicDisplay.IsReadyToRender()) {
+                openSheetMusicDisplay.render();
+            } else {
+                selectSampleOnChange(); // reload sample e.g. after osmd.clear()
+            }
             enable();
         }, 0);
     }

+ 8 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -6,9 +6,11 @@ import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 
 export class EngravingRules {
     private static rules: EngravingRules;
+    /** A unit of distance. 1.0 is the distance between lines of a stave for OSMD, which is 10 pixels in Vexflow. */
     private static unit: number = 1.0;
     private samplingUnit: number;
     private staccatoShorteningFactor: number;
+    /** Height (size) of the sheet title. */
     private sheetTitleHeight: number;
     private sheetSubtitleHeight: number;
     private sheetMinimumDistanceBetweenTitleAndSubtitle: number;
@@ -104,6 +106,10 @@ export class EngravingRules {
     private repetitionEndingLabelYOffset: number;
     private repetitionEndingLineYLowerOffset: number;
     private repetitionEndingLineYUpperOffset: number;
+    /** Default alignment of lyrics.
+     * Left alignments will extend text to the right of the bounding box,
+     * which facilitates spacing by extending measure width.
+     */
     private lyricsAlignmentStandard: TextAlignmentEnum;
     private lyricsHeight: number;
     private lyricsYOffsetToStaffHeight: number;
@@ -162,6 +168,7 @@ export class EngravingRules {
     private noteDistancesScalingFactors: number[] = [1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0];
     private durationDistanceDict: {[_: number]: number; } = {};
     private durationScalingDistanceDict: {[_: number]: number; } = {};
+    /** Whether to render a label for the composer of the piece at the top of the sheet. */
     private renderComposer: boolean;
     private renderTitle: boolean;
     private renderSubtitle: boolean;
@@ -170,6 +177,7 @@ export class EngravingRules {
     private renderFingerings: boolean;
     private dynamicExpressionMaxDistance: number;
     private dynamicExpressionSpacer: number;
+    /** Position of fingering label in relation to corresponding note (left, right supported, above, below experimental) */
     private fingeringPosition: PlacementEnum;
     private fingeringInsideStafflines: boolean;
 

+ 139 - 111
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -47,6 +47,9 @@ export class OpenSheetMusicDisplay {
             throw new Error("Please pass a valid div container to OpenSheetMusicDisplay");
         }
 
+        if (options.autoResize === undefined) {
+            options.autoResize = true;
+        }
         this.setOptions(options);
     }
 
@@ -61,6 +64,8 @@ export class OpenSheetMusicDisplay {
     private drawer: VexFlowMusicSheetDrawer;
     private graphic: GraphicalMusicSheet;
     private drawingParameters: DrawingParameters;
+    private autoResizeEnabled: boolean;
+    private resizeHandlerAttached: boolean;
 
     /**
      * Load a MusicXML file
@@ -168,6 +173,117 @@ export class OpenSheetMusicDisplay {
         }
     }
 
+    /** States whether the render() function can be safely called. */
+    public IsReadyToRender(): boolean {
+        return this.graphic !== undefined;
+    }
+
+    /** Clears what OSMD has drawn on its canvas. */
+    public clear(): void {
+        this.drawer.clear();
+        this.reset(); // without this, resize will draw loaded sheet again
+    }
+
+    /** Set OSMD rendering options using an IOSMDOptions object.
+     *  Can be called during runtime. Also called by constructor.
+     *  For example, setOptions({autoResize: false}) will disable autoResize even during runtime.
+     */
+    public setOptions(options: IOSMDOptions): void {
+        this.drawingParameters = new DrawingParameters();
+        if (options.drawingParameters) {
+            this.drawingParameters.DrawingParametersEnum =
+                (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
+        }
+
+        const updateExistingBackend: boolean = this.backend !== undefined;
+        if (options.backend !== undefined || this.backend === undefined) {
+            if (updateExistingBackend) {
+                // TODO doesn't work yet, still need to create a new OSMD object
+
+                this.drawer.clear();
+
+                // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
+                // musicSheetCalculator.clearRecreatedObjects();
+            }
+            if (options.backend === undefined || options.backend.toLowerCase() === "svg") {
+                this.backend = new SvgVexFlowBackend();
+            } else {
+                this.backend = new CanvasVexFlowBackend();
+            }
+            this.backend.initialize(this.container);
+            this.canvas = this.backend.getCanvas();
+            this.innerElement = this.backend.getInnerElement();
+            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
+            // Create the drawer
+            this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, this.drawingParameters);
+        }
+
+        // individual drawing parameters options
+        if (options.disableCursor) {
+            this.drawingParameters.drawCursors = false;
+            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
+        }
+        // alternative to if block: this.drawingsParameters.drawCursors = options.drawCursors !== false. No if, but always sets drawingParameters.
+        // note that every option can be undefined, which doesn't mean the option should be set to false.
+        if (options.drawHiddenNotes) {
+            this.drawingParameters.drawHiddenNotes = true;
+        }
+        if (options.drawTitle !== undefined) {
+            this.drawingParameters.DrawTitle = options.drawTitle;
+            // TODO these settings are duplicate in drawingParameters and EngravingRules. Maybe we only need them in EngravingRules.
+            // this sets the parameter in DrawingParameters, which in turn sets the parameter in EngravingRules.
+            // see settings below that don't call drawingParameters for the immediate approach
+        }
+        if (options.drawSubtitle !== undefined) {
+            this.drawingParameters.DrawSubtitle = options.drawSubtitle;
+        }
+        if (options.drawLyricist !== undefined) {
+            this.drawingParameters.DrawLyricist = options.drawLyricist;
+        }
+        if (options.drawCredits !== undefined) {
+            this.drawingParameters.drawCredits = options.drawCredits;
+        }
+        if (options.drawPartNames !== undefined) {
+            this.drawingParameters.DrawPartNames = options.drawPartNames;
+        }
+        if (options.drawFingerings === false) {
+            EngravingRules.Rules.RenderFingerings = false;
+        }
+        if (options.fingeringPosition !== undefined) {
+            EngravingRules.Rules.FingeringPosition = AbstractExpression.PlacementEnumFromString(options.fingeringPosition);
+        }
+        if (options.fingeringInsideStafflines !== undefined) {
+            EngravingRules.Rules.FingeringInsideStafflines = options.fingeringInsideStafflines;
+        }
+        if (options.setWantedStemDirectionByXml !== undefined) {
+            EngravingRules.Rules.SetWantedStemDirectionByXml = options.setWantedStemDirectionByXml;
+        }
+        if (options.defaultColorNoteHead) {
+            this.drawingParameters.defaultColorNoteHead = options.defaultColorNoteHead;
+        }
+        if (options.defaultColorStem) {
+            this.drawingParameters.defaultColorStem = options.defaultColorStem;
+        }
+        if (options.tupletsRatioed) {
+            EngravingRules.Rules.TupletsRatioed = true;
+        }
+        if (options.tupletsBracketed) {
+            EngravingRules.Rules.TupletsBracketed = true;
+        }
+        if (options.tripletsBracketed) {
+            EngravingRules.Rules.TripletsBracketed = true;
+        }
+        if (options.autoResize) {
+            if (!this.resizeHandlerAttached) {
+                this.autoResize();
+            }
+            this.autoResizeEnabled = true;
+        } else if (options.autoResize === false) { // not undefined
+            this.autoResizeEnabled = false;
+            // we could remove the window EventListener here, but not necessary.
+        }
+    }
+
     /**
      * Sets the logging level for this OSMD instance. By default, this is set to `warn`.
      *
@@ -233,7 +349,7 @@ export class OpenSheetMusicDisplay {
                 //    document.documentElement.offsetWidth
                 //);
                 //self.container.style.width = width + "px";
-                if (self.graphic !== undefined) {
+                if (self.IsReadyToRender()) {
                     self.render();
                 }
             }
@@ -249,6 +365,19 @@ export class OpenSheetMusicDisplay {
         let rtime: number;
         let timeout: number = undefined;
         const delta: number = 200;
+        const self: OpenSheetMusicDisplay = this;
+
+        function resizeStart(): void {
+            if (!self.AutoResizeEnabled) {
+                return;
+            }
+            rtime = (new Date()).getTime();
+            if (!timeout) {
+                startCallback();
+                rtime = (new Date()).getTime();
+                timeout = window.setTimeout(resizeEnd, delta);
+            }
+        }
 
         function resizeEnd(): void {
             timeout = undefined;
@@ -260,21 +389,13 @@ export class OpenSheetMusicDisplay {
             }
         }
 
-        function resizeStart(): void {
-            rtime = (new Date()).getTime();
-            if (!timeout) {
-                startCallback();
-                rtime = (new Date()).getTime();
-                timeout = window.setTimeout(resizeEnd, delta);
-            }
-        }
-
         if ((<any>window).attachEvent) {
             // Support IE<9
             (<any>window).attachEvent("onresize", resizeStart);
         } else {
             window.addEventListener("resize", resizeStart);
         }
+        this.resizeHandlerAttached = true;
 
         window.setTimeout(startCallback, 0);
         window.setTimeout(endCallback, 1);
@@ -304,111 +425,12 @@ export class OpenSheetMusicDisplay {
     }
 
     //#region GETTER / SETTER
-    public setOptions(options: IOSMDOptions): void {
-        this.drawingParameters = new DrawingParameters();
-        if (options.drawingParameters) {
-            this.drawingParameters.DrawingParametersEnum =
-                (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
-        }
-
-        const updateExistingBackend: boolean = this.backend !== undefined;
-        if (options.backend !== undefined || this.backend === undefined) {
-            if (updateExistingBackend) {
-                // TODO doesn't work yet, still need to create a new OSMD object
-
-                this.drawer.clear();
-
-                // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
-                // musicSheetCalculator.clearRecreatedObjects();
-            }
-            if (options.backend === undefined || options.backend.toLowerCase() === "svg") {
-                this.backend = new SvgVexFlowBackend();
-            } else {
-                this.backend = new CanvasVexFlowBackend();
-            }
-            this.backend.initialize(this.container);
-            this.canvas = this.backend.getCanvas();
-            this.innerElement = this.backend.getInnerElement();
-            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
-            // Create the drawer
-            this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, this.drawingParameters);
-        }
-
-        // individual drawing parameters options
-        if (options.disableCursor) {
-            this.drawingParameters.drawCursors = false;
-            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
-        }
-        // alternative to if block: this.drawingsParameters.drawCursors = options.drawCursors !== false. No if, but always sets drawingParameters.
-        // note that every option can be undefined, which doesn't mean the option should be set to false.
-        if (options.drawHiddenNotes) {
-            this.drawingParameters.drawHiddenNotes = true;
-        }
-        if (options.drawTitle !== undefined) {
-            this.drawingParameters.DrawTitle = options.drawTitle;
-            // TODO these settings are duplicate in drawingParameters and EngravingRules. Maybe we only need them in EngravingRules.
-            // this sets the parameter in DrawingParameters, which in turn sets the parameter in EngravingRules.
-            // see tuplets settings below for the immediate approach
-        }
-        if (options.drawSubtitle !== undefined) {
-            this.drawingParameters.DrawSubtitle = options.drawSubtitle;
-        }
-        if (options.drawLyricist !== undefined) {
-            this.drawingParameters.DrawLyricist = options.drawLyricist;
-        }
-        if (options.drawCredits !== undefined) {
-            this.drawingParameters.drawCredits = options.drawCredits;
-        }
-        if (options.drawPartNames !== undefined) {
-            this.drawingParameters.DrawPartNames = options.drawPartNames;
-        }
-        if (options.drawFingerings === false) {
-            EngravingRules.Rules.RenderFingerings = false;
-        }
-        if (options.fingeringPosition !== undefined) {
-            EngravingRules.Rules.FingeringPosition = AbstractExpression.PlacementEnumFromString(options.fingeringPosition);
-        }
-        if (options.fingeringInsideStafflines !== undefined) {
-            EngravingRules.Rules.FingeringInsideStafflines = options.fingeringInsideStafflines;
-        }
-        if (options.setWantedStemDirectionByXml !== undefined) {
-            EngravingRules.Rules.SetWantedStemDirectionByXml = options.setWantedStemDirectionByXml;
-        }
-        if (options.defaultColorNoteHead) {
-            this.drawingParameters.defaultColorNoteHead = options.defaultColorNoteHead;
-        }
-        if (options.defaultColorStem) {
-            this.drawingParameters.defaultColorStem = options.defaultColorStem;
-        }
-        if (options.tupletsRatioed) {
-            EngravingRules.Rules.TupletsRatioed = true;
-        }
-        if (options.tupletsBracketed) {
-            EngravingRules.Rules.TupletsBracketed = true;
-        }
-        if (options.tripletsBracketed) {
-            EngravingRules.Rules.TripletsBracketed = true;
-        }
-        if (options.autoResize) {
-            this.autoResize();
-        } else if (options.autoResize === false) { // not undefined
-            this.handleResize(
-                () => {
-                    // empty
-                },
-                () => {
-                    // empty
-            });
-        }
-    }
-
     public set DrawSkyLine(value: boolean) {
         if (this.drawer) {
             this.drawer.skyLineVisible = value;
             this.render();
         }
     }
-
     public get DrawSkyLine(): boolean {
         return this.drawer.skyLineVisible;
     }
@@ -419,7 +441,6 @@ export class OpenSheetMusicDisplay {
             this.render();
         }
     }
-
     public get DrawBottomLine(): boolean {
         return this.drawer.bottomLineVisible;
     }
@@ -428,9 +449,16 @@ export class OpenSheetMusicDisplay {
         this.drawer.drawableBoundingBoxElement = value;
         this.render();
     }
-
     public get DrawBoundingBox(): string {
         return this.drawer.drawableBoundingBoxElement;
     }
+
+    public get AutoResizeEnabled(): boolean {
+        return this.autoResizeEnabled;
+    }
+    public set AutoResizeEnabled(value: boolean) {
+        this.autoResizeEnabled = value;
+    }
+
     //#endregion
 }