19 Commits

Author SHA1 Message Date
Vadim eadb7c21ea version update 2015-09-29 22:41:32 -04:00
Vadim 7939f91cd7 fixed 'sizeToFill' and added test 2015-09-29 22:35:22 -04:00
Vadim Namniak 77617bae67 Update bower.json 2015-09-20 20:20:17 -04:00
Vadim Namniak ab03d8fc09 Update package.json 2015-09-20 20:20:01 -04:00
Vadim Namniak 348fd5cfac Update README.md 2015-09-20 20:19:45 -04:00
Vadim ed3fa124d6 added keywords 2015-09-20 20:16:21 -04:00
Vadim 0391cacffa versions 2015-09-19 22:57:07 -04:00
Vadim Namniak 4ae38b6eb8 Update README.md 2015-09-19 22:54:46 -04:00
Vadim eaf4897b0c version updated 2015-09-19 20:18:56 -04:00
Vadim 99ed3fa2fe added license 2015-09-19 20:15:00 -04:00
Vadim Namniak cafea085fd Update README.md 2015-09-19 20:08:28 -04:00
Vadim 7ab02c5b11 fixing options 2015-09-19 20:06:07 -04:00
Vadim c596c91d8d fixed line height 2015-09-19 19:47:37 -04:00
Vadim 72870dff76 refactor 2015-09-19 19:42:34 -04:00
Vadim a5171075ba updated version 2015-09-19 18:12:06 -04:00
Vadim Namniak 5951b330a4 Merge pull request #10 from nickdesaulniers/expose
browserify
2015-09-19 18:01:13 -04:00
Nick Desaulniers ca5a835bec add a simple example, also for testing purposes
Fixes #8
2015-09-18 10:37:25 -07:00
Nick Desaulniers c722fd3520 allow this to be used w/ Browserify 2015-09-18 10:36:52 -07:00
Nick Desaulniers 6891f79753 mark all files as non executable 2015-09-18 10:16:34 -07:00
8 changed files with 227 additions and 175 deletions
+154 -159
View File
@@ -1,182 +1,173 @@
/*! CanvasTextWrapper
* https://github.com/namniak/CanvasTextWrapper
* Version: 0.3.0
* Version: 0.4.0
* MIT License (http://www.opensource.org/licenses/mit-license.html)
* Copyright (c) 2014 Vadim Namniak
*/
(function() {
'use strict';
var EL_WIDTH,EL_HEIGHT,MAX_TXT_WIDTH,MAX_TXT_HEIGHT;
(function (root) {
var defaults = {
font: '18px Arial, sans-serif',
sizeToFill: false, // text is resized to fill the container (given font size is ignored)
lineHeight: 1, // default line height equivalent of '100%'
allowNewLine: true, // breaks text on every new line character '\n'
lineBreak: 'auto', // text fills the element's (canvas or parent) width going to a new line on a whole word
textAlign: 'left', // each line of text is aligned left
verticalAlign: 'top', // text lines block is aligned top
justifyLines: false, // lines are not justified
paddingX: 0, // 0px left & right text padding relatively to canvas or its container
paddingY: 0, // 0px top & bottom text padding relatively to canvas or its container
fitParent: false, // text is set to fit canvas width
strokeText: false // text is stroked according to context configuration
};
function CanvasTextWrapper(canvas, text, options) {
'use strict';
var CanvasTextWrapper = function(canvas,text,options) {
if (!(this instanceof CanvasTextWrapper)) {
return new CanvasTextWrapper(canvas,text,options);
}
var defaults = {
font: '18px Arial, sans-serif',
sizeToFill: false, // text is resized to fill the container (given font size is ignored)
lineHeight: 1, // default line height equivalent of '100%'
allowNewLine: true, // breaks text on every new line character '\n'
lineBreak: 'auto', // text fills the element's (canvas or parent) width going to a new line on a whole word
textAlign: 'left', // each line of text is aligned left
verticalAlign: 'top', // text lines block is aligned top
justifyLines: false, // lines are not justified
paddingX: 0, // 0px left & right text padding relatively to canvas or its container
paddingY: 0, // 0px top & bottom text padding relatively to canvas or its container
fitParent: false, // text is set to fit canvas width
strokeText: false // text is stroked according to context configuration
};
this.canvas = canvas;
this.text = text;
this.context = this.canvas.getContext('2d');
this.context.font = this.font;
this.context.textBaseline = 'bottom';
var opts = {};
for (var property in defaults) {
if (defaults.hasOwnProperty(property)) {
this[property] = (options && options[property]) ? options[property] : defaults[property];
opts[property] = (options && options[property]) ? options[property] : defaults[property];
}
}
EL_WIDTH = (this.fitParent === false) ? this.canvas.width : this.canvas.parentNode.clientWidth;
EL_HEIGHT = (this.fitParent === false) ? this.canvas.height : this.canvas.parentNode.clientHeight;
MAX_TXT_WIDTH = EL_WIDTH - (this.paddingX * 2);
MAX_TXT_HEIGHT = EL_HEIGHT - (this.paddingY * 2);
var context = canvas.getContext('2d');
context.font = opts.font;
context.textBaseline = 'bottom';
this._init();
};
var EL_WIDTH = (opts.fitParent === false) ? canvas.width : canvas.parentNode.clientWidth;
var EL_HEIGHT = (opts.fitParent === false) ? canvas.height : canvas.parentNode.clientHeight;
var MAX_TXT_WIDTH = EL_WIDTH - (opts.paddingX * 2);
var MAX_TXT_HEIGHT = EL_HEIGHT - (opts.paddingY * 2);
CanvasTextWrapper.prototype = {
_init: function() {
/* Substituting this line that causing a bug with font-weight numeric values */
//this.fontSize = parseInt(this.font.replace(/^\D+/g,''),10) || 18;
var fontSize, textBlockHeight, lines, newLineIndexes, textPos, lineHeight;
/* This line allow font values like : "italic 500 25px" to preserve both numeric a literal value of font-weight*/
this.fontSize = this.font.match(/\d+(px|em|\%)/g) ? +this.font.match(/\d+(px|em|\%)/g)[0].match(/\d+/g) : 18;
init();
this.textBlockHeight = 0;
this.lines = [];
this.newLineIndexes = [];
this.textPos = {x: 0,y: 0};
function init() {
fontSize = opts.font.match(/\d+(px|em|%)/g) ? +opts.font.match(/\d+(px|em|%)/g)[0].match(/\d+/g) : 18;
this._setFont(this.fontSize);
this._setLineHeight();
this._validate();
this._render();
},
textBlockHeight = 0;
lines = [];
newLineIndexes = [];
textPos = {x: 0, y: 0};
_render: function() {
if (this.sizeToFill) {
var numWords = this.text.trim().split(/\s+/).length;
setFont(fontSize);
setLineHeight();
validate();
render();
}
function render() {
if (opts.sizeToFill) {
var numWords = text.trim().split(/\s+/).length;
var fontSize = 0;
do {
this._setFont(++fontSize);
this.lineHeight = this.fontSize;
this._wrap();
} while (this.textBlockHeight < MAX_TXT_HEIGHT && (this.lines.join(' ').split(/\s+/).length == numWords));
setFont(++fontSize);
lineHeight = fontSize;
wrap();
} while (textBlockHeight < MAX_TXT_HEIGHT && (lines.join(' ').split(/\s+/).length == numWords));
this._setFont(--fontSize);
this.lineHeight = this.fontSize;
setFont(--fontSize);
lineHeight = fontSize;
wrap();
} else {
this._wrap();
wrap();
}
if (this.justifyLines && this.lineBreak === 'auto') {
this._justify();
if (opts.justifyLines && opts.lineBreak === 'auto') {
justify();
}
this._setAlignY();
this._drawText();
},
setVerticalAlign();
drawText();
}
_setFont: function(fontSize) {
var fontParts = (!this.sizeToFill) ? this.font.split(/\b\d+px\b/i) : this.context.font.split(/\b\d+px\b/i);
this.context.font = fontParts[0] + fontSize + 'px' + fontParts[1];
this.fontSize = fontSize;
},
function setFont(fontSize) {
var fontParts = (!opts.sizeToFill) ? opts.font.split(/\b\d+px\b/i) : context.font.split(/\b\d+px\b/i);
context.font = fontParts[0] + fontSize + 'px' + fontParts[1];
}
_setLineHeight: function() {
if (!isNaN(this.lineHeight)) {
this.lineHeight = this.fontSize * this.lineHeight;
} else if (this.lineHeight.toString().indexOf('px') !== -1) {
this.lineHeight = parseInt(this.lineHeight);
} else if (this.lineHeight.toString().indexOf('%') !== -1) {
this.lineHeight = (parseInt(this.lineHeight) / 100) * this.fontSize;
function setLineHeight() {
if (!isNaN(opts.lineHeight)) {
lineHeight = fontSize * opts.lineHeight;
} else if (opts.lineHeight.toString().indexOf('px') !== -1) {
lineHeight = parseInt(opts.lineHeight);
} else if (opts.lineHeight.toString().indexOf('%') !== -1) {
lineHeight = (parseInt(opts.lineHeight) / 100) * fontSize;
}
},
}
_wrap: function() {
if (this.allowNewLine) {
var newLines = this.text.trim().split('\n');
for (var i = 0,idx = 0; i < newLines.length - 1; i++) {
function wrap() {
if (opts.allowNewLine) {
var newLines = text.trim().split('\n');
for (var i = 0, idx = 0; i < newLines.length - 1; i++) {
idx += newLines[i].trim().split(/\s+/).length;
this.newLineIndexes.push(idx)
newLineIndexes.push(idx)
}
}
var words = this.text.trim().split(/\s+/);
this._checkLength(words);
this._breakText(words);
var words = text.trim().split(/\s+/);
checkLength(words);
breakText(words);
this.textBlockHeight = this.lines.length * this.lineHeight;
},
textBlockHeight = lines.length * lineHeight;
}
_checkLength: function(words) {
var testString,tokenLen,sliced,leftover;
function checkLength(words) {
var testString, tokenLen, sliced, leftover;
for (var i = 0; i < words.length; i++) {
testString = '';
tokenLen = this.context.measureText(words[i]).width;
tokenLen = context.measureText(words[i]).width;
if (tokenLen > MAX_TXT_WIDTH) {
for (var k = 0; (this.context.measureText(testString + words[i][k]).width <= MAX_TXT_WIDTH) && (k < words[i].length); k++) {
for (var k = 0; (context.measureText(testString + words[i][k]).width <= MAX_TXT_WIDTH) && (k < words[i].length); k++) {
testString += words[i][k];
}
sliced = words[i].slice(0,k);
sliced = words[i].slice(0, k);
leftover = words[i].slice(k);
words.splice(i,1,sliced,leftover);
words.splice(i, 1, sliced, leftover);
}
}
},
}
_breakText: function(words) {
for (var i = 0,j = 0; i < words.length; j++) {
this.lines[j] = '';
function breakText(words) {
for (var i = 0, j = 0; i < words.length; j++) {
lines[j] = '';
if (this.lineBreak === 'auto') {
while ((this.context.measureText(this.lines[j] + words[i]).width <= MAX_TXT_WIDTH) && (i < words.length)) {
if (opts.lineBreak === 'auto') {
while ((context.measureText(lines[j] + words[i]).width <= MAX_TXT_WIDTH) && (i < words.length)) {
this.lines[j] += words[i] + ' ';
lines[j] += words[i] + ' ';
i++;
if (this.allowNewLine) {
for (var k = 0; k < this.newLineIndexes.length; k++) {
if (this.newLineIndexes[k] === i) {
if (opts.allowNewLine) {
for (var k = 0; k < newLineIndexes.length; k++) {
if (newLineIndexes[k] === i) {
j++;
this.lines[j] = '';
lines[j] = '';
break;
}
}
}
}
this.lines[j] = this.lines[j].trim();
lines[j] = lines[j].trim();
} else {
this.lines[j] = words[i];
lines[j] = words[i];
i++;
}
}
},
}
_justify: function() {
var maxLen,longestLineIndex,tokenLen;
for (var i = 0; i < this.lines.length; i++) {
tokenLen = this.context.measureText(this.lines[i]).width;
function justify() {
var maxLen, longestLineIndex, tokenLen;
for (var i = 0; i < lines.length; i++) {
tokenLen = context.measureText(lines[i]).width;
if (!maxLen || tokenLen > maxLen) {
maxLen = tokenLen;
@@ -185,18 +176,18 @@
}
// fill lines with extra spaces
var numWords,spaceLength,numOfSpaces,num,filler;
var numWords, spaceLength, numOfSpaces, num, filler;
var delimiter = '\u200A';
for (i = 0; i < this.lines.length; i++) {
for (i = 0; i < lines.length; i++) {
if (i === longestLineIndex) continue;
numWords = this.lines[i].trim().split(/\s+/).length;
numWords = lines[i].trim().split(/\s+/).length;
if (numWords <= 1) continue;
this.lines[i] = this.lines[i].trim().split(/\s+/).join(delimiter);
lines[i] = lines[i].trim().split(/\s+/).join(delimiter);
spaceLength = this.context.measureText(delimiter).width;
numOfSpaces = (maxLen - this.context.measureText(this.lines[i]).width) / spaceLength;
spaceLength = context.measureText(delimiter).width;
numOfSpaces = (maxLen - context.measureText(lines[i]).width) / spaceLength;
num = numOfSpaces / (numWords - 1);
filler = '';
@@ -204,85 +195,89 @@
filler += delimiter;
}
this.lines[i] = this.lines[i].trim().split(delimiter).join(filler);
lines[i] = lines[i].trim().split(delimiter).join(filler);
//console.log('numWords:', numWords, 'numOfSpaces:', numOfSpaces, 'num:', num);
}
},
}
_drawText: function() {
for (var i = 0; i < this.lines.length; i++) {
this._setAlignX(this.lines[i]);
function drawText() {
for (var i = 0; i < lines.length; i++) {
setHorizontalAlign(lines[i]);
this.textPos.y = parseInt(this.textPos.y) + this.lineHeight;
this.context.fillText(this.lines[i],this.textPos.x,this.textPos.y);
textPos.y = parseInt(textPos.y) + lineHeight;
context.fillText(lines[i], textPos.x, textPos.y);
if (this.strokeText) {
this.context.strokeText(this.lines[i],this.textPos.x,this.textPos.y);
if (opts.strokeText) {
context.strokeText(lines[i], textPos.x, textPos.y);
}
}
},
}
_setAlignX: function(line) {
if (this.textAlign == 'center') {
this.textPos.x = (EL_WIDTH - this.context.measureText(line).width) / 2;
} else if (this.textAlign == 'right') {
this.textPos.x = EL_WIDTH - this.context.measureText(line).width - this.paddingX;
function setHorizontalAlign(line) {
if (opts.textAlign == 'center') {
textPos.x = (EL_WIDTH - context.measureText(line).width) / 2;
} else if (opts.textAlign == 'right') {
textPos.x = EL_WIDTH - context.measureText(line).width - opts.paddingX;
} else {
this.textPos.x = this.paddingX;
textPos.x = opts.paddingX;
}
},
}
_setAlignY: function() {
if (this.verticalAlign == 'middle') {
this.textPos.y = (EL_HEIGHT - this.textBlockHeight) / 2;
} else if (this.verticalAlign == 'bottom') {
this.textPos.y = EL_HEIGHT - this.textBlockHeight - this.paddingY;
function setVerticalAlign() {
if (opts.verticalAlign == 'middle') {
textPos.y = (EL_HEIGHT - textBlockHeight) / 2;
} else if (opts.verticalAlign == 'bottom') {
textPos.y = EL_HEIGHT - textBlockHeight - opts.paddingY;
} else {
this.textPos.y = this.paddingY;
textPos.y = opts.paddingY;
}
},
}
_validate: function() {
if (!(this.canvas instanceof HTMLCanvasElement))
function validate() {
if (!(canvas instanceof HTMLCanvasElement))
throw new TypeError('The first parameter must be an instance of HTMLCanvasElement.');
if (typeof this.text !== 'string')
if (typeof text !== 'string')
throw new TypeError('The second parameter must be a string.');
if (isNaN(this.fontSize))
if (isNaN(fontSize))
throw new TypeError('Cannot parse "font".');
if (isNaN(this.lineHeight))
if (isNaN(lineHeight))
throw new TypeError('Cannot parse "lineHeight".');
if (this.textAlign.toLocaleLowerCase() !== 'left' && this.textAlign.toLocaleLowerCase() !== 'center' && this.textAlign.toLocaleLowerCase() !== 'right')
if (opts.textAlign.toLocaleLowerCase() !== 'left' && opts.textAlign.toLocaleLowerCase() !== 'center' && opts.textAlign.toLocaleLowerCase() !== 'right')
throw new TypeError('Property "textAlign" must be set to either "left", "center", or "right".');
if (this.verticalAlign.toLocaleLowerCase() !== 'top' && this.verticalAlign.toLocaleLowerCase() !== 'middle' && this.verticalAlign.toLocaleLowerCase() !== 'bottom')
if (opts.verticalAlign.toLocaleLowerCase() !== 'top' && opts.verticalAlign.toLocaleLowerCase() !== 'middle' && opts.verticalAlign.toLocaleLowerCase() !== 'bottom')
throw new TypeError('Property "verticalAlign" must be set to either "top", "middle", or "bottom".');
if (typeof this.justifyLines !== 'boolean')
if (typeof opts.justifyLines !== 'boolean')
throw new TypeError('Property "justifyLines" must be set to a Boolean.');
if (isNaN(this.paddingX))
if (isNaN(opts.paddingX))
throw new TypeError('Property "paddingX" must be set to a Number.');
if (isNaN(this.paddingY))
if (isNaN(opts.paddingY))
throw new TypeError('Property "paddingY" must be set to a Number.');
if (typeof this.fitParent !== 'boolean')
if (typeof opts.fitParent !== 'boolean')
throw new TypeError('Property "fitParent" must be set to a Boolean.');
if (this.lineBreak.toLocaleLowerCase() !== 'auto' && this.lineBreak.toLocaleLowerCase() !== 'word')
if (opts.lineBreak.toLocaleLowerCase() !== 'auto' && opts.lineBreak.toLocaleLowerCase() !== 'word')
throw new TypeError('Property "lineBreak" must be set to either "auto" or "word".');
if (typeof this.sizeToFill !== 'boolean')
if (typeof opts.sizeToFill !== 'boolean')
throw new TypeError('Property "sizeToFill" must be set to a Boolean.');
if (typeof this.strokeText !== 'boolean')
if (typeof opts.strokeText !== 'boolean')
throw new TypeError('Property "strokeText" must be set to a Boolean.');
}
};
}
window.CanvasTextWrapper = CanvasTextWrapper;
})();
if ('module' in root && 'exports' in module) {
module.exports = CanvasTextWrapper;
} else {
root.CanvasTextWrapper = CanvasTextWrapper;
}
})(this);
+2 -3
View File
File diff suppressed because one or more lines are too long
Executable → Regular
View File
+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015 Vadim Namniak
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
+15 -10
View File
@@ -1,9 +1,9 @@
CanvasTextWrapper
=================
##Syntax
## Syntax
```
new CanvasTextWrapper(HTMLCanvasElement, String [, options]);
CanvasTextWrapper(HTMLCanvasElement, String [, options]);
```
```options``` - is an object with the following available properties and values:
@@ -23,7 +23,7 @@ new CanvasTextWrapper(HTMLCanvasElement, String [, options]);
NOTE: if a single word is too long to fit the width with specified font size, it will break on any letter unless ```sizeToFill``` option is enabled.
##Defaults
## Default options
```
{
font: "18px Arial, sans-serif",
@@ -41,9 +41,12 @@ NOTE: if a single word is too long to fit the width with specified font size, it
}
```
##Usage
## Usage
Configure context settings properties such as "fillStyle", "lineWidth" or "strokeStyle" before using CanvasTextWrapper like so:
```
```
var CanvasTextWrapper = require('canvas-text-wrapper').CanvasTextWrapper;
var canvas = document.getElementById("#canvasText");
canvas.width = 200;
canvas.height = 200;
@@ -53,13 +56,15 @@ context.strokeStyle = "#ff0000";
CanvasTextWrapper(canvas,"Hello"); //default options will apply
```
##Examples
http://namniak.github.io/CanvasTextWrapper/
## Test
In terminal go to CanvasTextWrapper folder and run ```npm t```
##Installation
## Examples
[Demo](http://namniak.github.io/CanvasTextWrapper/)
## Installation
```
bower install canvas-text-wrapper
npm install canvas-text-wrapper
npm i canvas-text-wrapper --save
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "canvas-text-wrapper",
"version": "0.3.2",
"version": "0.4.4",
"ignore": [
"**/.*",
"**/*.log",
Executable → Regular
+6 -2
View File
@@ -1,9 +1,10 @@
{
"name": "canvas-text-wrapper",
"description": "Pure JavaScript canvas text wrapper that automatically splits a string into lines on specified rule with alignment and padding.",
"version": "0.3.2",
"version": "0.4.4",
"license": "MIT",
"main": "CanvasTextWrapper.min.js",
"keywords": ["canvas", "canvas text", " text", "split"],
"homepage": "http://namniak.github.io/CanvasTextWrapper/",
"repository": {
"type": "git",
@@ -13,8 +14,11 @@
"bugs": {
"url": "https://github.com/namniak/CanvasTextWrapper/issues"
},
"scripts": {
"test": "beefy test/test.js --live --open"
},
"devDependencies": {
"grunt-contrib-uglify": "^0.9.1",
"uglify-save-license": "^0.4.1"
}
}
}
+29
View File
@@ -0,0 +1,29 @@
var CanvasTextWrapper = require('../CanvasTextWrapper').CanvasTextWrapper;
var body = document.getElementsByTagName('body')[0];
body.style.margin = 0;
var canvas = document.createElement('canvas');
canvas.width = 600;
canvas.height = 600;
canvas.style.position = 'absolute';
canvas.style.left = '50%';
canvas.style.top = '50%';
canvas.style.transform = 'translateX(-50%) translateY(-50%)';
body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop('0.2', 'magenta');
gradient.addColorStop('0.5', 'blue');
gradient.addColorStop('0.7', 'purple');
ctx.fillStyle = gradient;
var opts = {
sizeToFill: true,
textAlign: 'center',
verticalAlign: 'middle'
};
CanvasTextWrapper(canvas, 'What an awesome library!', opts);