47 Commits

Author SHA1 Message Date
Vadim Namniak 9515466c5d updated minified file. v 0.3.2 2015-05-06 00:02:06 -04:00
Vadim Namniak 457a58a4f8 uncommitted modules 2015-05-05 23:59:41 -04:00
Vadim Namniak af8face42e got rid of node modules in repo 2015-05-05 23:57:06 -04:00
Vadim Namniak 5f12d60d12 git ignore updates 2015-05-05 23:53:15 -04:00
Vadim Namniak 66da3411c3 updated to v0.3.1 2015-05-05 17:14:54 -04:00
Vadim Namniak a1ba88647e Merge pull request #6 from steoo/master
Fixed bug with numeric value of font-weight
2015-05-05 13:28:55 -04:00
Steoo 13abe2526c Fixed bug with numeric value of font-weight 2015-05-04 22:30:48 +02:00
Vadim Namniak ffcaf3425c Update README.md 2015-01-25 15:27:49 -05:00
Vadim Namniak 365bd4e24c Update README.md 2015-01-25 15:27:19 -05:00
Vadim Namniak 96001e64df Update README.md 2015-01-25 15:26:14 -05:00
Vadim Namniak 49fea5c2bb Update README.md 2015-01-25 15:23:53 -05:00
Vadim Namniak 020d1dee52 Update README.md 2015-01-25 15:22:28 -05:00
Vadim Namniak c75fae5ea0 Update README.md 2015-01-25 15:18:39 -05:00
Vadim Namniak 9b2c460a69 v0.3.0 justifyLines, lineHeight and allowNewLine 2015-01-25 14:53:51 -05:00
Vadim Namniak 055cdaeb13 version updates 2014-10-12 11:04:53 -04:00
Vadim Namniak cfdccc0c61 version update 2014-10-12 11:03:27 -04:00
Vadim Namniak bb707cd508 Update README.md 2014-10-12 10:49:45 -04:00
Vadim Namniak 8dda2cb645 Update README.md 2014-10-12 10:37:29 -04:00
Vadim Namniak 7e3d425769 Update README.md 2014-10-12 01:57:56 -04:00
Vadim Namniak 6e03f629ce Merge pull request #3 from gmjosack/master
Add the ability to stroke text.
2014-10-12 01:32:44 -04:00
Gary M. Josack ebb8908812 Add the ability to stroke text.
This is useful in scenarios where a background is many colors.
2014-10-11 21:33:06 -07:00
Vadim Namniak 30c326ed36 Update README.md 2014-10-08 23:03:57 -04:00
Vadim Namniak f464af8c5d context fixes + default font size added 2014-10-08 22:53:50 -04:00
Vadim Namniak 5b7a7f9cfe Merge pull request #2 from peeinears/master
Added sizeToFill option for resizing text to fill its padded container
2014-10-08 21:34:52 -04:00
Ian Pearce 45a2b83782 Do not break text when sizeToFill is on 2014-10-06 12:15:49 +08:00
Ian Pearce 43c8775359 Run grunt 2014-10-06 03:07:23 +08:00
Ian Pearce 4924f6f06a Slightly DRYer 2014-10-06 03:05:27 +08:00
Ian Pearce 60c009b432 Added sizeToFill option for resizing text to fill its container 2014-10-06 03:00:53 +08:00
Vadim Namniak e5ad6e9e22 Update README.md 2014-06-27 09:11:06 -04:00
Vadim Namniak b6eb83cbbc Update README.md 2014-06-27 09:10:44 -04:00
Vadim Namniak ba409c2f9a Update README.md 2014-06-27 09:10:31 -04:00
Vadim Namniak 9b14e6bc20 Update README.md 2014-06-27 09:09:55 -04:00
namniak ab5ee97446 gitignore update 2014-06-27 02:53:02 -04:00
namniak 68f08dccf0 gitignore update 2014-06-27 02:52:23 -04:00
namniak b86396f979 v0.1.1 2014-06-27 02:48:39 -04:00
Vadim Namniak 53f765b0e8 Update README.md 2014-06-27 02:35:40 -04:00
Vadim Namniak 4d7e9ed627 Delete package.json~ 2014-06-27 02:30:41 -04:00
Vadim Namniak 68a9da4826 Delete .npmignore~ 2014-06-27 02:30:28 -04:00
namniak 9534bbbdf7 added bower & npm repositories 2014-06-27 02:29:15 -04:00
Vadim Namniak d4566c71cf Update README.md 2014-06-27 02:16:45 -04:00
Vadim Namniak c5464dab42 Update README.md 2014-06-27 02:11:03 -04:00
Vadim Namniak c18627bd6a Delete bower.json~ 2014-06-27 01:44:38 -04:00
namniak a884a840db v0.1.0 2014-06-27 01:43:51 -04:00
Vadim Namniak c53c170d5c Delete bower.json 2014-06-27 01:07:49 -04:00
Vadim Namniak 19d9b83fea Delete package.json 2014-06-27 01:07:42 -04:00
Vadim Namniak 2361b45226 Delete CanvasTextWrapper.min.js 2014-06-27 01:07:30 -04:00
Vadim Namniak f2f142e81b Delete CanvasTextWrapper.js 2014-06-27 01:07:18 -04:00
14 changed files with 429 additions and 742 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
.idea/
node_modules/
node_modules/
+3
View File
@@ -0,0 +1,3 @@
node_modules
examples
bower.json
+288
View File
@@ -0,0 +1,288 @@
/*! CanvasTextWrapper
* https://github.com/namniak/CanvasTextWrapper
* Version: 0.3.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;
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
};
var CanvasTextWrapper = function(canvas,text,options) {
if (!(this instanceof CanvasTextWrapper)) {
return new CanvasTextWrapper(canvas,text,options);
}
this.canvas = canvas;
this.text = text;
this.context = this.canvas.getContext('2d');
this.context.font = this.font;
this.context.textBaseline = 'bottom';
for (var property in defaults) {
if (defaults.hasOwnProperty(property)) {
this[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);
this._init();
};
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;
/* 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;
this.textBlockHeight = 0;
this.lines = [];
this.newLineIndexes = [];
this.textPos = {x: 0,y: 0};
this._setFont(this.fontSize);
this._setLineHeight();
this._validate();
this._render();
},
_render: function() {
if (this.sizeToFill) {
var numWords = this.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));
this._setFont(--fontSize);
this.lineHeight = this.fontSize;
} else {
this._wrap();
}
if (this.justifyLines && this.lineBreak === 'auto') {
this._justify();
}
this._setAlignY();
this._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;
},
_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;
}
},
_wrap: function() {
if (this.allowNewLine) {
var newLines = this.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)
}
}
var words = this.text.trim().split(/\s+/);
this._checkLength(words);
this._breakText(words);
this.textBlockHeight = this.lines.length * this.lineHeight;
},
_checkLength: function(words) {
var testString,tokenLen,sliced,leftover;
for (var i = 0; i < words.length; i++) {
testString = '';
tokenLen = this.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++) {
testString += words[i][k];
}
sliced = words[i].slice(0,k);
leftover = words[i].slice(k);
words.splice(i,1,sliced,leftover);
}
}
},
_breakText: function(words) {
for (var i = 0,j = 0; i < words.length; j++) {
this.lines[j] = '';
if (this.lineBreak === 'auto') {
while ((this.context.measureText(this.lines[j] + words[i]).width <= MAX_TXT_WIDTH) && (i < words.length)) {
this.lines[j] += words[i] + ' ';
i++;
if (this.allowNewLine) {
for (var k = 0; k < this.newLineIndexes.length; k++) {
if (this.newLineIndexes[k] === i) {
j++;
this.lines[j] = '';
break;
}
}
}
}
this.lines[j] = this.lines[j].trim();
} else {
this.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;
if (!maxLen || tokenLen > maxLen) {
maxLen = tokenLen;
longestLineIndex = i;
}
}
// fill lines with extra spaces
var numWords,spaceLength,numOfSpaces,num,filler;
var delimiter = '\u200A';
for (i = 0; i < this.lines.length; i++) {
if (i === longestLineIndex) continue;
numWords = this.lines[i].trim().split(/\s+/).length;
if (numWords <= 1) continue;
this.lines[i] = this.lines[i].trim().split(/\s+/).join(delimiter);
spaceLength = this.context.measureText(delimiter).width;
numOfSpaces = (maxLen - this.context.measureText(this.lines[i]).width) / spaceLength;
num = numOfSpaces / (numWords - 1);
filler = '';
for (var j = 0; j < num; j++) {
filler += delimiter;
}
this.lines[i] = this.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]);
this.textPos.y = parseInt(this.textPos.y) + this.lineHeight;
this.context.fillText(this.lines[i],this.textPos.x,this.textPos.y);
if (this.strokeText) {
this.context.strokeText(this.lines[i],this.textPos.x,this.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;
} else {
this.textPos.x = this.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;
} else {
this.textPos.y = this.paddingY;
}
},
_validate: function() {
if (!(this.canvas instanceof HTMLCanvasElement))
throw new TypeError('The first parameter must be an instance of HTMLCanvasElement.');
if (typeof this.text !== 'string')
throw new TypeError('The second parameter must be a string.');
if (isNaN(this.fontSize))
throw new TypeError('Cannot parse "font".');
if (isNaN(this.lineHeight))
throw new TypeError('Cannot parse "lineHeight".');
if (this.textAlign.toLocaleLowerCase() !== 'left' && this.textAlign.toLocaleLowerCase() !== 'center' && this.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')
throw new TypeError('Property "verticalAlign" must be set to either "top", "middle", or "bottom".');
if (typeof this.justifyLines !== 'boolean')
throw new TypeError('Property "justifyLines" must be set to a Boolean.');
if (isNaN(this.paddingX))
throw new TypeError('Property "paddingX" must be set to a Number.');
if (isNaN(this.paddingY))
throw new TypeError('Property "paddingY" must be set to a Number.');
if (typeof this.fitParent !== 'boolean')
throw new TypeError('Property "fitParent" must be set to a Boolean.');
if (this.lineBreak.toLocaleLowerCase() !== 'auto' && this.lineBreak.toLocaleLowerCase() !== 'word')
throw new TypeError('Property "lineBreak" must be set to either "auto" or "word".');
if (typeof this.sizeToFill !== 'boolean')
throw new TypeError('Property "sizeToFill" must be set to a Boolean.');
if (typeof this.strokeText !== 'boolean')
throw new TypeError('Property "strokeText" must be set to a Boolean.');
}
};
window.CanvasTextWrapper = CanvasTextWrapper;
})();
+7
View File
File diff suppressed because one or more lines are too long
Executable
+27
View File
@@ -0,0 +1,27 @@
module.exports = function (grunt) {
var saveLicense = require('uglify-save-license');
// project configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
my_target: {
options: {
preserveComments: saveLicense
},
src: ['CanvasTextWrapper.js'],
dest: 'CanvasTextWrapper.min.js'
}
}
});
// load plugins
grunt.loadNpmTasks('uglify-save-license');
grunt.loadNpmTasks('grunt-contrib-uglify');
// default tasks.
grunt.registerTask('default', [
'uglify'
]);
};
+65
View File
@@ -0,0 +1,65 @@
CanvasTextWrapper
=================
##Syntax
```
new CanvasTextWrapper(HTMLCanvasElement, String [, options]);
```
```options``` - is an object with the following available properties and values:
- ```font:``` (String) - text style that includes font size in px, font weight, font family, etc. Similar to CSS font shorthand property
- ```lineHeight:``` (String or Number) - Number means n times font size where 1 is equivalent to '100%'. Also the property can be set in "%" or "px" using String
- ```textAlign: "left" | "center" | "right"``` - horizontal alignment of each line
- ```verticalAlign: "top" | "middle" | "bottom"``` - vertical alignment of the whole text block
- ```paddingX:``` (Number) - horizontal padding (in px) set equally on both, left and right sides
- ```paddingY:``` (Number) - vertical padding (in px) set equally on both, top and bottom sides
- ```fitParent:``` (Boolean) - if enabled, text will fit canvas container's width instead of canvas own width
- ```lineBreak: "auto" | "word"``` - text split rule. When using ```"auto"```, text goes to a next line on a whole word when there's no more room. If ```"word"``` is set as value, each next word will be placed on a new line.
- ```sizeToFill:``` (Boolean) - ignore given font size and line height and resize text to fill its padded container
- ```strokeText:``` (Boolean) - outline text based on context configuration
- ```justifyLines:``` (Boolean) - if enabled, all lines match the same width with flexed spaces between words (one-word lines are ignored).
- ```allowNewLine:``` (Boolean) if enabled, the text breaks on every new line character "\n" otherwise it'll be considered as a space
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
```
{
font: "18px Arial, sans-serif",
lineHeight: 1,
textAlign: "left",
verticalAlign: "top",
paddingX: 0,
paddingY: 0,
fitParent: false,
lineBreak: "auto",
strokeText: false
sizeToFill: false,
allowNewLine: true,
justifyLines: false
}
```
##Usage
Configure context settings properties such as "fillStyle", "lineWidth" or "strokeStyle" before using CanvasTextWrapper like so:
```
var canvas = document.getElementById("#canvasText");
canvas.width = 200;
canvas.height = 200;
context = canvas.getContext("2d");
context.lineWidth = 2;
context.strokeStyle = "#ff0000";
CanvasTextWrapper(canvas,"Hello"); //default options will apply
```
##Examples
http://namniak.github.io/CanvasTextWrapper/
##Installation
```
bower install canvas-text-wrapper
npm install canvas-text-wrapper
```
+18
View File
@@ -0,0 +1,18 @@
{
"name": "canvas-text-wrapper",
"version": "0.3.2",
"ignore": [
"**/.*",
"**/*.log",
"**/*.json",
"Gruntfile.js",
"examples",
"node_modules",
"README.md"
],
"repository": {
"type": "git",
"url": "git://github.com/namniak/CanvasTextWrapper.git"
},
"homepage": "http://namniak.github.io/CanvasTextWrapper/"
}
-48
View File
@@ -1,48 +0,0 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
-159
View File
@@ -1,159 +0,0 @@
body {
width: 960px;
margin: auto;
font-family: 'Open Sans', sans-serif;
color: #fff;
line-height: 140%;
}
div, header, footer, article {
box-sizing: border-box;
}
header, footer, article {
width: 930px;
margin: auto;
background-color: #3299af;
padding: 20px;
}
header {
font-size: 60px;
font-weight: 700;
}
.description {
width: 800px;
margin-top: 20px;
font-size: 22px;
font-weight: normal;
}
footer {
margin-top: 40px;
}
a {
font-size: 20px;
font-weight: normal;
color: #fff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
article {
border-top: 1px solid #fff;
}
.white-block {
display: table-cell;
height: 30px;
padding: 20px;
vertical-align: middle;
background-color: #fff;
font-weight: 100;
color: #2E6571;
line-height: 120%;
}
pre.white-block {
padding-left: 0;
padding-bottom: 0;
}
p {
margin: 10px 0;
line-height: 120%;
}
h2 {
font-size: 30px;
padding-top: 40px;
}
h2:first-of-type {
padding-top: 10px;
}
h6 {
font-weight: 700;
text-decoration: underline;
display: inline;
}
.syntax {
padding: 10px 10px 0 20px;
}
.syntax > li {
list-style: none;
margin-bottom: 15px;
}
.syntax p {
margin-bottom: 0;
}
.values li {
text-indent: 20px;
list-style: inside circle;
line-height: 120%;
}
section {
margin-top: 40px;
}
section > div {
display: inline-block;
position: relative;
width: 450px;
margin: 15px;
border: 1px solid #3299af;
overflow: hidden;
}
section h2 {
color: #3299af;
text-indent: 30px;
margin-bottom: 20px;
}
section > div div {
width: 100%;
height: 240px;
background-color: #3299af;
margin-top: 250px;
padding: 20px;
}
canvas, img {
position: absolute;
top: 0;
left: 0;
}
img {
width: 100%;
z-index: -1;
}
span {
margin-left: 20px;
margin-right: 5px;
}
span, .emph {
font-weight: 600;
display: inline;
}
strong {
background: #fff;
color: #3299af;
font-weight: bold;
padding: 0 5px;
}
-160
View File
@@ -1,160 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,700,600' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/styles.css">
<script src="js/CanvasTextWrapper.js"></script>
<script src="js/options.js"></script>
<script src="js/examples.js"></script>
<title>CanvasTextWrapper</title>
</head>
<body>
<header>
<p>CanvasTextWrapper</p>
<p class="description">
Pure JavaScript canvas text wrapper that automatically splits a string into lines on specified rule with
alignment and padding.
</p>
</header>
<article>
<h2>Syntax</h2><br/>
<p class="white-block">CanvasTextWrapper(HTMLCanvasElement, String [, Options]);</p><br/>
<div>
<div class="emph">Options</div>
- is an object with the following available properties and values:
</div>
<ul class="syntax">
<li>
<div class="emph">font</div>
(String) - text style that includes font size in px, weight, font family, etc. Similar to CSS font shorthand
property
</li>
<li>
<div class="emph">lineHeight</div>
(Number or String)
<ul class="values">
<li>number - n times font size where 1 is equivalent to '100%'</li>
<li>"%"</li>
<li>"px"</li>
</ul>
</li>
<li>
<div class="emph">textAlign</div>
(String) - horizontal alignment of each line
<ul class="values">
<li>"left"</li>
<li>"center"</li>
<li>"right"</li>
</ul>
</li>
<li>
<div class="emph">verticalAlign</div>
(String) - vertical alignment of the whole text block
<ul class="values">
<li>"top"</li>
<li>"middle"</li>
<li>"bottom"</li>
</ul>
</li>
<li>
<div class="emph">paddingX</div>
(Number) - horizontal padding (in px) set equally on both, left and right sides
</li>
<li>
<div class="emph">paddingY</div>
(Number) - vertical padding (in px) set equally on both, top and bottom sides
</li>
<li>
<div class="emph">fitParent</div>
(Boolean) - parameter that controls which element to fit
<ul class="values">
<li>true - fit canvas parent's width instead of canvas own width</li>
<li>false - fit canvas width</li>
</ul>
</li>
<li>
<div class="emph">lineBreak</div>
(String) - text split rule
<ul class="values">
<li>"auto" - text fills padded container going to a next line on a whole word</li>
<li>"word" - each next word will be placed on a new line</li>
</ul>
</li>
<li>
<div class="emph">sizeToFill</div>
(Boolean) - auto font size to fill text container
<ul class="values">
<li>true - ignore given font size/line height and resize text to fill its padded container</li>
<li>false - use specified or default font size</li>
</ul>
</li>
<li>
<div class="emph">strokeText</div>
(Boolean) - if enabled, all lines match the same width with flexed spaces between words (one-word lines are
ignored)
</li>
<li>
<div class="emph">justifyLines</div>
(Boolean) - if enabled, text breaks on every new line character "\n"
</li>
<p>
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.
</p>
</ul>
<h2>Default Options</h2><br/>
<pre class="white-block">
{
<strong>font</strong>: "18px Arial, sans-serif",
<strong>lineHeight</strong>: 1,
<strong>textAlign</strong>: "left",
<strong>verticalAlign</strong>: "top",
<strong>paddingX</strong>: 0,
<strong>paddingY</strong>: 0,
<strong>fitParent</strong>: false,
<strong>lineBreak</strong>: "auto",
<strong>sizeToFill</strong>: false,
<strong>allowNewLine</strong>: true,
<strong>justifyLines</strong>: false,
<strong>strokeText</strong>: false
}
</pre>
<h2>Usage</h2><br/>
<p>
Configure context settings properties such as "fillStyle", "lineWidth" or "strokeStyle" before using
CanvasTextWrapper like so:
</p>
<pre class="white-block">
var canvas = document.getElementById("#canvasText");
canvas.width = 200;
canvas.height = 200;
context = canvas.getContext("2d");
context.lineWidth = 2;
context.strokeStyle = "#ff0000";
CanvasTextWrapper(canvas,"Hello"); //default options will apply
</pre>
<h2>Installation</h2><br/>
<p class="white-block">
bower install canvas-text-wrapper<br/>
npm install canvas-text-wrapper
</p>
</article>
<section>
<h2>Examples</h2>
</section>
<footer>
&larr;&nbsp;<a href="https://github.com/namniak/CanvasTextWrapper">View on GitHub</a>
</footer>
</body>
</html>
-276
View File
@@ -1,276 +0,0 @@
(function (root) {
function CanvasTextWrapper(canvas, text, options) {
'use strict';
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
};
var opts = {};
for (var property in defaults) {
if (defaults.hasOwnProperty(property)) {
opts[property] = (options && options[property]) ? options[property] : defaults[property];
}
}
var context = canvas.getContext('2d');
context.font = opts.font;
context.textBaseline = 'bottom';
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);
var fontSize, textBlockHeight, lines, newLineIndexes, textPos, lineHeight;
init();
function init() {
fontSize = opts.font.match(/\d+(px|em|%)/g) ? +opts.font.match(/\d+(px|em|%)/g)[0].match(/\d+/g) : 18;
textBlockHeight = 0;
lines = [];
newLineIndexes = [];
textPos = {x: 0, y: 0};
setFont(fontSize);
setLineHeight();
validate();
render();
}
function render() {
if (opts.sizeToFill) {
var numWords = text.trim().split(/\s+/).length;
var fontSize = 0;
do {
setFont(++fontSize);
lineHeight = fontSize;
wrap();
} while (textBlockHeight < MAX_TXT_HEIGHT && (lines.join(' ').split(/\s+/).length == numWords));
setFont(--fontSize);
lineHeight = fontSize;
wrap();
} else {
wrap();
}
if (opts.justifyLines && opts.lineBreak === 'auto') {
justify();
}
setVerticalAlign();
drawText();
}
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];
}
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;
}
}
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;
newLineIndexes.push(idx)
}
}
var words = text.trim().split(/\s+/);
checkLength(words);
breakText(words);
textBlockHeight = lines.length * lineHeight;
}
function checkLength(words) {
var testString, tokenLen, sliced, leftover;
for (var i = 0; i < words.length; i++) {
testString = '';
tokenLen = context.measureText(words[i]).width;
if (tokenLen > MAX_TXT_WIDTH) {
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);
leftover = words[i].slice(k);
words.splice(i, 1, sliced, leftover);
}
}
}
function breakText(words) {
for (var i = 0, j = 0; i < words.length; j++) {
lines[j] = '';
if (opts.lineBreak === 'auto') {
while ((context.measureText(lines[j] + words[i]).width <= MAX_TXT_WIDTH) && (i < words.length)) {
lines[j] += words[i] + ' ';
i++;
if (opts.allowNewLine) {
for (var k = 0; k < newLineIndexes.length; k++) {
if (newLineIndexes[k] === i) {
j++;
lines[j] = '';
break;
}
}
}
}
lines[j] = lines[j].trim();
} else {
lines[j] = words[i];
i++;
}
}
}
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;
longestLineIndex = i;
}
}
// fill lines with extra spaces
var numWords, spaceLength, numOfSpaces, num, filler;
var delimiter = '\u200A';
for (i = 0; i < lines.length; i++) {
if (i === longestLineIndex) continue;
numWords = lines[i].trim().split(/\s+/).length;
if (numWords <= 1) continue;
lines[i] = lines[i].trim().split(/\s+/).join(delimiter);
spaceLength = context.measureText(delimiter).width;
numOfSpaces = (maxLen - context.measureText(lines[i]).width) / spaceLength;
num = numOfSpaces / (numWords - 1);
filler = '';
for (var j = 0; j < num; j++) {
filler += delimiter;
}
lines[i] = lines[i].trim().split(delimiter).join(filler);
//console.log('numWords:', numWords, 'numOfSpaces:', numOfSpaces, 'num:', num);
}
}
function drawText() {
for (var i = 0; i < lines.length; i++) {
setHorizontalAlign(lines[i]);
textPos.y = parseInt(textPos.y) + lineHeight;
context.fillText(lines[i], textPos.x, textPos.y);
if (opts.strokeText) {
context.strokeText(lines[i], textPos.x, textPos.y);
}
}
}
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 {
textPos.x = opts.paddingX;
}
}
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 {
textPos.y = opts.paddingY;
}
}
function validate() {
if (!(canvas instanceof HTMLCanvasElement))
throw new TypeError('The first parameter must be an instance of HTMLCanvasElement.');
if (typeof text !== 'string')
throw new TypeError('The second parameter must be a string.');
if (isNaN(fontSize))
throw new TypeError('Cannot parse "font".');
if (isNaN(lineHeight))
throw new TypeError('Cannot parse "lineHeight".');
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 (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 opts.justifyLines !== 'boolean')
throw new TypeError('Property "justifyLines" must be set to a Boolean.');
if (isNaN(opts.paddingX))
throw new TypeError('Property "paddingX" must be set to a Number.');
if (isNaN(opts.paddingY))
throw new TypeError('Property "paddingY" must be set to a Number.');
if (typeof opts.fitParent !== 'boolean')
throw new TypeError('Property "fitParent" must be set to a Boolean.');
if (opts.lineBreak.toLocaleLowerCase() !== 'auto' && opts.lineBreak.toLocaleLowerCase() !== 'word')
throw new TypeError('Property "lineBreak" must be set to either "auto" or "word".');
if (typeof opts.sizeToFill !== 'boolean')
throw new TypeError('Property "sizeToFill" must be set to a Boolean.');
if (typeof opts.strokeText !== 'boolean')
throw new TypeError('Property "strokeText" must be set to a Boolean.');
}
}
if ('module' in root && 'exports' in module) {
module.exports = CanvasTextWrapper;
} else {
root.CanvasTextWrapper = CanvasTextWrapper;
}
})(this);
-51
View File
@@ -1,51 +0,0 @@
document.onreadystatechange = function() {
'use strict';
if (document.readyState === 'complete') {
(function() {
var container = document.getElementsByTagName('section')[0];
var w = 448;
var h = 250;
var options = model;
createExamples();
function createExamples() {
var fragment = new DocumentFragment();
var context;
for (var i = 0; i < options.length; i++) {
var exampleItem = document.createElement('div');
fragment.appendChild(exampleItem);
var canvas = document.createElement('canvas');
exampleItem.appendChild(canvas);
canvas.width = w;
canvas.height = h;
context = canvas.getContext('2d');
context.lineWidth = 1;
context.strokeStyle = 'yellow';
CanvasTextWrapper(canvas,(options[i].txt),options[i]);
var hint = document.createElement('div');
exampleItem.appendChild(hint);
var optionsData = '';
for (var property in options[i]) {
if (options[i].hasOwnProperty(property)) {
if (property == 'txt') continue;
var stringWrapper = (property == 'paddingX' || property == 'paddingY' || property == 'sizeToFill' || property === 'justifyLines' || property === 'allowNewLine' || property === 'strokeText') ? '' : '"';
optionsData += ' <span>' + property + ':</span> ' +
stringWrapper + options[i][property] + stringWrapper + ',<br/>';
}
}
hint.innerHTML = '<h6>CODE:</h6><p>' +
'CanvasTextWrapper(canvas, str, {<br/>' + optionsData + '});' +
'</p>';
}
container.appendChild(fragment);
}
})();
}
};
-47
View File
@@ -1,47 +0,0 @@
var model = [
{
txt: 'Break text on every next word',
font: 'italic 800 25px Arial, sans-serif',
textAlign: 'center',
lineBreak: 'word',
lineHeight: "160%"
},
{
txt: 'Center text block horizontally and vertically',
font: 'bold 35px Arial, sans-serif',
textAlign: 'center',
verticalAlign: 'middle'
},
{
txt: 'Resize text automatically to fill its padded container. Given "font" and "lineHeight" properties are ignored',
textAlign: 'center',
verticalAlign: 'middle',
sizeToFill: true,
paddingX: 10,
paddingY: 30
},
{
txt: 'Justify text lines takes effect only with "auto" break line. Single word lines are skipped',
font: 'bold 40px Arial, sans-serif',
paddingX: 20,
paddingY: 20,
verticalAlign: 'middle',
textAlign: 'center',
justifyLines: true
},
{
txt: 'Apply text stroke is based on context settings',
font: 'bold 40px Arial, sans-serif',
textAlign: 'right',
paddingX: 25,
strokeText: true,
lineHeight: '60px'
},
{
txt: 'Use\n new line character "\\n"\n to break lines\nwhere needed',
font: 'bold 30px Arial, sans-serif',
verticalAlign: 'bottom',
allowNewLine: true,
lineHeight: '200%'
}
];
Executable
+20
View File
@@ -0,0 +1,20 @@
{
"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",
"license": "MIT",
"main": "CanvasTextWrapper.min.js",
"homepage": "http://namniak.github.io/CanvasTextWrapper/",
"repository": {
"type": "git",
"url": "https://github.com/namniak/CanvasTextWrapper"
},
"author": "Vadim Namniak <vnamnyak@gmail.com> (https://github.com/namniak)",
"bugs": {
"url": "https://github.com/namniak/CanvasTextWrapper/issues"
},
"devDependencies": {
"grunt-contrib-uglify": "^0.9.1",
"uglify-save-license": "^0.4.1"
}
}