forked from ringw/vexflow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathannotation.js
177 lines (152 loc) · 5.8 KB
/
annotation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
//
// ## Description
//
// This file implements text annotations as modifiers that can be attached to
// notes.
//
// See `tests/annotation_tests.js` for usage examples.
Vex.Flow.Annotation = (function() {
function Annotation(text) {
if (arguments.length > 0) this.init(text);
}
Annotation.CATEGORY = "annotations";
// To enable logging for this class. Set `Vex.Flow.Annotation.DEBUG` to `true`.
function L() { if (Annotation.DEBUG) Vex.L("Vex.Flow.Annotation", arguments); }
// Text annotations can be positioned and justified relative to the note.
Annotation.Justify = {
LEFT: 1,
CENTER: 2,
RIGHT: 3,
CENTER_STEM: 4
};
Annotation.VerticalJustify = {
TOP: 1,
CENTER: 2,
BOTTOM: 3,
CENTER_STEM: 4
};
// Arrange annotations within a `ModifierContext`
Annotation.format = function(annotations, state) {
if (!annotations || annotations.length === 0) return false;
var text_line = state.text_line;
var max_width = 0;
// Format Annotations
var width;
for (var i = 0; i < annotations.length; ++i) {
var annotation = annotations[i];
annotation.setTextLine(text_line);
width = annotation.getWidth() > max_width ?
annotation.getWidth() : max_width;
text_line++;
}
state.left_shift += width / 2;
state.right_shift += width / 2;
return true;
};
// ## Prototype Methods
//
// Annotations inherit from `Modifier` and is positioned correctly when
// in a `ModifierContext`.
var Modifier = Vex.Flow.Modifier;
Vex.Inherit(Annotation, Modifier, {
// Create a new `Annotation` with the string `text`.
init: function(text) {
Annotation.superclass.init.call(this);
this.note = null;
this.index = null;
this.text_line = 0;
this.text = text;
this.justification = Annotation.Justify.CENTER;
this.vert_justification = Annotation.VerticalJustify.TOP;
this.font = {
family: "Arial",
size: 10,
weight: ""
};
// The default width is calculated from the text.
this.setWidth(Vex.Flow.textWidth(text));
},
// Set the vertical position of the text relative to the stave.
setTextLine: function(line) { this.text_line = line; return this; },
// Set font family, size, and weight. E.g., `Arial`, `10pt`, `Bold`.
setFont: function(family, size, weight) {
this.font = { family: family, size: size, weight: weight };
return this;
},
// Set vertical position of text (above or below stave). `just` must be
// a value in `Annotation.VerticalJustify`.
setVerticalJustification: function(just) {
this.vert_justification = just;
return this;
},
// Get and set horizontal justification. `justification` is a value in
// `Annotation.Justify`.
getJustification: function() { return this.justification; },
setJustification: function(justification) {
this.justification = justification; return this; },
// Render text beside the note.
draw: function() {
if (!this.context) throw new Vex.RERR("NoContext",
"Can't draw text annotation without a context.");
if (!this.note) throw new Vex.RERR("NoNoteForAnnotation",
"Can't draw text annotation without an attached note.");
var start = this.note.getModifierStartXY(Modifier.Position.ABOVE,
this.index);
// We're changing context parameters. Save current state.
this.context.save();
this.context.setFont(this.font.family, this.font.size, this.font.weight);
var text_width = this.context.measureText(this.text).width;
// Estimate text height to be the same as the width of an 'm'.
//
// This is a hack to work around the inability to measure text height
// in HTML5 Canvas (and SVG).
var text_height = this.context.measureText("m").width;
var x, y;
if (this.justification == Annotation.Justify.LEFT) {
x = start.x;
} else if (this.justification == Annotation.Justify.RIGHT) {
x = start.x - text_width;
} else if (this.justification == Annotation.Justify.CENTER) {
x = start.x - text_width / 2;
} else /* CENTER_STEM */ {
x = this.note.getStemX() - text_width / 2;
}
var stem_ext, spacing;
var has_stem = this.note.hasStem();
var stave = this.note.getStave();
// The position of the text varies based on whether or not the note
// has a stem.
if (has_stem) {
stem_ext = this.note.getStem().getExtents();
spacing = stave.getSpacingBetweenLines();
}
if (this.vert_justification == Annotation.VerticalJustify.BOTTOM) {
y = stave.getYForBottomText(this.text_line);
if (has_stem) {
var stem_base = (this.note.getStemDirection() === 1 ? stem_ext.baseY : stem_ext.topY);
y = Math.max(y, stem_base + (spacing * (this.text_line + 2)));
}
} else if (this.vert_justification ==
Annotation.VerticalJustify.CENTER) {
var yt = this.note.getYForTopText(this.text_line) - 1;
var yb = stave.getYForBottomText(this.text_line);
y = yt + ( yb - yt ) / 2 + text_height / 2;
} else if (this.vert_justification ==
Annotation.VerticalJustify.TOP) {
y = Math.min(stave.getYForTopText(this.text_line), this.note.getYs()[0] - 10);
if (has_stem) {
y = Math.min(y, (stem_ext.topY - 5) - (spacing * this.text_line));
}
} else /* CENTER_STEM */{
var extents = this.note.getStemExtents();
y = extents.topY + (extents.baseY - extents.topY) / 2 +
text_height / 2;
}
L("Rendering annotation: ", this.text, x, y);
this.context.fillText(this.text, x, y);
this.context.restore();
}
});
return Annotation;
}());