Using a HTML canvas for computing the text width (#3000)

* Use a helper HTML canvas element to measure text width for better performance
This commit is contained in:
Sam El-Husseini
2019-10-24 13:02:32 -04:00
committed by GitHub
parent 6170b77027
commit 649dc116ed
5 changed files with 76 additions and 12 deletions

View File

@@ -257,6 +257,18 @@ Blockly.Field.prototype.EDITABLE = true;
*/
Blockly.Field.prototype.SERIALIZABLE = false;
/**
* Point size of text. Should match blocklyText's font-size in CSS.
* @const {string}
*/
Blockly.Field.FONTSIZE = 11;
/**
* Text font family. Should match blocklyText's font-family in CSS.
* @const {string}
*/
Blockly.Field.FONTFAMILY = 'sans-serif';
/**
* Process the configuration map passed to the field.
* @param {!Object} config A map of options used to configure the field. See
@@ -641,8 +653,9 @@ Blockly.Field.prototype.updateWidth = function() {
* @protected
*/
Blockly.Field.prototype.updateSize_ = function() {
var textWidth = Blockly.utils.dom.getTextWidth(
/** @type {!SVGTextElement} */ (this.textElement_));
var textWidth = Blockly.utils.dom.getFastTextWidth(
/** @type {!SVGTextElement} */ (this.textElement_),
Blockly.Field.FONTSIZE, Blockly.Field.FONTFAMILY);
var totalWidth = textWidth;
if (this.borderRect_) {
totalWidth += Blockly.Field.X_PADDING;

View File

@@ -514,8 +514,9 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
this.imageElement_.setAttribute('height', imageJson.height);
this.imageElement_.setAttribute('width', imageJson.width);
var arrowWidth = Blockly.utils.dom.getTextWidth(
/** @type {!SVGTSpanElement} */ (this.arrow_));
var arrowWidth = Blockly.utils.dom.getFastTextWidth(
/** @type {!SVGTSpanElement} */ (this.arrow_),
Blockly.Field.FONTSIZE, Blockly.Field.FONTFAMILY);
var imageHeight = Number(imageJson.height);
var imageWidth = Number(imageJson.width);
@@ -549,7 +550,8 @@ Blockly.FieldDropdown.prototype.renderSelectedText_ = function() {
this.textElement_.setAttribute('x', Blockly.Field.DEFAULT_TEXT_OFFSET);
// Height and width include the border rect.
this.size_.height = Blockly.Field.BORDER_RECT_DEFAULT_HEIGHT;
this.size_.width = Blockly.utils.dom.getTextWidth(this.textElement_) +
this.size_.width = Blockly.utils.dom.getFastTextWidth(this.textElement_,
Blockly.Field.FONTSIZE, Blockly.Field.FONTFAMILY) +
Blockly.Field.X_PADDING;
};

View File

@@ -245,7 +245,7 @@ Blockly.FieldMultilineInput.prototype.widgetCreate_ = function() {
var htmlInput = /** @type {HTMLTextAreaElement} */ (document.createElement('textarea'));
htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput';
htmlInput.setAttribute('spellcheck', this.spellcheck_);
var fontSize = (Blockly.FieldTextInput.FONTSIZE * scale) + 'pt';
var fontSize = (Blockly.Field.FONTSIZE * scale) + 'pt';
div.style.fontSize = fontSize;
htmlInput.style.fontSize = fontSize;
var borderRadius = (Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px';

View File

@@ -107,11 +107,6 @@ Blockly.FieldTextInput.fromJson = function(options) {
*/
Blockly.FieldTextInput.prototype.SERIALIZABLE = true;
/**
* Point size of text. Should match blocklyText's font-size in CSS.
*/
Blockly.FieldTextInput.FONTSIZE = 11;
/**
* Pixel size of input border radius.
* Should match blocklyText's border-radius in CSS.
@@ -291,7 +286,7 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() {
htmlInput.className = 'blocklyHtmlInput';
htmlInput.setAttribute('spellcheck', this.spellcheck_);
var fontSize =
(Blockly.FieldTextInput.FONTSIZE * this.workspace_.scale) + 'pt';
(Blockly.Field.FONTSIZE * this.workspace_.scale) + 'pt';
div.style.fontSize = fontSize;
htmlInput.style.fontSize = fontSize;
var borderRadius =

View File

@@ -76,6 +76,13 @@ Blockly.utils.dom.cacheWidths_ = null;
*/
Blockly.utils.dom.cacheReference_ = 0;
/**
* A HTML canvas context used for computing text width.
* @type {CanvasRenderingContext2D}
* @private
*/
Blockly.utils.dom.canvasContext_ = null;
/**
* Helper method for creating SVG elements.
* @param {string} name Element's tag name.
@@ -271,3 +278,50 @@ Blockly.utils.dom.getTextWidth = function(textElement) {
}
return width;
};
/**
* Gets the width of a text element using a faster method than `getTextWidth`.
* This method requires that we know the text element's font family and size in
* advance. Similar to `getTextWidth`, we cache the width we compute.
* @param {!Element} textElement An SVG 'text' element.
* @param {string} fontSize The font size to use.
* @param {string} fontFamily The font family to use.
* @return {number} Width of element.
*/
Blockly.utils.dom.getFastTextWidth = function(textElement,
fontSize, fontFamily) {
var text = textElement.textContent;
var key = text + '\n' + textElement.className.baseVal;
var width;
// Return the cached width if it exists.
if (Blockly.utils.dom.cacheWidths_) {
width = Blockly.utils.dom.cacheWidths_[key];
if (width) {
return width;
}
}
if (!Blockly.utils.dom.canvasContext_) {
// Inject the canvas element used for computing text widths.
var computeCanvas = document.createElement('canvas');
computeCanvas.className = 'blocklyComputeCanvas';
document.body.appendChild(computeCanvas);
// Initialize the HTML canvas context and set the font.
// The context font must match blocklyText's fontsize and font-family
// set in CSS.
Blockly.utils.dom.canvasContext_ = computeCanvas.getContext('2d');
}
// Set the desired font size and family.
Blockly.utils.dom.canvasContext_.font = fontSize + 'pt ' + fontFamily;
// Measure the text width using the helper canvas context.
width = Blockly.utils.dom.canvasContext_.measureText(text).width;
// Cache the computed width and return.
if (Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_[key] = width;
}
return width;
};