@scratch/scratch-svg-renderer 11.0.0-UEPR-176

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ const loadSvgString = require('./load-svg-string');
2
+ const serializeSvgToString = require('./serialize-svg-to-string');
3
+
4
+ /**
5
+ * Main quirks-mode SVG rendering code.
6
+ * @deprecated Call into individual methods exported from this library instead.
7
+ */
8
+ class SvgRenderer {
9
+ /**
10
+ * Create a quirks-mode SVG renderer for a particular canvas.
11
+ * @param {HTMLCanvasElement} [canvas] An optional canvas element to draw to. If this is not provided, the renderer
12
+ * will create a new canvas.
13
+ * @constructor
14
+ */
15
+ constructor (canvas) {
16
+ /**
17
+ * The canvas that this SVG renderer will render to.
18
+ * @type {HTMLCanvasElement}
19
+ * @private
20
+ */
21
+ this._canvas = canvas || document.createElement('canvas');
22
+ this._context = this._canvas.getContext('2d');
23
+
24
+ /**
25
+ * A measured SVG "viewbox"
26
+ * @typedef {object} SvgRenderer#SvgMeasurements
27
+ * @property {number} x - The left edge of the SVG viewbox.
28
+ * @property {number} y - The top edge of the SVG viewbox.
29
+ * @property {number} width - The width of the SVG viewbox.
30
+ * @property {number} height - The height of the SVG viewbox.
31
+ */
32
+
33
+ /**
34
+ * The measurement box of the currently loaded SVG.
35
+ * @type {SvgRenderer#SvgMeasurements}
36
+ * @private
37
+ */
38
+ this._measurements = {x: 0, y: 0, width: 0, height: 0};
39
+
40
+ /**
41
+ * The `<img>` element with the contents of the currently loaded SVG.
42
+ * @type {?HTMLImageElement}
43
+ * @private
44
+ */
45
+ this._cachedImage = null;
46
+
47
+ /**
48
+ * True if this renderer's current SVG is loaded and can be rendered to the canvas.
49
+ * @type {boolean}
50
+ */
51
+ this.loaded = false;
52
+ }
53
+
54
+ /**
55
+ * @returns {!HTMLCanvasElement} this renderer's target canvas.
56
+ */
57
+ get canvas () {
58
+ return this._canvas;
59
+ }
60
+
61
+ /**
62
+ * @return {Array<number>} the natural size, in Scratch units, of this SVG.
63
+ */
64
+ get size () {
65
+ return [this._measurements.width, this._measurements.height];
66
+ }
67
+
68
+ /**
69
+ * @return {Array<number>} the offset (upper left corner) of the SVG's view box.
70
+ */
71
+ get viewOffset () {
72
+ return [this._measurements.x, this._measurements.y];
73
+ }
74
+
75
+ /**
76
+ * Load an SVG string and normalize it. All the steps before drawing/measuring.
77
+ * @param {!string} svgString String of SVG data to draw in quirks-mode.
78
+ * @param {?boolean} fromVersion2 True if we should perform conversion from
79
+ * version 2 to version 3 svg.
80
+ */
81
+ loadString (svgString, fromVersion2) {
82
+ // New svg string invalidates the cached image
83
+ this._cachedImage = null;
84
+ const svgTag = loadSvgString(svgString, fromVersion2);
85
+
86
+ this._svgTag = svgTag;
87
+ this._measurements = {
88
+ width: svgTag.viewBox.baseVal.width,
89
+ height: svgTag.viewBox.baseVal.height,
90
+ x: svgTag.viewBox.baseVal.x,
91
+ y: svgTag.viewBox.baseVal.y
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Load an SVG string, normalize it, and prepare it for (synchronous) rendering.
97
+ * @param {!string} svgString String of SVG data to draw in quirks-mode.
98
+ * @param {?boolean} fromVersion2 True if we should perform conversion from version 2 to version 3 svg.
99
+ * @param {Function} [onFinish] - An optional callback to call when the SVG is loaded and can be rendered.
100
+ */
101
+ loadSVG (svgString, fromVersion2, onFinish) {
102
+ this.loadString(svgString, fromVersion2);
103
+ this._createSVGImage(onFinish);
104
+ }
105
+
106
+ /**
107
+ * Creates an <img> element for the currently loaded SVG string, then calls the callback once it's loaded.
108
+ * @param {Function} [onFinish] - An optional callback to call when the <img> has loaded.
109
+ */
110
+ _createSVGImage (onFinish) {
111
+ if (this._cachedImage === null) this._cachedImage = new Image();
112
+ const img = this._cachedImage;
113
+
114
+ img.onload = () => {
115
+ this.loaded = true;
116
+ if (onFinish) onFinish();
117
+ };
118
+ const svgText = this.toString(true /* shouldInjectFonts */);
119
+ img.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`;
120
+ this.loaded = false;
121
+ }
122
+
123
+ /**
124
+ * Serialize the active SVG DOM to a string.
125
+ * @param {?boolean} shouldInjectFonts True if fonts should be included in the SVG as
126
+ * base64 data.
127
+ * @returns {string} String representing current SVG data.
128
+ * @deprecated Use the standalone `serializeSvgToString` export instead.
129
+ */
130
+ toString (shouldInjectFonts) {
131
+ return serializeSvgToString(this._svgTag, shouldInjectFonts);
132
+ }
133
+
134
+ /**
135
+ * Synchronously draw the loaded SVG to this renderer's `canvas`.
136
+ * @param {number} [scale] - Optionally, also scale the image by this factor.
137
+ */
138
+ draw (scale) {
139
+ if (!this.loaded) throw new Error('SVG image has not finished loading');
140
+ this._drawFromImage(scale);
141
+ }
142
+
143
+ /**
144
+ * Draw to the canvas from a loaded image element.
145
+ * @param {number} [scale] - Optionally, also scale the image by this factor.
146
+ **/
147
+ _drawFromImage (scale) {
148
+ if (this._cachedImage === null) return;
149
+
150
+ const ratio = Number.isFinite(scale) ? scale : 1;
151
+ const bbox = this._measurements;
152
+ this._canvas.width = bbox.width * ratio;
153
+ this._canvas.height = bbox.height * ratio;
154
+ // Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored pre-scaling.
155
+ // e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have a width of 1, but
156
+ // the image's width will be rounded down to 0 on some browsers (Firefox) prior to being drawn at that scale.
157
+ if (
158
+ this._canvas.width <= 0 ||
159
+ this._canvas.height <= 0 ||
160
+ this._cachedImage.naturalWidth <= 0 ||
161
+ this._cachedImage.naturalHeight <= 0
162
+ ) return;
163
+ this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);
164
+ this._context.setTransform(ratio, 0, 0, ratio, 0, 0);
165
+ this._context.drawImage(this._cachedImage, 0, 0);
166
+ }
167
+ }
168
+
169
+ module.exports = SvgRenderer;