vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature
1,328 lines (909 loc) • 60.3 kB
HTML
<!DOCTYPE html>
<html>
<head>
<title>svgcontext.js</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" media="all" href="public/stylesheets/normalize.css" />
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div class="container">
<div class="page">
<div class="header">
<h1>svgcontext.js</h1>
<div class="toc">
<h3>Table of Contents</h3>
<ol>
<li>
<a class="source" href="accidental.html">
accidental.js
</a>
</li>
<li>
<a class="source" href="annotation.html">
annotation.js
</a>
</li>
<li>
<a class="source" href="articulation.html">
articulation.js
</a>
</li>
<li>
<a class="source" href="barnote.html">
barnote.js
</a>
</li>
<li>
<a class="source" href="beam.html">
beam.js
</a>
</li>
<li>
<a class="source" href="bend.html">
bend.js
</a>
</li>
<li>
<a class="source" href="boundingbox.html">
boundingbox.js
</a>
</li>
<li>
<a class="source" href="boundingboxcomputation.html">
boundingboxcomputation.js
</a>
</li>
<li>
<a class="source" href="canvascontext.html">
canvascontext.js
</a>
</li>
<li>
<a class="source" href="clef.html">
clef.js
</a>
</li>
<li>
<a class="source" href="clefnote.html">
clefnote.js
</a>
</li>
<li>
<a class="source" href="crescendo.html">
crescendo.js
</a>
</li>
<li>
<a class="source" href="curve.html">
curve.js
</a>
</li>
<li>
<a class="source" href="dot.html">
dot.js
</a>
</li>
<li>
<a class="source" href="easyscore.html">
easyscore.js
</a>
</li>
<li>
<a class="source" href="element.html">
element.js
</a>
</li>
<li>
<a class="source" href="factory.html">
factory.js
</a>
</li>
<li>
<a class="source" href="formatter.html">
formatter.js
</a>
</li>
<li>
<a class="source" href="fraction.html">
fraction.js
</a>
</li>
<li>
<a class="source" href="frethandfinger.html">
frethandfinger.js
</a>
</li>
<li>
<a class="source" href="ghostnote.html">
ghostnote.js
</a>
</li>
<li>
<a class="source" href="glyph.html">
glyph.js
</a>
</li>
<li>
<a class="source" href="glyphnote.html">
glyphnote.js
</a>
</li>
<li>
<a class="source" href="gracenote.html">
gracenote.js
</a>
</li>
<li>
<a class="source" href="gracenotegroup.html">
gracenotegroup.js
</a>
</li>
<li>
<a class="source" href="gracetabnote.html">
gracetabnote.js
</a>
</li>
<li>
<a class="source" href="index.html">
index.js
</a>
</li>
<li>
<a class="source" href="keymanager.html">
keymanager.js
</a>
</li>
<li>
<a class="source" href="keysignature.html">
keysignature.js
</a>
</li>
<li>
<a class="source" href="keysignote.html">
keysignote.js
</a>
</li>
<li>
<a class="source" href="modifier.html">
modifier.js
</a>
</li>
<li>
<a class="source" href="modifiercontext.html">
modifiercontext.js
</a>
</li>
<li>
<a class="source" href="multimeasurerest.html">
multimeasurerest.js
</a>
</li>
<li>
<a class="source" href="music.html">
music.js
</a>
</li>
<li>
<a class="source" href="note.html">
note.js
</a>
</li>
<li>
<a class="source" href="notehead.html">
notehead.js
</a>
</li>
<li>
<a class="source" href="notesubgroup.html">
notesubgroup.js
</a>
</li>
<li>
<a class="source" href="ornament.html">
ornament.js
</a>
</li>
<li>
<a class="source" href="parser.html">
parser.js
</a>
</li>
<li>
<a class="source" href="pedalmarking.html">
pedalmarking.js
</a>
</li>
<li>
<a class="source" href="raphaelcontext.html">
raphaelcontext.js
</a>
</li>
<li>
<a class="source" href="registry.html">
registry.js
</a>
</li>
<li>
<a class="source" href="renderer.html">
renderer.js
</a>
</li>
<li>
<a class="source" href="repeatnote.html">
repeatnote.js
</a>
</li>
<li>
<a class="source" href="smufl.html">
smufl.js
</a>
</li>
<li>
<a class="source" href="stave.html">
stave.js
</a>
</li>
<li>
<a class="source" href="stavebarline.html">
stavebarline.js
</a>
</li>
<li>
<a class="source" href="staveconnector.html">
staveconnector.js
</a>
</li>
<li>
<a class="source" href="stavehairpin.html">
stavehairpin.js
</a>
</li>
<li>
<a class="source" href="staveline.html">
staveline.js
</a>
</li>
<li>
<a class="source" href="stavemodifier.html">
stavemodifier.js
</a>
</li>
<li>
<a class="source" href="stavenote.html">
stavenote.js
</a>
</li>
<li>
<a class="source" href="staverepetition.html">
staverepetition.js
</a>
</li>
<li>
<a class="source" href="stavesection.html">
stavesection.js
</a>
</li>
<li>
<a class="source" href="stavetempo.html">
stavetempo.js
</a>
</li>
<li>
<a class="source" href="stavetext.html">
stavetext.js
</a>
</li>
<li>
<a class="source" href="stavetie.html">
stavetie.js
</a>
</li>
<li>
<a class="source" href="stavevolta.html">
stavevolta.js
</a>
</li>
<li>
<a class="source" href="stem.html">
stem.js
</a>
</li>
<li>
<a class="source" href="stemmablenote.html">
stemmablenote.js
</a>
</li>
<li>
<a class="source" href="stringnumber.html">
stringnumber.js
</a>
</li>
<li>
<a class="source" href="strokes.html">
strokes.js
</a>
</li>
<li>
<a class="source" href="svgcontext.html">
svgcontext.js
</a>
</li>
<li>
<a class="source" href="system.html">
system.js
</a>
</li>
<li>
<a class="source" href="tables.html">
tables.js
</a>
</li>
<li>
<a class="source" href="tabnote.html">
tabnote.js
</a>
</li>
<li>
<a class="source" href="tabslide.html">
tabslide.js
</a>
</li>
<li>
<a class="source" href="tabstave.html">
tabstave.js
</a>
</li>
<li>
<a class="source" href="tabtie.html">
tabtie.js
</a>
</li>
<li>
<a class="source" href="textbracket.html">
textbracket.js
</a>
</li>
<li>
<a class="source" href="textdynamics.html">
textdynamics.js
</a>
</li>
<li>
<a class="source" href="textnote.html">
textnote.js
</a>
</li>
<li>
<a class="source" href="tickable.html">
tickable.js
</a>
</li>
<li>
<a class="source" href="tickcontext.html">
tickcontext.js
</a>
</li>
<li>
<a class="source" href="timesignature.html">
timesignature.js
</a>
</li>
<li>
<a class="source" href="timesignote.html">
timesignote.js
</a>
</li>
<li>
<a class="source" href="tremolo.html">
tremolo.js
</a>
</li>
<li>
<a class="source" href="tuning.html">
tuning.js
</a>
</li>
<li>
<a class="source" href="tuplet.html">
tuplet.js
</a>
</li>
<li>
<a class="source" href="vex.html">
vex.js
</a>
</li>
<li>
<a class="source" href="vibrato.html">
vibrato.js
</a>
</li>
<li>
<a class="source" href="vibratobracket.html">
vibratobracket.js
</a>
</li>
<li>
<a class="source" href="voice.html">
voice.js
</a>
</li>
<li>
<a class="source" href="voicegroup.html">
voicegroup.js
</a>
</li>
</ol>
</div>
</div>
<p><a href="http://vexflow.com">VexFlow</a> - Copyright (c) Mohit Muthanna 2010.
@author Gregory Ristow (2015)</p>
<div class='highlight'><pre>
<span class="hljs-keyword">import</span> { Vex } <span class="hljs-keyword">from</span> <span class="hljs-string">'./vex'</span>;
<span class="hljs-keyword">const</span> attrNamesToIgnoreMap = {
<span class="hljs-attr">path</span>: {
<span class="hljs-attr">x</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">y</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">width</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">height</span>: <span class="hljs-literal">true</span>,
},
<span class="hljs-attr">rect</span>: {
},
<span class="hljs-attr">text</span>: {
<span class="hljs-attr">width</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">height</span>: <span class="hljs-literal">true</span>,
},
};
{
<span class="hljs-keyword">const</span> fontAttrNamesToIgnore = {
<span class="hljs-string">'font-family'</span>: <span class="hljs-literal">true</span>,
<span class="hljs-string">'font-weight'</span>: <span class="hljs-literal">true</span>,
<span class="hljs-string">'font-style'</span>: <span class="hljs-literal">true</span>,
<span class="hljs-string">'font-size'</span>: <span class="hljs-literal">true</span>,
};
Vex.Merge(attrNamesToIgnoreMap.rect, fontAttrNamesToIgnore);
Vex.Merge(attrNamesToIgnoreMap.path, fontAttrNamesToIgnore);
}
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SVGContext</span> </span>{
<span class="hljs-keyword">constructor</span>(element) {</pre></div>
<p>element is the parent DOM object</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.element = element;</pre></div>
<p>Create the SVG in the SVG namespace:</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.svgNS = <span class="hljs-string">'http://www.w3.org/2000/svg'</span>;
<span class="hljs-keyword">const</span> svg = <span class="hljs-keyword">this</span>.create(<span class="hljs-string">'svg'</span>);</pre></div>
<p>Add it to the canvas:</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.element.appendChild(svg);</pre></div>
<p>Point to it:</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.svg = svg;
<span class="hljs-keyword">this</span>.groups = [<span class="hljs-keyword">this</span>.svg]; <span class="hljs-comment">// Create the group stack</span>
<span class="hljs-keyword">this</span>.parent = <span class="hljs-keyword">this</span>.svg;
<span class="hljs-keyword">this</span>.path = <span class="hljs-string">''</span>;
<span class="hljs-keyword">this</span>.pen = { <span class="hljs-attr">x</span>: <span class="hljs-literal">NaN</span>, <span class="hljs-attr">y</span>: <span class="hljs-literal">NaN</span> };
<span class="hljs-keyword">this</span>.lineWidth = <span class="hljs-number">1.0</span>;
<span class="hljs-keyword">this</span>.state = {
<span class="hljs-attr">scale</span>: { <span class="hljs-attr">x</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">1</span> },
<span class="hljs-string">'font-family'</span>: <span class="hljs-string">'Arial'</span>,
<span class="hljs-string">'font-size'</span>: <span class="hljs-string">'8pt'</span>,
<span class="hljs-string">'font-weight'</span>: <span class="hljs-string">'normal'</span>,
};
<span class="hljs-keyword">this</span>.attributes = {
<span class="hljs-string">'stroke-width'</span>: <span class="hljs-number">0.3</span>,
<span class="hljs-string">'fill'</span>: <span class="hljs-string">'black'</span>,
<span class="hljs-string">'stroke'</span>: <span class="hljs-string">'black'</span>,
<span class="hljs-string">'stroke-dasharray'</span>: <span class="hljs-string">'none'</span>,
<span class="hljs-string">'font-family'</span>: <span class="hljs-string">'Arial'</span>,
<span class="hljs-string">'font-size'</span>: <span class="hljs-string">'10pt'</span>,
<span class="hljs-string">'font-weight'</span>: <span class="hljs-string">'normal'</span>,
<span class="hljs-string">'font-style'</span>: <span class="hljs-string">'normal'</span>,
};
<span class="hljs-keyword">this</span>.background_attributes = {
<span class="hljs-string">'stroke-width'</span>: <span class="hljs-number">0</span>,
<span class="hljs-string">'fill'</span>: <span class="hljs-string">'white'</span>,
<span class="hljs-string">'stroke'</span>: <span class="hljs-string">'white'</span>,
<span class="hljs-string">'stroke-dasharray'</span>: <span class="hljs-string">'none'</span>,
<span class="hljs-string">'font-family'</span>: <span class="hljs-string">'Arial'</span>,
<span class="hljs-string">'font-size'</span>: <span class="hljs-string">'10pt'</span>,
<span class="hljs-string">'font-weight'</span>: <span class="hljs-string">'normal'</span>,
<span class="hljs-string">'font-style'</span>: <span class="hljs-string">'normal'</span>,
};
<span class="hljs-keyword">this</span>.shadow_attributes = {
<span class="hljs-attr">width</span>: <span class="hljs-number">0</span>,
<span class="hljs-attr">color</span>: <span class="hljs-string">'black'</span>,
};
<span class="hljs-keyword">this</span>.state_stack = [];</pre></div>
<p>Test for Internet Explorer</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.iePolyfill();
}
create(svgElementType) {
<span class="hljs-keyword">return</span> <span class="hljs-built_in">document</span>.createElementNS(<span class="hljs-keyword">this</span>.svgNS, svgElementType);
}</pre></div>
<p>Allow grouping elements in containers for interactivity.</p>
<div class='highlight'><pre> openGroup(cls, id, attrs) {
<span class="hljs-keyword">const</span> group = <span class="hljs-keyword">this</span>.create(<span class="hljs-string">'g'</span>);
<span class="hljs-keyword">this</span>.groups.push(group);
<span class="hljs-keyword">this</span>.parent.appendChild(group);
<span class="hljs-keyword">this</span>.parent = group;
<span class="hljs-keyword">if</span> (cls) group.setAttribute(<span class="hljs-string">'class'</span>, Vex.Prefix(cls));
<span class="hljs-keyword">if</span> (id) group.setAttribute(<span class="hljs-string">'id'</span>, Vex.Prefix(id));
<span class="hljs-keyword">if</span> (attrs && attrs.pointerBBox) {
group.setAttribute(<span class="hljs-string">'pointer-events'</span>, <span class="hljs-string">'bounding-box'</span>);
}
<span class="hljs-keyword">return</span> group;
}
closeGroup() {
<span class="hljs-keyword">this</span>.groups.pop();
<span class="hljs-keyword">this</span>.parent = <span class="hljs-keyword">this</span>.groups[<span class="hljs-keyword">this</span>.groups.length - <span class="hljs-number">1</span>];
}
add(elem) {
<span class="hljs-keyword">this</span>.parent.appendChild(elem);
}</pre></div>
<p>Tests if the browser is Internet Explorer; if it is,
we do some tricks to improve text layout. See the
note at ieMeasureTextFix() for details.</p>
<div class='highlight'><pre> iePolyfill() {
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> (navigator) !== <span class="hljs-string">'undefined'</span>) {
<span class="hljs-keyword">this</span>.ie = (
<span class="hljs-regexp">/MSIE 9/i</span>.test(navigator.userAgent) ||
<span class="hljs-regexp">/MSIE 10/i</span>.test(navigator.userAgent) ||
<span class="hljs-regexp">/rv:11\.0/i</span>.test(navigator.userAgent) ||
<span class="hljs-regexp">/Trident/i</span>.test(navigator.userAgent)
);
}
}</pre></div>
<h3 id="styling--state-methods">Styling & State Methods:</h3>
<div class='highlight'><pre>
setFont(family, size, weight) {</pre></div>
<p>Unlike canvas, in SVG italic is handled by font-style,
not weight. So: we search the weight argument and
apply bold and italic to weight and style respectively.</p>
<div class='highlight'><pre> <span class="hljs-keyword">let</span> bold = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> italic = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> style = <span class="hljs-string">'normal'</span>;</pre></div>
<p>Weight might also be a number (200, 400, etc…) so we
test its type to be sure we have access to String methods.</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> weight === <span class="hljs-string">'string'</span>) {</pre></div>
<p>look for “italic” in the weight:</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (weight.indexOf(<span class="hljs-string">'italic'</span>) !== <span class="hljs-number">-1</span>) {
weight = weight.replace(<span class="hljs-regexp">/italic/g</span>, <span class="hljs-string">''</span>);
italic = <span class="hljs-literal">true</span>;
}</pre></div>
<p>look for “bold” in weight</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (weight.indexOf(<span class="hljs-string">'bold'</span>) !== <span class="hljs-number">-1</span>) {
weight = weight.replace(<span class="hljs-regexp">/bold/g</span>, <span class="hljs-string">''</span>);
bold = <span class="hljs-literal">true</span>;
}</pre></div>
<p>remove any remaining spaces</p>
<div class='highlight'><pre> weight = weight.replace(<span class="hljs-regexp">/ /g</span>, <span class="hljs-string">''</span>);
}
weight = bold ? <span class="hljs-string">'bold'</span> : weight;
weight = (<span class="hljs-keyword">typeof</span> weight === <span class="hljs-string">'undefined'</span> || weight === <span class="hljs-string">''</span>) ? <span class="hljs-string">'normal'</span> : weight;
style = italic ? <span class="hljs-string">'italic'</span> : style;
<span class="hljs-keyword">const</span> fontAttributes = {
<span class="hljs-string">'font-family'</span>: family,
<span class="hljs-string">'font-size'</span>: size + <span class="hljs-string">'pt'</span>,
<span class="hljs-string">'font-weight'</span>: weight,
<span class="hljs-string">'font-style'</span>: style,
};</pre></div>
<p>Store the font size so that if the browser is Internet
Explorer we can fix its calculations of text width.</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.fontSize = <span class="hljs-built_in">Number</span>(size);
Vex.Merge(<span class="hljs-keyword">this</span>.attributes, fontAttributes);
Vex.Merge(<span class="hljs-keyword">this</span>.state, fontAttributes);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setRawFont(font) {
font = font.trim();</pre></div>
<p>Assumes size first, splits on space – which is presently
how all existing modules are calling this.</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> fontArray = font.split(<span class="hljs-string">' '</span>);
<span class="hljs-keyword">this</span>.attributes[<span class="hljs-string">'font-family'</span>] = fontArray[<span class="hljs-number">1</span>];
<span class="hljs-keyword">this</span>.state[<span class="hljs-string">'font-family'</span>] = fontArray[<span class="hljs-number">1</span>];
<span class="hljs-keyword">this</span>.attributes[<span class="hljs-string">'font-size'</span>] = fontArray[<span class="hljs-number">0</span>];
<span class="hljs-keyword">this</span>.state[<span class="hljs-string">'font-size'</span>] = fontArray[<span class="hljs-number">0</span>];</pre></div>
<p>Saves fontSize for IE polyfill</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.fontSize = <span class="hljs-built_in">Number</span>(fontArray[<span class="hljs-number">0</span>].match(<span class="hljs-regexp">/\d+/</span>));
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setFillStyle(style) {
<span class="hljs-keyword">this</span>.attributes.fill = style;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setBackgroundFillStyle(style) {
<span class="hljs-keyword">this</span>.background_attributes.fill = style;
<span class="hljs-keyword">this</span>.background_attributes.stroke = style;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setStrokeStyle(style) {
<span class="hljs-keyword">this</span>.attributes.stroke = style;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setShadowColor(style) {
<span class="hljs-keyword">this</span>.shadow_attributes.color = style;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setShadowBlur(blur) {
<span class="hljs-keyword">this</span>.shadow_attributes.width = blur;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setLineWidth(width) {
<span class="hljs-keyword">this</span>.attributes[<span class="hljs-string">'stroke-width'</span>] = width;
<span class="hljs-keyword">this</span>.lineWidth = width;
}</pre></div>
<p>@param array {lineDash} as [dashInt, spaceInt, dashInt, spaceInt, etc…]</p>
<div class='highlight'><pre> setLineDash(lineDash) {
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Object</span>.prototype.toString.call(lineDash) === <span class="hljs-string">'[object Array]'</span>) {
lineDash = lineDash.join(<span class="hljs-string">', '</span>);
<span class="hljs-keyword">this</span>.attributes[<span class="hljs-string">'stroke-dasharray'</span>] = lineDash;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'ArgumentError'</span>, <span class="hljs-string">'lineDash must be an array of integers.'</span>);
}
}
setLineCap(lineCap) {
<span class="hljs-keyword">this</span>.attributes[<span class="hljs-string">'stroke-linecap'</span>] = lineCap;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<h3 id="sizing--scaling-methods">Sizing & Scaling Methods:</h3>
<p>TODO (GCR): See note at scale() – seperate our internal
conception of pixel-based width/height from the style.width
and style.height properties eventually to allow users to
apply responsive sizing attributes to the SVG.</p>
<div class='highlight'><pre> resize(width, height) {
<span class="hljs-keyword">this</span>.width = width;
<span class="hljs-keyword">this</span>.height = height;
<span class="hljs-keyword">this</span>.element.style.width = width;
<span class="hljs-keyword">this</span>.svg.style.width = width;
<span class="hljs-keyword">this</span>.svg.style.height = height;
<span class="hljs-keyword">const</span> attributes = {
width,
height,
};
<span class="hljs-keyword">this</span>.applyAttributes(<span class="hljs-keyword">this</span>.svg, attributes);
<span class="hljs-keyword">this</span>.scale(<span class="hljs-keyword">this</span>.state.scale.x, <span class="hljs-keyword">this</span>.state.scale.y);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
scale(x, y) {</pre></div>
<p>uses viewBox to scale
TODO (GCR): we may at some point want to distinguish the
style.width / style.height properties that are applied to
the SVG object from our internal conception of the SVG
width/height. This would allow us to create automatically
scaling SVG’s that filled their containers, for instance.</p>
<p>As this isn’t implemented in Canvas or Raphael contexts,
I’ve left as is for now, but in using the viewBox to
handle internal scaling, am trying to make it possible
for us to eventually move in that direction.</p>
<div class='highlight'><pre>
<span class="hljs-keyword">this</span>.state.scale = { x, y };
<span class="hljs-keyword">const</span> visibleWidth = <span class="hljs-keyword">this</span>.width / x;
<span class="hljs-keyword">const</span> visibleHeight = <span class="hljs-keyword">this</span>.height / y;
<span class="hljs-keyword">this</span>.setViewBox(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, visibleWidth, visibleHeight);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setViewBox(...args) {</pre></div>
<p>Override for “x y w h” style:</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (args.length === <span class="hljs-number">1</span>) {
<span class="hljs-keyword">const</span> [viewBox] = args;
<span class="hljs-keyword">this</span>.svg.setAttribute(<span class="hljs-string">'viewBox'</span>, viewBox);
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">const</span> [xMin, yMin, width, height] = args;
<span class="hljs-keyword">const</span> viewBoxString = xMin + <span class="hljs-string">' '</span> + yMin + <span class="hljs-string">' '</span> + width + <span class="hljs-string">' '</span> + height;
<span class="hljs-keyword">this</span>.svg.setAttribute(<span class="hljs-string">'viewBox'</span>, viewBoxString);
}
}</pre></div>
<h3 id="drawing-helper-methods">Drawing helper methods:</h3>
<div class='highlight'><pre>
applyAttributes(element, attributes) {
<span class="hljs-keyword">const</span> attrNamesToIgnore = attrNamesToIgnoreMap[element.nodeName];
<span class="hljs-built_in">Object</span>
.keys(attributes)
.forEach(<span class="hljs-function"><span class="hljs-params">propertyName</span> =></span> {
<span class="hljs-keyword">if</span> (attrNamesToIgnore && attrNamesToIgnore[propertyName]) {
<span class="hljs-keyword">return</span>;
}
element.setAttributeNS(<span class="hljs-literal">null</span>, propertyName, attributes[propertyName]);
});
<span class="hljs-keyword">return</span> element;
}</pre></div>
<h3 id="shape--path-methods">Shape & Path Methods:</h3>
<div class='highlight'><pre>
clear() {</pre></div>
<p>Clear the SVG by removing all inner children.</p>
<p>(This approach is usually slightly more efficient
than removing the old SVG & adding a new one to
the container element, since it does not cause the
container to resize twice. Also, the resize
triggered by removing the entire SVG can trigger
a touchcancel event when the element resizes away
from a touch point.)</p>
<div class='highlight'><pre>
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">this</span>.svg.lastChild) {
<span class="hljs-keyword">this</span>.svg.removeChild(<span class="hljs-keyword">this</span>.svg.lastChild);
}</pre></div>
<p>Replace the viewbox attribute we just removed:</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.scale(<span class="hljs-keyword">this</span>.state.scale.x, <span class="hljs-keyword">this</span>.state.scale.y);
}</pre></div>
<h2 id="rectangles">Rectangles:</h2>
<div class='highlight'><pre>
rect(x, y, width, height, attributes) {</pre></div>
<p>Avoid invalid negative height attribs by
flipping the rectangle on its head:</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (height < <span class="hljs-number">0</span>) {
y += height;
height *= <span class="hljs-number">-1</span>;
}</pre></div>
<p>Create the rect & style it:</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> rectangle = <span class="hljs-keyword">this</span>.create(<span class="hljs-string">'rect'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> attributes === <span class="hljs-string">'undefined'</span>) {
attributes = {
<span class="hljs-attr">fill</span>: <span class="hljs-string">'none'</span>,
<span class="hljs-string">'stroke-width'</span>: <span class="hljs-keyword">this</span>.lineWidth,
<span class="hljs-attr">stroke</span>: <span class="hljs-string">'black'</span>,
};
}
Vex.Merge(attributes, {
x,
y,
width,
height,
});
<span class="hljs-keyword">this</span>.applyAttributes(rectangle, attributes);
<span class="hljs-keyword">this</span>.add(rectangle);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
fillRect(x, y, width, height) {
<span class="hljs-keyword">if</span> (height < <span class="hljs-number">0</span>) {
y += height;
height *= <span class="hljs-number">-1</span>;
}
<span class="hljs-keyword">this</span>.rect(x, y, width, height, <span class="hljs-keyword">this</span>.attributes);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
clearRect(x, y, width, height) {</pre></div>
<p>TODO(GCR): Improve implementation of this…
Currently it draws a box of the background color, rather
than creating alpha through lower z-levels.</p>
<p>See the implementation of this in SVGKit:
<a href="http://sourceforge.net/projects/svgkit/">http://sourceforge.net/projects/svgkit/</a>
as a starting point.</p>
<p>Adding a large number of transform paths (as we would
have to do) could be a real performance hit. Since
tabNote seems to be the only module that makes use of this
it may be worth creating a seperate tabStave that would
draw lines around locations of tablature fingering.</p>
<div class='highlight'><pre>
<span class="hljs-keyword">this</span>.rect(x, y, width, height, <span class="hljs-keyword">this</span>.background_attributes);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<h2 id="paths">Paths:</h2>
<div class='highlight'><pre>
beginPath() {
<span class="hljs-keyword">this</span>.path = <span class="hljs-string">''</span>;
<span class="hljs-keyword">this</span>.pen.x = <span class="hljs-literal">NaN</span>;
<span class="hljs-keyword">this</span>.pen.y = <span class="hljs-literal">NaN</span>;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
moveTo(x, y) {
<span class="hljs-keyword">this</span>.path += <span class="hljs-string">'M'</span> + x + <span class="hljs-string">' '</span> + y;
<span class="hljs-keyword">this</span>.pen.x = x;
<span class="hljs-keyword">this</span>.pen.y = y;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
lineTo(x, y) {
<span class="hljs-keyword">this</span>.path += <span class="hljs-string">'L'</span> + x + <span class="hljs-string">' '</span> + y;
<span class="hljs-keyword">this</span>.pen.x = x;
<span class="hljs-keyword">this</span>.pen.y = y;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
bezierCurveTo(x1, y1, x2, y2, x, y) {
<span class="hljs-keyword">this</span>.path += <span class="hljs-string">'C'</span> +
x1 + <span class="hljs-string">' '</span> +
y1 + <span class="hljs-string">','</span> +
x2 + <span class="hljs-string">' '</span> +
y2 + <span class="hljs-string">','</span> +
x + <span class="hljs-string">' '</span> +
y;
<span class="hljs-keyword">this</span>.pen.x = x;
<span class="hljs-keyword">this</span>.pen.y = y;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
quadraticCurveTo(x1, y1, x, y) {
<span class="hljs-keyword">this</span>.path += <span class="hljs-string">'Q'</span> +
x1 + <span class="hljs-string">' '</span> +
y1 + <span class="hljs-string">','</span> +
x + <span class="hljs-string">' '</span> +
y;
<span class="hljs-keyword">this</span>.pen.x = x;
<span class="hljs-keyword">this</span>.pen.y = y;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>This is an attempt (hack) to simulate the HTML5 canvas
arc method.</p>
<div class='highlight'><pre> arc(x, y, radius, startAngle, endAngle, antiClockwise) {
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">normalizeAngle</span>(<span class="hljs-params">angle</span>) </span>{
<span class="hljs-keyword">while</span> (angle < <span class="hljs-number">0</span>) {
angle += <span class="hljs-built_in">Math</span>.PI * <span class="hljs-number">2</span>;
}
<span class="hljs-keyword">while</span> (angle > <span class="hljs-built_in">Math</span>.PI * <span class="hljs-number">2</span>) {
angle -= <span class="hljs-built_in">Math</span>.PI * <span class="hljs-number">2</span>;
}
<span class="hljs-keyword">return</span> angle;
}
startAngle = normalizeAngle(startAngle);
endAngle = normalizeAngle(endAngle);
<span class="hljs-keyword">if</span> (startAngle > endAngle) {
<span class="hljs-keyword">const</span> tmp = startAngle;
startAngle = endAngle;
endAngle = tmp;
antiClockwise = !antiClockwise;
}
<span class="hljs-keyword">const</span> delta = endAngle - startAngle;
<span class="hljs-keyword">if</span> (delta > <span class="hljs-built_in">Math</span>.PI) {
<span class="hljs-keyword">this</span>.arcHelper(x, y, radius, startAngle, startAngle + delta / <span class="hljs-number">2</span>, antiClockwise);
<span class="hljs-keyword">this</span>.arcHelper(x, y, radius, startAngle + delta / <span class="hljs-number">2</span>, endAngle, antiClockwise);
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">this</span>.arcHelper(x, y, radius, startAngle, endAngle, antiClockwise);
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
arcHelper(x, y, radius, startAngle, endAngle, antiClockwise) {
<span class="hljs-keyword">const</span> x1 = x + radius * <span class="hljs-built_in">Math</span>.cos(startAngle);
<span class="hljs-keyword">const</span> y1 = y + radius * <span class="hljs-built_in">Math</span>.sin(startAngle);
<span class="hljs-keyword">const</span> x2 = x + radius * <span class="hljs-built_in">Math</span>.cos(endAngle);
<span class="hljs-keyword">const</span> y2 = y + radius * <span class="hljs-built_in">Math</span>.sin(endAngle);
<span class="hljs-keyword">let</span> largeArcFlag = <span class="hljs-number">0</span>;
<span class="hljs-keyword">let</span> sweepFlag = <span class="hljs-number">0</span>;
<span class="hljs-keyword">if</span> (antiClockwise) {
sweepFlag = <span class="hljs-number">1</span>;
<span class="hljs-keyword">if</span> (endAngle - startAngle < <span class="hljs-built_in">Math</span>.PI) {
largeArcFlag = <span class="hljs-number">1</span>;
}
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (endAngle - startAngle > <span class="hljs-built_in">Math</span>.PI) {
largeArcFlag = <span class="hljs-number">1</span>;
}
<span class="hljs-keyword">this</span>.path += <span class="hljs-string">'M'</span> + x1 + <span class="hljs-string">' '</span> + y1 + <span class="hljs-string">' A'</span> +
radius + <span class="hljs-string">' '</span> + radius + <span class="hljs-string">' 0 '</span> + largeArcFlag + <span class="hljs-string">' '</span> + sweepFlag + <span class="hljs-string">' '</span> +
x2 + <span class="hljs-string">' '</span> + y2;
<span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(<span class="hljs-keyword">this</span>.pen.x) && !<span class="hljs-built_in">isNaN</span>(<span class="hljs-keyword">this</span>.pen.y)) {
<span class="hljs-keyword">this</span>.peth += <span class="hljs-string">'M'</span> + <span class="hljs-keyword">this</span>.pen.x + <span class="hljs-string">' '</span> + <span class="hljs-keyword">this</span>.pen.y;
}
}
closePath() {
<span class="hljs-keyword">this</span>.path += <span class="hljs-string">'Z'</span>;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>Adapted from the source for Raphael’s Element.glow</p>
<div class='highlight'><pre> glow() {</pre></div>
<p>Calculate the width & paths of the glow:</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.shadow_attributes.width > <span class="hljs-number">0</span>) {
<span class="hljs-keyword">const</span> sa = <span class="hljs-keyword">this</span>.shadow_attributes;
<span class="hljs-keyword">const</span> num_paths = sa.width / <span class="hljs-number">2</span>;</pre></div>
<p>Stroke at varying widths to create effect of gaussian blur:</p>
<div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">1</span>; i <= num_paths; i++) {
<span class="hljs-keyword">const</span> attributes = {
<span class="hljs-attr">stroke</span>: sa.color,
<span class="hljs-string">'stroke-linejoin'</span>: <span class="hljs-string">'round'</span>,
<span class="hljs-string">'stroke-linecap'</span>: <span class="hljs-string">'round'</span>,
<span class="hljs-string">'stroke-width'</span>: +((sa.width * <span class="hljs-number">0.4</span>) / num_paths * i).toFixed(<span class="hljs-number">3</span>),
<span class="hljs-attr">opacity</span>: +((sa.opacity || <span class="hljs-number">0.3</span>) / num_paths).toFixed(<span class="hljs-number">3</span>),
};
<span class="hljs-keyword">const</span> path = <span class="hljs-keyword">this</span>.create(<span class="hljs-string">'path'</span>);
attributes.d = <span class="hljs-keyword">this</span>.path;
<span class="hljs-keyword">this</span>.applyAttributes(path, attributes);
<span class="hljs-keyword">this</span>.add(path);
}
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
fill(attributes) {</pre></div>
<p>If our current path is set to glow, make it glow</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.glow();
<span class="hljs-keyword">const</span> path = <span class="hljs-keyword">this</span>.create(<span class="hljs-string">'path'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> attributes === <span class="hljs-string">'undefined'</span>) {
attributes = {};
Vex.Merge(attributes, <span class="hljs-keyword">this</span>.attributes);
attributes.stroke = <span class="hljs-string">'none'</span>;
}
attributes.d = <span class="hljs-keyword">this</span>.path;
<span class="hljs-keyword">this</span>.applyAttributes(