Skip to content

Instantly share code, notes, and snippets.

@dschnare
Last active January 18, 2020 03:12
Show Gist options
  • Save dschnare/6360657 to your computer and use it in GitHub Desktop.
Save dschnare/6360657 to your computer and use it in GitHub Desktop.
HTML5 Canvas Tricks: Text Caching and Registration Point
// Create our stage canvas.
var stage = document.createElement('canvas');
stage.style.border = '1px solid #000';
document.body.appendChild(stage);
// Create offscreen buffer for our text rendering.
// This way all we have to do is draw our buffer to
// the main canvas rather than drawing text each frame.
var textBuffer = document.createElement('canvas');
// Lets cache a text rendering.
var text = 'google';
var fontSize = 24;
var fontFamily = 'Arial';
var m = measureText(text, fontSize, fontFamily);
// Resize the buffer.
textBuffer.width = m.width;
textBuffer.height = m.height;
// Render to our buffer.
var ctx = textBuffer.getContext('2d');
ctx.font = fontSize + 'px/' + fontSize + 'px ' + fontFamily;
// Set the baseline to middle and offset by half the text height.
ctx.textBaseline = 'middle';
ctx.fillText(text, 0, m.height/2);
// Now draw our buffer to the stage.
stage.getContext('2d').drawImage(textBuffer, 0, 0);
// Create our stage canvas.
var stage = document.createElement('canvas');
stage.style.border = '1px solid #000';
document.body.appendChild(stage);
// Create offscreen buffer for our text rendering.
// This way all we have to do is draw our buffer to
// the main canvas rather than drawing text each frame.
var textBuffer = document.createElement('canvas');
// Lets cache a text rendering.
var text = 'google';
var fontSize = 24;
var fontFamily = 'Arial';
var m = measureText(text, fontSize, fontFamily);
// Resize the buffer.
textBuffer.width = m.width;
textBuffer.height = m.height;
// Render to our buffer.
var ctx = textBuffer.getContext('2d');
ctx.font = fontSize + 'px/' + fontSize + 'px ' + fontFamily;
ctx.fillText(text, 0, m.height);
// Now draw our buffer to the stage.
stage.getContext('2d').drawImage(textBuffer, 0, 0);
function measureText(text, fontSize, fontFamily) {
var w, h, div = measureText.div || document.createElement('div');
div.style.font = fontSize + 'px/' + fontSize + 'px ' + fontFamily;
div.style.padding = '0';
div.style.margin = '0';
div.style.position = 'absolute';
div.style.visibility = 'hidden';
div.innerHTML = text;
if (!measureText.div) document.body.appendChild(div);
w = div.clientWidth;
h = div.clientHeight;
measureText.div = div;
return {width: w, height: h};
}
/* EXMPLE USAGE
// create an instance of our Text object.
var text = Object.create(Text).init('google');
// Set the text registration point to its top right.
// Our origin coordinates are percentile.
text.originX = 1;
text.originY = 0;
// Place the text object at the top right corner of our main canvas.
text.x = c.width;
// Simulate an update and draw phase of a game loop.
text.update();
text.draw(ctx);
*/
// The Text object type.
var Text = {
// static
measureText: function (text, fontSize, fontFamily) {
var w, h, div = Text.measureText.div || document.createElement('div');
div.style.font = fontSize + 'px/' + fontSize + 'px ' + fontFamily;
div.style.padding = '0';
div.style.margin = '0';
div.style.position = 'absolute';
div.style.visibility = 'hidden';
div.innerHTML = text;
if (!Text.measureText.div) document.body.appendChild(div);
w = div.clientWidth;
h = div.clientHeight;
Text.measureText.div = div;
return {width: w, height: h};
},
text: '',
fontSize: 24,
fontFamily: 'Arial',
prevState: '',
x: 0,
y: 0,
originX: 0.0,
originY: 0.0,
dirty: false,
c: null,
ctx: null,
init: function (text) {
if (text) this.text = text + '';
this.c = document.createElement('canvas');
this.ctx = this.c.getContext('2d');
return this;
},
state: function () {
return [this.fontSize, this.fontFamily, this.text].join(',');
},
update: function () {
var s = this.state();
this.dirty = s !== this.prevState;
this.prevState = s;
// Measure phase
if (this.dirty) this.measure();
},
measure: function () {
var m = Text.measureText(this.text, this.fontSize, this.fontFamily);
this.c.width = m.width;
this.c.height = m.height;
},
draw: function (ctx) {
this.render();
ctx.save();
// Offset the result by our registration point.
ctx.translate(-this.originX*this.c.width, -this.originY*this.c.height);
// Draw and translate.
ctx.drawImage(this.c, this.x, this.y);
ctx.restore();
},
render: function () {
if (!this.dirty) return;
this.ctx.font = this.fontSize + 'px/' + this.fontSize + 'px ' + this.fontFamily;
this.ctx.textBaseline = 'middle';
this.ctx.fillText(this.text, 0, this.c.height/2);
this.dirty = false;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment