UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature

1,328 lines (909 loc) 60.3 kB
<!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 &amp;&amp; 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 &amp; 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 &amp; 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> =&gt;</span> { <span class="hljs-keyword">if</span> (attrNamesToIgnore &amp;&amp; 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 &amp; 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 &amp; 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 &lt; <span class="hljs-number">0</span>) { y += height; height *= <span class="hljs-number">-1</span>; }</pre></div> <p>Create the rect &amp; 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 &lt; <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 &lt; <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 &gt; <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 &gt; 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 &gt; <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 &lt; <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 &gt; <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) &amp;&amp; !<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 &amp; paths of the glow:</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.shadow_attributes.width &gt; <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 &lt;= 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(