mirror of
https://github.com/google/blockly.git
synced 2026-01-10 02:17:09 +01:00
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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user