@stringsync/vexml
Version:
MusicXML to Vexflow
64 lines (63 loc) • 2.42 kB
JavaScript
import * as util from '../util';
import { Point, Rect } from '../spatial';
import { TextMeasurer } from './textmeasurer';
import { Label } from './label';
export class GapOverlay {
config;
log;
label;
fragmentRender;
style;
ctx = null;
constructor(config, log, label, fragmentRender, style) {
this.config = config;
this.log = log;
this.label = label;
this.fragmentRender = fragmentRender;
this.style = style;
util.assert(fragmentRender.rectSrc !== 'none'); // This means we can trust the rects.
}
setContext(ctx) {
this.ctx = ctx;
return this;
}
draw() {
const ctx = this.ctx;
util.assertNotNull(ctx);
const topRect = this.fragmentRender.partRenders.at(0)?.staveRenders.at(0)?.playableRect;
util.assertDefined(topRect);
const bottomRect = this.fragmentRender.partRenders.at(-1)?.staveRenders.at(-1)?.playableRect;
util.assertDefined(bottomRect);
const rect = Rect.merge([topRect, bottomRect]);
ctx.save();
const fontSize = this.style?.fontSize ?? this.config.DEFAULT_GAP_OVERLAY_FONT_SIZE;
const fontFamily = this.style?.fontFamily ?? this.config.DEFAULT_GAP_OVERLAY_FONT_FAMILY;
const fontColor = this.style?.fontColor ?? this.config.DEFAULT_GAP_OVERLAY_FONT_COLOR;
this.drawRect(rect);
// Draw the label in the center of the overlay.
if (this.label) {
const textMeasurer = new TextMeasurer({
size: fontSize,
family: fontFamily,
});
const measurement = textMeasurer.measure(this.label);
const x = rect.center().x - measurement.width / 2;
const y = rect.center().y + measurement.approximateHeight / 2;
const position = new Point(x, y);
Label.singleLine(this.config, this.log, this.label, position, {}, { size: fontSize, family: fontFamily, color: fontColor })
.setContext(ctx)
.draw();
}
ctx.restore();
return this;
}
drawRect(rect) {
const ctx = this.ctx;
util.assertNotNull(ctx);
ctx.save();
const fill = this.style?.fill ?? this.config.DEFAULT_GAP_OVERLAY_FILL_COLOR;
ctx.setFillStyle(fill);
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
ctx.restore();
}
}