mirror of
https://github.com/wassname/CanvasTextWrapper.git
synced 2026-08-11 11:11:42 +08:00
v 0.3.0
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
.idea/
|
||||||
+21
-7
@@ -13,7 +13,7 @@ div, header, footer, article {
|
|||||||
header, footer, article {
|
header, footer, article {
|
||||||
width: 930px;
|
width: 930px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
background-color: #0D9F69;
|
background-color: #3299af;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +55,15 @@ article {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
color: #0D9F69;
|
color: #2E6571;
|
||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre.white-block {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
@@ -66,6 +71,11 @@ p {
|
|||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
|
padding-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:first-of-type {
|
||||||
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h6 {
|
h6 {
|
||||||
@@ -83,6 +93,10 @@ h6 {
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.syntax p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.values li {
|
.values li {
|
||||||
text-indent: 20px;
|
text-indent: 20px;
|
||||||
list-style: inside circle;
|
list-style: inside circle;
|
||||||
@@ -98,20 +112,20 @@ section > div {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 450px;
|
width: 450px;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
border: 1px solid #0D9F69;
|
border: 1px solid #3299af;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
section h2 {
|
section h2 {
|
||||||
color: #0D9F69;
|
color: #3299af;
|
||||||
text-indent: 30px;
|
text-indent: 30px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
section > div div {
|
section > div div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200px;
|
height: 240px;
|
||||||
background-color: #0D9F69;
|
background-color: #3299af;
|
||||||
margin-top: 250px;
|
margin-top: 250px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
@@ -139,7 +153,7 @@ span, .emph {
|
|||||||
|
|
||||||
strong {
|
strong {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #0D9F69;
|
color: #3299af;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 58 KiB |
+57
-51
@@ -8,35 +8,44 @@
|
|||||||
<script src="js/CanvasTextWrapper.js"></script>
|
<script src="js/CanvasTextWrapper.js"></script>
|
||||||
<script src="js/options.js"></script>
|
<script src="js/options.js"></script>
|
||||||
<script src="js/examples.js"></script>
|
<script src="js/examples.js"></script>
|
||||||
<title>CanvasTextWrapper.js</title>
|
<title>CanvasTextWrapper</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<p>CanvasTextWrapper.js</p>
|
<p>CanvasTextWrapper</p>
|
||||||
|
|
||||||
<p class="description">
|
<p class="description">
|
||||||
JavaScript canvas text wrapper that automatically splits a string into lines on specified rule with
|
Pure JavaScript canvas text wrapper that automatically splits a string into lines on specified rule with
|
||||||
optional alignments and padding.
|
alignment and padding.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<article>
|
<article>
|
||||||
<h2>Syntax</h2><br/>
|
<h2>Syntax</h2><br/>
|
||||||
|
|
||||||
<p class="white-block">new CanvasTextWrapper(HTMLCanvasElement, String [, Options]);</p><br/>
|
<p class="white-block">CanvasTextWrapper(HTMLCanvasElement, String [, Options]);</p><br/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="emph">OPTIONS</div>
|
<div class="emph">Options</div>
|
||||||
- is a JavaScript object with the following available properties and values:
|
- is an object with the following available properties and values:
|
||||||
</div>
|
</div>
|
||||||
<ul class="syntax">
|
<ul class="syntax">
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">font</div>
|
<div class="emph">font</div>
|
||||||
(String) - text style that includes font size in px (REQUIRED), weight & family, etc. specified similarly to CSS
|
(String) - text style that includes font size in px, weight, font family, etc. Similar to CSS font shorthand
|
||||||
font shorthand property
|
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>
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">textAlign</div>
|
<div class="emph">textAlign</div>
|
||||||
(String) - horizontal alignment that applies for each line
|
(String) - horizontal alignment of each line
|
||||||
<ul class="values">
|
<ul class="values">
|
||||||
<li>"left"</li>
|
<li>"left"</li>
|
||||||
<li>"center"</li>
|
<li>"center"</li>
|
||||||
@@ -45,7 +54,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">verticalAlign</div>
|
<div class="emph">verticalAlign</div>
|
||||||
(String) - vertical alignment that applies on a whole block of text
|
(String) - vertical alignment of the whole block of text
|
||||||
<ul class="values">
|
<ul class="values">
|
||||||
<li>"top"</li>
|
<li>"top"</li>
|
||||||
<li>"middle"</li>
|
<li>"middle"</li>
|
||||||
@@ -54,12 +63,11 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">paddingX</div>
|
<div class="emph">paddingX</div>
|
||||||
(Number) - horizontal padding in pixels set equally on both, left and right sides of
|
(Number) - horizontal padding (in px) set equally on both, left and right sides of the container
|
||||||
the element
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">paddingY</div>
|
<div class="emph">paddingY</div>
|
||||||
(Number) - vertical padding in pixels set equally on both, top and bottom sides of the element
|
(Number) - vertical padding (in px) set equally on both, top and bottom sides of the container
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">fitParent</div>
|
<div class="emph">fitParent</div>
|
||||||
@@ -73,7 +81,7 @@
|
|||||||
<div class="emph">lineBreak</div>
|
<div class="emph">lineBreak</div>
|
||||||
(String) - text split rule
|
(String) - text split rule
|
||||||
<ul class="values">
|
<ul class="values">
|
||||||
<li>"auto" - text fills the element's width going to a new line on a whole word when no more space</li>
|
<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>
|
<li>"word" - each next word will be placed on a new line</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -81,61 +89,59 @@
|
|||||||
<div class="emph">sizeToFill</div>
|
<div class="emph">sizeToFill</div>
|
||||||
(Boolean) - auto font size to fill text container
|
(Boolean) - auto font size to fill text container
|
||||||
<ul class="values">
|
<ul class="values">
|
||||||
<li>"true" - ignore given font size and resize text to fill its padded container</li>
|
<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>
|
<li>false - use specified or default font size</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="emph">strokeText</div>
|
<div class="emph">strokeText</div>
|
||||||
(Boolean) - text outline based on context configuration (make sure it doesn't contradict with other context settings such as globalCompositeOperation, etc)
|
(Boolean) - outline text based on context configuration
|
||||||
<ul class="values">
|
</li>
|
||||||
<li>"true" - enable text outline</li>
|
<li>
|
||||||
<li>"false" - enable text outline</li>
|
<div class="emph">justifyLines</div>
|
||||||
</ul>
|
(Boolean) - flex spaces between words so all lines match the same width (one-word lines are skipped).
|
||||||
</li>
|
</li>
|
||||||
<p>
|
<p>
|
||||||
NOTE: if a single word is too long to fit the width with specified font size, it will be broken into as
|
NOTE: if a single word is too long to fit the width with specified font size, it will break on any letter
|
||||||
many lines as required on any letter of the word unless <strong>sizeToFill</strong> option is used.
|
unless "sizeToFill" option is enabled.
|
||||||
</p>
|
</p>
|
||||||
</ul><br/>
|
</ul>
|
||||||
|
|
||||||
<h2>Defaults</h2><br/>
|
|
||||||
|
|
||||||
<p>The default options object which values will be used if a property is not specified or no object is passed:</p>
|
|
||||||
|
|
||||||
|
<h2>Default Options</h2><br/>
|
||||||
<pre class="white-block">
|
<pre class="white-block">
|
||||||
{
|
{
|
||||||
font: "18px Arial, sans-serif",
|
<strong>font</strong>: "18px Arial, sans-serif",
|
||||||
textAlign: "left",
|
<strong>lineHeight</strong>: 1,
|
||||||
verticalAlign: "top",
|
<strong>textAlign</strong>: "left",
|
||||||
paddingX: 0,
|
<strong>verticalAlign</strong>: "top",
|
||||||
paddingY: 0,
|
<strong>paddingX</strong>: 0,
|
||||||
fitParent: false,
|
<strong>paddingY</strong>: 0,
|
||||||
lineBreak: "auto",
|
<strong>fitParent</strong>: false,
|
||||||
sizeToFill: false
|
<strong>lineBreak</strong>: "auto",
|
||||||
|
<strong>sizeToFill</strong>: false,
|
||||||
|
<strong>allowNewLine</strong>: true,
|
||||||
|
<strong>justifyLines</strong>: false
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
<br/><br/>
|
|
||||||
|
|
||||||
<h2>Usage</h2><br/>
|
<h2>Usage</h2><br/>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Use standard canvas text drawing methods such as "fillStyle" and "globalCompositeOperation" when needed before
|
Use context settings such as "fillStyle", "lineWidth" or "strokeStyle" before using CanvasTextWrapper like so:
|
||||||
using CanvasTextWrapper like so:
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="white-block">
|
<pre class="white-block">
|
||||||
var canvas = document.createElement('canvas');<br/>
|
var canvas = document.getElementById("#canvasText");
|
||||||
canvas.width = 300;<br/>
|
canvas.width = 200;
|
||||||
canvas.height = 250;<br/>
|
canvas.height = 200;
|
||||||
context = canvas.getContext("2d");<br/>
|
context = canvas.getContext("2d");
|
||||||
context.fillStyle = "rgb(255, 255, 255)";<br/>
|
context.lineWidth = 2;
|
||||||
context.fillRect(0, 0, canvas.width, canvas.height);<br/><br/>
|
context.strokeStyle = "#ff0000";
|
||||||
context.globalCompositeOperation = "destination-out";<br/>
|
CanvasTextWrapper(canvas,"Hello"); //default options will apply
|
||||||
var wrapper = new CanvasTextWrapper(canvas, 'Hello'); // default options will apply<br/>
|
</pre>
|
||||||
</p>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<h2>Installation</h2><br/>
|
<h2>Installation</h2><br/>
|
||||||
|
|
||||||
<p class="white-block">
|
<p class="white-block">
|
||||||
bower install canvas-text-wrapper<br/>
|
bower install canvas-text-wrapper<br/>
|
||||||
npm install canvas-text-wrapper
|
npm install canvas-text-wrapper
|
||||||
|
|||||||
+246
-169
@@ -1,206 +1,283 @@
|
|||||||
/*! CanvasTextWrapper (https://github.com/namniak/CanvasTextWrapper)
|
/*! CanvasTextWrapper
|
||||||
* Version: 0.2.0
|
* https://github.com/namniak/CanvasTextWrapper
|
||||||
*
|
* Version: 0.3.0
|
||||||
* MIT License (http://www.opensource.org/licenses/mit-license.html)
|
* MIT License (http://www.opensource.org/licenses/mit-license.html)
|
||||||
* Copyright (c) 2014 Vadim Namniak
|
* Copyright (c) 2014 Vadim Namniak
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var defaultOptions = {
|
var EL_WIDTH,EL_HEIGHT,MAX_TXT_WIDTH,MAX_TXT_HEIGHT;
|
||||||
font: '18px Arial, sans-serif',
|
|
||||||
textAlign: 'left', // each line of text is aligned left
|
|
||||||
verticalAlign: 'top', // text lines block is aligned top
|
|
||||||
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
|
|
||||||
sizeToFill: false, // text is resized to fill the container (given font size is ignored)
|
|
||||||
strokeText: false // text is stroked according to context configuration.
|
|
||||||
};
|
|
||||||
|
|
||||||
window.CanvasTextWrapper = function(canvas, text, opts) {
|
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
|
||||||
|
};
|
||||||
|
|
||||||
if (!(this instanceof CanvasTextWrapper)) {
|
var CanvasTextWrapper = function(canvas,text,options) {
|
||||||
throw new TypeError('CanvasTextWrapper constructor failed. Use "new" keyword when instantiating.');
|
if (!(this instanceof CanvasTextWrapper)) {
|
||||||
}
|
return new CanvasTextWrapper(canvas,text,options);
|
||||||
|
}
|
||||||
|
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
this.context = this.canvas.getContext('2d');
|
||||||
|
this.context.font = this.font;
|
||||||
|
this.context.textBaseline = 'bottom';
|
||||||
|
|
||||||
// set options to specified or default values
|
for (var property in defaults) {
|
||||||
for (var property in defaultOptions) {
|
if (defaults.hasOwnProperty(property)) {
|
||||||
this[property] = (opts && opts[property]) ? opts[property] : defaultOptions[property];
|
this[property] = (options && options[property]) ? options[property] : defaults[property];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// extract font size
|
EL_WIDTH = (this.fitParent === false) ? this.canvas.width : this.canvas.parentNode.clientWidth;
|
||||||
this.lineHeight = parseInt(this.font.replace(/^\D+/g, ''), 10) || 18;
|
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);
|
||||||
|
|
||||||
// validate all set properties
|
this._init();
|
||||||
this.validate();
|
};
|
||||||
|
|
||||||
// basic context settings
|
CanvasTextWrapper.prototype = {
|
||||||
this.context = this.canvas.getContext('2d');
|
_init: function() {
|
||||||
this.context.font = this.font;
|
this.fontSize = parseInt(this.font.replace(/^\D+/g,''),10) || 18;
|
||||||
this.context.textBaseline = 'bottom';
|
this.textBlockHeight = 0;
|
||||||
|
this.lines = [];
|
||||||
|
this.newLineIndexes = [];
|
||||||
|
this.textPos = {x: 0,y: 0};
|
||||||
|
|
||||||
this.drawText();
|
this._setFont(this.fontSize);
|
||||||
};
|
this._setLineHeight();
|
||||||
|
this._validate();
|
||||||
|
this._render();
|
||||||
|
},
|
||||||
|
|
||||||
CanvasTextWrapper.prototype = {
|
_render: function() {
|
||||||
|
if (this.sizeToFill) {
|
||||||
|
var numWords = this.text.trim().split(/\s+/).length;
|
||||||
|
var fontSize = 0;
|
||||||
|
|
||||||
drawText: function() {
|
do {
|
||||||
var elementWidth = (this.fitParent === false) ? this.canvas.width : this.canvas.parentNode.clientWidth;
|
this._setFont(++fontSize);
|
||||||
var textPos = {
|
this.lineHeight = this.fontSize;
|
||||||
x: 0,
|
this._wrap();
|
||||||
y: 0
|
} while (this.textBlockHeight < MAX_TXT_HEIGHT && (this.lines.join(' ').split(/\s+/).length == numWords));
|
||||||
};
|
|
||||||
|
|
||||||
if (this.sizeToFill) {
|
this._setFont(--fontSize);
|
||||||
// starting at 1px increase font size by 1px until text block exceeds the height of its padded container or until words break
|
this.lineHeight = this.fontSize;
|
||||||
var elementHeight = ((this.fitParent === false) ? this.canvas.height : this.canvas.parentNode.clientHeight) - (this.paddingX * 2);
|
} else {
|
||||||
var numWords = this.text.trim().split(/\s+/).length;
|
this._wrap();
|
||||||
var fontSize = 0;
|
}
|
||||||
do {
|
|
||||||
this.setFontSize(++fontSize);
|
|
||||||
var lines = this.getWrappedText(elementWidth);
|
|
||||||
var textBlockHeight = lines.length * this.lineHeight;
|
|
||||||
} while (textBlockHeight < elementHeight && lines.join(' ').split(/\s+/).length == numWords);
|
|
||||||
|
|
||||||
// use previous font size, not the one that broke the while condition
|
if (this.justifyLines && this.lineBreak === 'auto') {
|
||||||
this.setFontSize(--fontSize);
|
this._justify();
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = this.getWrappedText(elementWidth);
|
this._setAlignY();
|
||||||
var textBlockHeight = lines.length * this.lineHeight;
|
this._drawText();
|
||||||
|
},
|
||||||
|
|
||||||
// set vertical align for the whole text block
|
_setFont: function(fontSize) {
|
||||||
this.setTextVerticalAlign(textPos, textBlockHeight);
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
for (var i = 0; i < lines.length; i++) {
|
_setLineHeight: function() {
|
||||||
this.setTextHorizontalAlign(this.context, textPos, elementWidth, lines[i]);
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
textPos.y = parseInt(textPos.y) + parseInt(this.lineHeight);
|
_wrap: function() {
|
||||||
this.context.fillText(lines[i], textPos.x, textPos.y);
|
if (this.allowNewLine) {
|
||||||
if (this.strokeText) {
|
var newLines = this.text.trim().split('\n');
|
||||||
this.context.strokeText(lines[i], textPos.x, textPos.y);
|
for (var i = 0,idx = 0; i < newLines.length - 1; i++) {
|
||||||
}
|
idx += newLines[i].trim().split(/\s+/).length;
|
||||||
}
|
this.newLineIndexes.push(idx)
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setFontSize: function(size) {
|
var words = this.text.trim().split(/\s+/);
|
||||||
var fontParts = this.context.font.split(/\b\d+px\b/i);
|
this._checkLength(words);
|
||||||
this.context.font = fontParts[0] + size + 'px' + fontParts[1];
|
this._breakText(words);
|
||||||
this.lineHeight = size;
|
|
||||||
},
|
|
||||||
|
|
||||||
getWrappedText: function(elementWidth) {
|
this.textBlockHeight = this.lines.length * this.lineHeight;
|
||||||
var maxTextLength = elementWidth - (this.paddingX * 2);
|
},
|
||||||
|
|
||||||
var words = this.text.trim().split(/\s+/);
|
_checkLength: function(words) {
|
||||||
var lines = [];
|
var testString,tokenLen,sliced,leftover;
|
||||||
|
|
||||||
this.checkWordsLength(this.context, words, maxTextLength);
|
for (var i = 0; i < words.length; i++) {
|
||||||
this.breakTextIntoLines(this.context, lines, words, maxTextLength);
|
testString = '';
|
||||||
|
tokenLen = this.context.measureText(words[i]).width;
|
||||||
|
|
||||||
return lines;
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
checkWordsLength: function(context, words, maxTextLength) {
|
sliced = words[i].slice(0,k);
|
||||||
for (var i = 0; i < words.length; i++) {
|
leftover = words[i].slice(k);
|
||||||
var testString = '';
|
words.splice(i,1,sliced,leftover);
|
||||||
var tokenLen = context.measureText(words[i]).width;
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// check if a word exceeds the element's width
|
_breakText: function(words) {
|
||||||
if (tokenLen > maxTextLength) {
|
for (var i = 0,j = 0; i < words.length; j++) {
|
||||||
for (var k = 0; (context.measureText(testString + words[i][k]).width <= maxTextLength) && (k < words[i].length); k++) {
|
this.lines[j] = '';
|
||||||
testString += words[i][k];
|
|
||||||
}
|
|
||||||
|
|
||||||
// break the word because it's too long
|
if (this.lineBreak === 'auto') {
|
||||||
var sliced = words[i].slice(0, k);
|
while ((this.context.measureText(this.lines[j] + words[i]).width <= MAX_TXT_WIDTH) && (i < words.length)) {
|
||||||
var leftover = words[i].slice(k);
|
|
||||||
words.splice(i, 1, sliced, leftover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
breakTextIntoLines: function(context, lines, words, maxTextLength) {
|
this.lines[j] += words[i] + ' ';
|
||||||
for (var i = 0, j = 0; i < words.length; j++) {
|
i++;
|
||||||
lines[j] = '';
|
|
||||||
|
|
||||||
if (this.lineBreak === 'auto') {
|
if (this.allowNewLine) {
|
||||||
// put as many full words in a line as can fit element
|
for (var k = 0; k < this.newLineIndexes.length; k++) {
|
||||||
while ((context.measureText(lines[j] + words[i]).width <= maxTextLength) && (i < words.length)) {
|
if (this.newLineIndexes[k] === i) {
|
||||||
lines[j] += words[i] + ' ';
|
j++;
|
||||||
i++;
|
this.lines[j] = '';
|
||||||
}
|
break;
|
||||||
lines[j] = lines[j].trim();
|
}
|
||||||
} else if (this.lineBreak === 'word') {
|
}
|
||||||
// put each next word in a new line
|
}
|
||||||
lines[j] = words[i];
|
}
|
||||||
i++;
|
this.lines[j] = this.lines[j].trim();
|
||||||
}
|
} else {
|
||||||
}
|
this.lines[j] = words[i];
|
||||||
},
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setTextHorizontalAlign: function(context, textPos, elementWidth, line) {
|
_justify: function() {
|
||||||
if (this.textAlign === 'center') {
|
var maxLen,longestLineIndex,tokenLen;
|
||||||
textPos.x = (elementWidth - context.measureText(line).width) / 2;
|
for (var i = 0; i < this.lines.length; i++) {
|
||||||
} else if (this.textAlign === 'right') {
|
tokenLen = this.context.measureText(this.lines[i]).width;
|
||||||
textPos.x = elementWidth - context.measureText(line).width - this.paddingX;
|
|
||||||
} else {
|
|
||||||
textPos.x = this.paddingX;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setTextVerticalAlign: function(textPos, textBlockHeight) {
|
if (!maxLen || tokenLen > maxLen) {
|
||||||
var elementHeight = (this.fitParent === false) ? this.canvas.height : this.canvas.parentNode.clientHeight;
|
maxLen = tokenLen;
|
||||||
|
longestLineIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.verticalAlign === 'middle') {
|
// fill lines with extra spaces
|
||||||
textPos.y = (elementHeight - textBlockHeight) / 2;
|
var numWords,spaceLength,numOfSpaces,num,filler;
|
||||||
} else if (this.verticalAlign === 'bottom') {
|
var delimiter = '\u200A';
|
||||||
textPos.y = elementHeight - textBlockHeight - this.paddingY;
|
for (i = 0; i < this.lines.length; i++) {
|
||||||
} else {
|
if (i === longestLineIndex) continue;
|
||||||
textPos.y = this.paddingY;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validate: function() {
|
numWords = this.lines[i].trim().split(/\s+/).length;
|
||||||
if (!(this.canvas instanceof HTMLCanvasElement)) {
|
if (numWords <= 1) continue;
|
||||||
throw new TypeError('From CanvasTextWrapper(): Element passed as the first parameter is not an instance of HTMLCanvasElement.');
|
|
||||||
}
|
this.lines[i] = this.lines[i].trim().split(/\s+/).join(delimiter);
|
||||||
if (typeof this.text !== 'string') {
|
|
||||||
throw new TypeError('From CanvasTextWrapper(): The second, dedicated for the text, parameter must be a string.');
|
spaceLength = this.context.measureText(delimiter).width;
|
||||||
}
|
numOfSpaces = (maxLen - this.context.measureText(this.lines[i]).width) / spaceLength;
|
||||||
if (isNaN(this.lineHeight)) {
|
num = numOfSpaces / (numWords - 1);
|
||||||
throw new TypeError('From CanvasTextWrapper(): Cannot parse font size as an Integer. Check "font" property\'s value.');
|
|
||||||
}
|
filler = '';
|
||||||
if (this.textAlign !== 'left' && this.textAlign !== 'center' && this.textAlign !== 'right') {
|
for (var j = 0; j < num; j++) {
|
||||||
throw new TypeError('From CanvasTextWrapper(): Unsupported horizontal align value is used. Property "textAlign" can only be set to "left", "center", or "right".');
|
filler += delimiter;
|
||||||
}
|
}
|
||||||
if (this.verticalAlign !== 'top' && this.verticalAlign !== 'middle' && this.verticalAlign !== 'bottom') {
|
|
||||||
throw new TypeError('From CanvasTextWrapper(): Unsupported vertical align value is used. Property "verticalAlign" can only be set to "top", "middle", or "bottom".');
|
this.lines[i] = this.lines[i].trim().split(delimiter).join(filler);
|
||||||
}
|
//console.log('numWords:', numWords, 'numOfSpaces:', numOfSpaces, 'num:', num);
|
||||||
if (isNaN(this.paddingX)) {
|
}
|
||||||
throw new TypeError('From CanvasTextWrapper(): Unsupported horizontal padding value is used. Property "paddingX" must be set to a number');
|
},
|
||||||
}
|
|
||||||
if (isNaN(this.paddingY)) {
|
_drawText: function() {
|
||||||
throw new TypeError('From CanvasTextWrapper(): Unsupported vertical padding value is used. Property "paddingY" must be set to a number.');
|
for (var i = 0; i < this.lines.length; i++) {
|
||||||
}
|
this._setAlignX(this.lines[i]);
|
||||||
if (typeof this.fitParent !== 'boolean') {
|
|
||||||
throw new TypeError('From CanvasTextWrapper(): Property "fitParent" must be set to a Boolean.');
|
this.textPos.y = parseInt(this.textPos.y) + this.lineHeight;
|
||||||
}
|
this.context.fillText(this.lines[i],this.textPos.x,this.textPos.y);
|
||||||
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 (this.strokeText) {
|
||||||
}
|
this.context.strokeText(this.lines[i],this.textPos.x,this.textPos.y);
|
||||||
if (typeof this.sizeToFill !== 'boolean') {
|
}
|
||||||
throw new TypeError('From CanvasTextWrapper(): Property "sizeToFill" must be set to a Boolean.');
|
}
|
||||||
}
|
},
|
||||||
if (typeof this.strokeText !== 'boolean') {
|
|
||||||
throw new TypeError('From CanvasTextWrapper(): Property "strokeText" must be set to a Boolean.');
|
_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;
|
||||||
})();
|
})();
|
||||||
+41
-75
@@ -1,85 +1,51 @@
|
|||||||
document.onreadystatechange = function() {
|
document.onreadystatechange = function() {
|
||||||
if (document.readyState === 'complete') {
|
'use strict';
|
||||||
|
if (document.readyState === 'complete') {
|
||||||
|
(function() {
|
||||||
|
var container = document.getElementsByTagName('section')[0];
|
||||||
|
var w = 448;
|
||||||
|
var h = 250;
|
||||||
|
var options = optionsArr;
|
||||||
|
|
||||||
(function() {
|
createExamples();
|
||||||
var container = document.getElementsByTagName('section')[0];
|
|
||||||
var w = 448;
|
|
||||||
var h = 250;
|
|
||||||
var aspectRatio = 0;
|
|
||||||
var text = 'Canvas text wrapping example';
|
|
||||||
|
|
||||||
var img = new Image();
|
function createExamples() {
|
||||||
img.src = 'img/bg.jpg';
|
var fragment = new DocumentFragment();
|
||||||
img.onload = function() {
|
var context;
|
||||||
aspectRatio = img.naturalWidth / img.naturalHeight;
|
|
||||||
createExamples();
|
|
||||||
};
|
|
||||||
|
|
||||||
// use options.js file
|
for (var i = 0; i < options.length; i++) {
|
||||||
var options = optionsArr;
|
var exampleItem = document.createElement('div');
|
||||||
|
fragment.appendChild(exampleItem);
|
||||||
|
|
||||||
function createExamples() {
|
var canvas = document.createElement('canvas');
|
||||||
var fragment = new DocumentFragment();
|
exampleItem.appendChild(canvas);
|
||||||
var context;
|
canvas.width = w;
|
||||||
|
canvas.height = h;
|
||||||
|
context = canvas.getContext('2d');
|
||||||
|
context.lineWidth = 2;
|
||||||
|
context.strokeStyle = 'red';
|
||||||
|
CanvasTextWrapper(canvas,(options[i].txt),options[i]);
|
||||||
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
var hint = document.createElement('div');
|
||||||
var exampleItem = document.createElement('div');
|
exampleItem.appendChild(hint);
|
||||||
fragment.appendChild(exampleItem);
|
var optionsData = '';
|
||||||
|
|
||||||
// draw canvas image
|
for (var property in options[i]) {
|
||||||
var canvasImg = document.createElement('canvas');
|
if (options[i].hasOwnProperty(property)) {
|
||||||
canvasImg.width = w;
|
if (property == 'txt') continue;
|
||||||
canvasImg.height = h;
|
var stringWrapper = (property == 'paddingX' || property == 'paddingY' || property == 'sizeToFill' || property === 'justifyLines' || property === 'allowNewLine' || property === 'strokeText') ? '' : '"';
|
||||||
context = canvasImg.getContext('2d');
|
optionsData += ' <span>' + property + ':</span> ' +
|
||||||
context.drawImage(img, 0, 0, w, w * aspectRatio);
|
stringWrapper + options[i][property] + stringWrapper + ',<br/>';
|
||||||
exampleItem.appendChild(canvasImg);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create canvas mask layer
|
hint.innerHTML = '<h6>CODE:</h6><p>' +
|
||||||
var canvasMask = document.createElement('canvas');
|
'CanvasTextWrapper(canvas, str, {<br/>' + optionsData + '});' +
|
||||||
canvasMask.width = w;
|
'</p>';
|
||||||
canvasMask.height = h;
|
}
|
||||||
context = canvasMask.getContext('2d');
|
|
||||||
context.fillStyle = 'rgba(255,255,255, 1)';
|
|
||||||
context.fillRect(0, 0, w, h);
|
|
||||||
exampleItem.appendChild(canvasMask);
|
|
||||||
|
|
||||||
if (i < options.length -1) {
|
container.appendChild(fragment);
|
||||||
// create text to be cut out mask layer
|
}
|
||||||
context.fillStyle = '#212121';
|
})();
|
||||||
context.globalCompositeOperation = 'destination-out';
|
}
|
||||||
} else {
|
|
||||||
// make stroke gradient
|
|
||||||
var gradient=context.createLinearGradient(0,0,canvasImg.width,0);
|
|
||||||
gradient.addColorStop("0","#ffff00");
|
|
||||||
gradient.addColorStop("1.0","red");
|
|
||||||
context.strokeStyle=gradient;
|
|
||||||
context.lineWidth = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create wrapper
|
|
||||||
new CanvasTextWrapper(canvasMask, ('#' + (i + 1) + ' ' + text), options[i]);
|
|
||||||
|
|
||||||
// create hint code block
|
|
||||||
var hint = document.createElement('div');
|
|
||||||
exampleItem.appendChild(hint);
|
|
||||||
var optionsData = '';
|
|
||||||
|
|
||||||
// read used properties
|
|
||||||
for (var property in options[i]) {
|
|
||||||
var stringWrapper = (property == 'paddingX' || property == 'paddingY' || property == 'sizeToFill') ? '' : '"';
|
|
||||||
optionsData += ' <span>' + property + ':</span> ' +
|
|
||||||
stringWrapper + options[i][property] + stringWrapper + ',<br/>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// print example code
|
|
||||||
hint.innerHTML = '<h6>CODE:</h6><p>' +
|
|
||||||
'new CanvasTextWrapper(canvasEl, textStr, {<br/>' + optionsData + '});' +
|
|
||||||
'</p>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// inject document fragment into actual DOM
|
|
||||||
container.appendChild(fragment);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
+47
-36
@@ -1,36 +1,47 @@
|
|||||||
(function() {
|
var optionsArr = [
|
||||||
window.optionsArr = [
|
{
|
||||||
{
|
txt: 'Break text on every next word',
|
||||||
font: 'bold 55px Open Sans, sans-serif'
|
font: 'bold 25px Arial, sans-serif',
|
||||||
},
|
textAlign: 'center',
|
||||||
{
|
lineBreak: 'word',
|
||||||
font: 'normal 40px Impact, Charcoal, sans-serif',
|
lineHeight: 1.6
|
||||||
textAlign: 'center',
|
},
|
||||||
paddingY: 10,
|
{
|
||||||
lineBreak: 'word'
|
txt: 'Center text block horizontally and vertically',
|
||||||
},
|
font: 'bold 35px Verdana, Geneva, sans-serif',
|
||||||
{
|
textAlign: 'center',
|
||||||
font: 'bold 55px Open Sans, sans-serif',
|
verticalAlign: 'middle'
|
||||||
textAlign: 'right',
|
},
|
||||||
verticalAlign: 'bottom',
|
{
|
||||||
paddingX: 30
|
txt: 'Resize text automatically to fill its padded container. Given "font" and "lineHeight" properties are ignored',
|
||||||
},
|
textAlign: 'center',
|
||||||
{
|
verticalAlign: 'middle',
|
||||||
font: 'bold 35px Verdana, Geneva, sans-serif',
|
sizeToFill: true,
|
||||||
textAlign: 'center',
|
paddingX: 10,
|
||||||
verticalAlign: 'middle'
|
paddingY: 30
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
textAlign: 'center',
|
txt: 'Justify text lines takes effect only with "auto" break line. One-word lines are skipped',
|
||||||
verticalAlign: 'middle',
|
font: 'bold 40px Arial, Geneva, sans-serif',
|
||||||
sizeToFill: true
|
paddingX: 20,
|
||||||
},
|
paddingY: 20,
|
||||||
{
|
verticalAlign: 'middle',
|
||||||
font: 'bold 50px Tahoma, Geneva, sans-serif',
|
textAlign: 'center',
|
||||||
textAlign: 'right',
|
justifyLines: true
|
||||||
paddingX: 25,
|
},
|
||||||
paddingY: 25,
|
{
|
||||||
strokeText: true
|
txt: 'Apply text stroke is based on context settings',
|
||||||
},
|
font: 'bold 40px Tahoma, Geneva, 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 Tahoma, Geneva, sans-serif',
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
allowNewLine: true,
|
||||||
|
lineHeight: '200%'
|
||||||
|
}
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user