-
Notifications
You must be signed in to change notification settings - Fork 12
/
TextSVGDrawable.js
110 lines (91 loc) · 4.33 KB
/
TextSVGDrawable.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
// Copyright 2016-2019, University of Colorado Boulder
/**
* SVG drawable for Text nodes.
*
* @author Jonathan Olson <jonathan.olson@colorado.edu>
*/
define( require => {
'use strict';
const inherit = require( 'PHET_CORE/inherit' );
const platform = require( 'PHET_CORE/platform' );
const Poolable = require( 'PHET_CORE/Poolable' );
const scenery = require( 'SCENERY/scenery' );
const SVGSelfDrawable = require( 'SCENERY/display/SVGSelfDrawable' );
const TextStatefulDrawable = require( 'SCENERY/display/drawables/TextStatefulDrawable' );
// TODO: change this based on memory and performance characteristics of the platform
const keepSVGTextElements = true; // whether we should pool SVG elements for the SVG rendering states, or whether we should free them when possible for memory
// Some browsers (IE/Edge) can't handle our UTF-8 embedding marks AND SVG textLength/spacingAndGlyphs. We disable
// using these features, because they aren't necessary on these browsers.
// See https://github.com/phetsims/scenery/issues/455 for more information.
const useSVGTextLengthAdjustments = !platform.ie && !platform.edge;
/**
* A generated SVGSelfDrawable whose purpose will be drawing our Text. One of these drawables will be created
* for each displayed instance of a Text node.
* @constructor
*
* @param {number} renderer - Renderer bitmask, see Renderer's documentation for more details.
* @param {Instance} instance
*/
function TextSVGDrawable( renderer, instance ) {
// Super-type initialization
this.initializeSVGSelfDrawable( renderer, instance, true, keepSVGTextElements ); // usesPaint: true
// @private {boolean}
this.hasLength = false;
if ( !this.svgElement ) {
// @protected {SVGTextElement} - Sole SVG element for this drawable, implementing API for SVGSelfDrawable
const text = document.createElementNS( scenery.svgns, 'text' );
this.svgElement = text;
text.appendChild( document.createTextNode( '' ) );
// TODO: flag adjustment for SVG qualities
text.setAttribute( 'dominant-baseline', 'alphabetic' ); // to match Canvas right now
text.setAttribute( 'text-rendering', 'geometricPrecision' );
text.setAttributeNS( 'http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve' );
text.setAttribute( 'direction', 'ltr' );
}
}
scenery.register( 'TextSVGDrawable', TextSVGDrawable );
inherit( SVGSelfDrawable, TextSVGDrawable, {
/**
* Updates the SVG elements so that they will appear like the current node's representation.
* @protected
*
* Implements the interface for SVGSelfDrawable (and is called from the SVGSelfDrawable's update).
*/
updateSVGSelf: function() {
const text = this.svgElement;
// set all of the font attributes, since we can't use the combined one
if ( this.dirtyFont ) {
text.setAttribute( 'font-family', this.node._font.getFamily() );
text.setAttribute( 'font-size', this.node._font.getSize() );
text.setAttribute( 'font-style', this.node._font.getStyle() );
text.setAttribute( 'font-weight', this.node._font.getWeight() );
text.setAttribute( 'font-stretch', this.node._font.getStretch() );
}
// update the text-node's value
if ( this.dirtyText ) {
text.lastChild.nodeValue = this.node.renderedText;
}
// text length correction, tested with scenery/tests/text-quality-test.html to determine how to match Canvas/SVG rendering (and overall length)
if ( this.dirtyBounds && useSVGTextLengthAdjustments ) {
const useLengthAdjustment = this.node._boundsMethod !== 'accurate' && isFinite( this.node.selfBounds.width );
if ( useLengthAdjustment ) {
if ( !this.hasLength ) {
this.hasLength = true;
text.setAttribute( 'lengthAdjust', 'spacingAndGlyphs' );
}
text.setAttribute( 'textLength', this.node.selfBounds.width );
}
else if ( this.hasLength ) {
this.hasLength = false;
text.removeAttribute( 'lengthAdjust' );
text.removeAttribute( 'textLength' );
}
}
// Apply any fill/stroke changes to our element.
this.updateFillStrokeStyle( text );
}
} );
TextStatefulDrawable.mixInto( TextSVGDrawable );
Poolable.mixInto( TextSVGDrawable );
return TextSVGDrawable;
} );