From 60c009b4329c2f132f6e8d229006877f9c5e76d4 Mon Sep 17 00:00:00 2001 From: Ian Pearce Date: Mon, 6 Oct 2014 03:00:53 +0800 Subject: [PATCH 1/4] Added sizeToFill option for resizing text to fill its container --- CanvasTextWrapper.js | 56 ++++++++++++++++++++++++++++++---------- CanvasTextWrapper.min.js | 2 +- README.md | 4 ++- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/CanvasTextWrapper.js b/CanvasTextWrapper.js index 5520ac3..d199c59 100644 --- a/CanvasTextWrapper.js +++ b/CanvasTextWrapper.js @@ -15,7 +15,8 @@ paddingX: 0, // zero px left & right text padding relative to canvas or parent paddingY: 0, // zero px top & bottom text padding relative to canvas or parent fitParent: false, // text is tested to fit canvas width - lineBreak: 'auto' // text fills the element's (canvas or parent) width going to a new line on a whole word + lineBreak: 'auto', // text fills the element's (canvas or parent) width going to a new line on a whole word + sizeToFill: false // text is resized to fill the container height (given font size is ignored) }; window.CanvasTextWrapper = function(canvas, text, opts) { @@ -49,24 +50,30 @@ CanvasTextWrapper.prototype = { drawText: function() { - var canvas = this.canvas; - var context = canvas.getContext('2d'); - - var elementWidth = (this.fitParent === false) ? canvas.width : canvas.parentNode.clientWidth; - var maxTextLength = elementWidth - (this.paddingX * 2); - - var words = this.text.split(/\s+/); - var lines = []; + var elementWidth = (this.fitParent === false) ? this.canvas.width : this.canvas.parentNode.clientWidth; var textPos = { x: 0, y: 0 }; - this.checkWordsLength(context, words, maxTextLength); - this.breakTextIntoLines(context, lines, words, maxTextLength); + if (this.sizeToFill) { + // starting at 1px increase font size by 1px until text block exceeds the height of its padded container + var elementHeight = ((this.fitParent === false) ? this.canvas.height : this.canvas.parentNode.clientHeight) - (this.paddingX * 2); + var fontSize = 0; + do { + this.setFontSize(++fontSize); + var lines = this.getWrappedText(elementWidth); + var textBlockHeight = lines.length * this.lineHeight; + } while (textBlockHeight < elementHeight); - // height of the broken down into lines text - var textBlockHeight = lines.length * this.lineHeight; + // use previous font size, not the one that broke the while condition + this.setFontSize(--fontSize); + var lines = this.getWrappedText(elementWidth); + var textBlockHeight = lines.length * this.lineHeight; + } else { + var lines = this.getWrappedText(elementWidth); + var textBlockHeight = lines.length * this.lineHeight; + } // set vertical align for the whole text block this.setTextVerticalAlign(textPos, textBlockHeight); @@ -79,6 +86,24 @@ } }, + setFontSize: function(size) { + var fontParts = this.context.font.split(/\b\d+px\b/i); + this.context.font = fontParts[0] + size + 'px' + fontParts[1]; + this.lineHeight = size; + }, + + getWrappedText: function(elementWidth) { + var maxTextLength = elementWidth - (this.paddingX * 2); + + var words = this.text.split(/\s+/); + var lines = []; + + this.checkWordsLength(this.context, words, maxTextLength); + this.breakTextIntoLines(this.context, lines, words, maxTextLength); + + return lines; + }, + checkWordsLength: function(context, words, maxTextLength) { for (var i = 0; i < words.length; i++) { var testString = ''; @@ -167,6 +192,9 @@ if (this.lineBreak !== 'auto' && this.lineBreak !== 'word') { throw new TypeError('From CanvasTextWrapper(): Unsupported line break value is used. Property "lineBreak" can only be set to "auto", or "word".'); } + if (typeof this.sizeToFill !== 'boolean') { + throw new TypeError('From CanvasTextWrapper(): Property "sizeToFill" must be set to a Boolean.'); + } } }; -})(); \ No newline at end of file +})(); diff --git a/CanvasTextWrapper.min.js b/CanvasTextWrapper.min.js index f016061..f937d15 100644 --- a/CanvasTextWrapper.min.js +++ b/CanvasTextWrapper.min.js @@ -4,4 +4,4 @@ * MIT License (http://www.opensource.org/licenses/mit-license.html) * Copyright (c) 2014 Vadim Namniak */ -!function(){"use strict";var a={font:"18px Arial, sans-serif",textAlign:"left",verticalAlign:"top",paddingX:0,paddingY:0,fitParent:!1,lineBreak:"auto"};window.CanvasTextWrapper=function(b,c,d){if(!(this instanceof CanvasTextWrapper))throw new TypeError('CanvasTextWrapper constructor failed. Use "new" keyword when instantiating.');this.canvas=b,this.text=c;for(var e in a)this[e]=d&&d[e]?d[e]:a[e];this.lineHeight=parseInt(this.font.replace(/^\D+/g,""),10),this.validate(),this.context=this.canvas.getContext("2d"),this.context.font=this.font,this.context.textBaseline="bottom",this.drawText()},CanvasTextWrapper.prototype={drawText:function(){var a=this.canvas,b=a.getContext("2d"),c=this.fitParent===!1?a.width:a.parentNode.clientWidth,d=c-2*this.paddingX,e=this.text.split(/\s+/),f=[],g={x:0,y:0};this.checkWordsLength(b,e,d),this.breakTextIntoLines(b,f,e,d);var h=f.length*this.lineHeight;this.setTextVerticalAlign(g,h);for(var i=0;ic){for(var g=0;a.measureText(e+b[d][g]).width<=c&&gf);this.setFontSize(--d);var e=this.getWrappedText(a),f=e.length*this.lineHeight}else var e=this.getWrappedText(a),f=e.length*this.lineHeight;this.setTextVerticalAlign(b,f);for(var g=0;gc){for(var g=0;a.measureText(e+b[d][g]).width<=c&&g Date: Mon, 6 Oct 2014 03:05:27 +0800 Subject: [PATCH 2/4] Slightly DRYer --- CanvasTextWrapper.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CanvasTextWrapper.js b/CanvasTextWrapper.js index d199c59..8a73d3b 100644 --- a/CanvasTextWrapper.js +++ b/CanvasTextWrapper.js @@ -68,13 +68,11 @@ // use previous font size, not the one that broke the while condition this.setFontSize(--fontSize); - var lines = this.getWrappedText(elementWidth); - var textBlockHeight = lines.length * this.lineHeight; - } else { - var lines = this.getWrappedText(elementWidth); - var textBlockHeight = lines.length * this.lineHeight; } + var lines = this.getWrappedText(elementWidth); + var textBlockHeight = lines.length * this.lineHeight; + // set vertical align for the whole text block this.setTextVerticalAlign(textPos, textBlockHeight); From 43c87753598d833665dc7863689e6e653cd2f334 Mon Sep 17 00:00:00 2001 From: Ian Pearce Date: Mon, 6 Oct 2014 03:07:23 +0800 Subject: [PATCH 3/4] Run grunt --- CanvasTextWrapper.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CanvasTextWrapper.min.js b/CanvasTextWrapper.min.js index f937d15..45e4fde 100644 --- a/CanvasTextWrapper.min.js +++ b/CanvasTextWrapper.min.js @@ -4,4 +4,4 @@ * MIT License (http://www.opensource.org/licenses/mit-license.html) * Copyright (c) 2014 Vadim Namniak */ -!function(){"use strict";var a={font:"18px Arial, sans-serif",textAlign:"left",verticalAlign:"top",paddingX:0,paddingY:0,fitParent:!1,lineBreak:"auto",sizeToFill:!1};window.CanvasTextWrapper=function(b,c,d){if(!(this instanceof CanvasTextWrapper))throw new TypeError('CanvasTextWrapper constructor failed. Use "new" keyword when instantiating.');this.canvas=b,this.text=c;for(var e in a)this[e]=d&&d[e]?d[e]:a[e];this.lineHeight=parseInt(this.font.replace(/^\D+/g,""),10),this.validate(),this.context=this.canvas.getContext("2d"),this.context.font=this.font,this.context.textBaseline="bottom",this.drawText()},CanvasTextWrapper.prototype={drawText:function(){var a=this.fitParent===!1?this.canvas.width:this.canvas.parentNode.clientWidth,b={x:0,y:0};if(this.sizeToFill){var c=(this.fitParent===!1?this.canvas.height:this.canvas.parentNode.clientHeight)-2*this.paddingX,d=0;do{this.setFontSize(++d);var e=this.getWrappedText(a),f=e.length*this.lineHeight}while(c>f);this.setFontSize(--d);var e=this.getWrappedText(a),f=e.length*this.lineHeight}else var e=this.getWrappedText(a),f=e.length*this.lineHeight;this.setTextVerticalAlign(b,f);for(var g=0;gc){for(var g=0;a.measureText(e+b[d][g]).width<=c&&gf);this.setFontSize(--d)}var e=this.getWrappedText(a),f=e.length*this.lineHeight;this.setTextVerticalAlign(b,f);for(var g=0;gc){for(var g=0;a.measureText(e+b[d][g]).width<=c&&g Date: Mon, 6 Oct 2014 12:15:49 +0800 Subject: [PATCH 4/4] Do not break text when sizeToFill is on --- CanvasTextWrapper.js | 7 ++++--- CanvasTextWrapper.min.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CanvasTextWrapper.js b/CanvasTextWrapper.js index 8a73d3b..705b141 100644 --- a/CanvasTextWrapper.js +++ b/CanvasTextWrapper.js @@ -57,14 +57,15 @@ }; if (this.sizeToFill) { - // starting at 1px increase font size by 1px until text block exceeds the height of its padded container + // starting at 1px increase font size by 1px until text block exceeds the height of its padded container or until words break var elementHeight = ((this.fitParent === false) ? this.canvas.height : this.canvas.parentNode.clientHeight) - (this.paddingX * 2); + var numWords = this.text.trim().split(/\s+/).length; var fontSize = 0; do { this.setFontSize(++fontSize); var lines = this.getWrappedText(elementWidth); var textBlockHeight = lines.length * this.lineHeight; - } while (textBlockHeight < elementHeight); + } while (textBlockHeight < elementHeight && lines.join(' ').split(/\s+/).length == numWords); // use previous font size, not the one that broke the while condition this.setFontSize(--fontSize); @@ -93,7 +94,7 @@ getWrappedText: function(elementWidth) { var maxTextLength = elementWidth - (this.paddingX * 2); - var words = this.text.split(/\s+/); + var words = this.text.trim().split(/\s+/); var lines = []; this.checkWordsLength(this.context, words, maxTextLength); diff --git a/CanvasTextWrapper.min.js b/CanvasTextWrapper.min.js index 45e4fde..3d652df 100644 --- a/CanvasTextWrapper.min.js +++ b/CanvasTextWrapper.min.js @@ -4,4 +4,4 @@ * MIT License (http://www.opensource.org/licenses/mit-license.html) * Copyright (c) 2014 Vadim Namniak */ -!function(){"use strict";var a={font:"18px Arial, sans-serif",textAlign:"left",verticalAlign:"top",paddingX:0,paddingY:0,fitParent:!1,lineBreak:"auto",sizeToFill:!1};window.CanvasTextWrapper=function(b,c,d){if(!(this instanceof CanvasTextWrapper))throw new TypeError('CanvasTextWrapper constructor failed. Use "new" keyword when instantiating.');this.canvas=b,this.text=c;for(var e in a)this[e]=d&&d[e]?d[e]:a[e];this.lineHeight=parseInt(this.font.replace(/^\D+/g,""),10),this.validate(),this.context=this.canvas.getContext("2d"),this.context.font=this.font,this.context.textBaseline="bottom",this.drawText()},CanvasTextWrapper.prototype={drawText:function(){var a=this.fitParent===!1?this.canvas.width:this.canvas.parentNode.clientWidth,b={x:0,y:0};if(this.sizeToFill){var c=(this.fitParent===!1?this.canvas.height:this.canvas.parentNode.clientHeight)-2*this.paddingX,d=0;do{this.setFontSize(++d);var e=this.getWrappedText(a),f=e.length*this.lineHeight}while(c>f);this.setFontSize(--d)}var e=this.getWrappedText(a),f=e.length*this.lineHeight;this.setTextVerticalAlign(b,f);for(var g=0;gc){for(var g=0;a.measureText(e+b[d][g]).width<=c&&gg&&f.join(" ").split(/\s+/).length==d);this.setFontSize(--e)}var f=this.getWrappedText(a),g=f.length*this.lineHeight;this.setTextVerticalAlign(b,g);for(var h=0;hc){for(var g=0;a.measureText(e+b[d][g]).width<=c&&g