mirror of
https://github.com/photonstorm/phaser
synced 2024-11-23 21:24:09 +00:00
Merge branch 'master' of https://github.com/photonstorm/phaser
This commit is contained in:
commit
4320524119
4 changed files with 346 additions and 12 deletions
|
@ -24,7 +24,11 @@ var propertyMap = {
|
||||||
fixedWidth: [ 'fixedWidth', false ],
|
fixedWidth: [ 'fixedWidth', false ],
|
||||||
fixedHeight: [ 'fixedHeight', false ],
|
fixedHeight: [ 'fixedHeight', false ],
|
||||||
rtl: [ 'rtl', false ],
|
rtl: [ 'rtl', false ],
|
||||||
testString: [ 'testString', '|MÉqgy' ]
|
testString: [ 'testString', '|MÉqgy' ],
|
||||||
|
wordWrapWidth: [ 'wordWrap.width', null ],
|
||||||
|
wordWrapCallback: [ 'wordWrap.callback', null ],
|
||||||
|
wordWrapCallbackScope: [ 'wordWrap.callbackScope', null ],
|
||||||
|
wordWrapUseAdvanced: [ 'wordWrap.useAdvancedWrap', false ]
|
||||||
};
|
};
|
||||||
|
|
||||||
var TextStyle = new Class({
|
var TextStyle = new Class({
|
||||||
|
@ -90,7 +94,15 @@ var TextStyle = new Class({
|
||||||
|
|
||||||
for (var key in propertyMap)
|
for (var key in propertyMap)
|
||||||
{
|
{
|
||||||
this[key] = GetAdvancedValue(style, propertyMap[key][0], propertyMap[key][1]);
|
if (key === 'wordWrapCallback' || key === 'wordWrapCallbackScope')
|
||||||
|
{
|
||||||
|
// Callback & scope should be set without processing the values
|
||||||
|
this[key] = GetValue(style, propertyMap[key][0], propertyMap[key][1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this[key] = GetAdvancedValue(style, propertyMap[key][0], propertyMap[key][1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow for 'font' override
|
// Allow for 'font' override
|
||||||
|
@ -124,6 +136,10 @@ var TextStyle = new Class({
|
||||||
syncFont: function (canvas, context)
|
syncFont: function (canvas, context)
|
||||||
{
|
{
|
||||||
context.font = this._font;
|
context.font = this._font;
|
||||||
|
},
|
||||||
|
|
||||||
|
syncStyle: function (canvas, context)
|
||||||
|
{
|
||||||
context.textBaseline = 'alphabetic';
|
context.textBaseline = 'alphabetic';
|
||||||
|
|
||||||
context.fillStyle = this.color;
|
context.fillStyle = this.color;
|
||||||
|
@ -335,6 +351,47 @@ var TextStyle = new Class({
|
||||||
return this.update(false);
|
return this.update(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the width (in pixels) to use for wrapping lines. Pass in null to remove wrapping by
|
||||||
|
* width.
|
||||||
|
*
|
||||||
|
* @param {number|null} width - The maximum width of a line in pixels. Set to null to remove
|
||||||
|
* wrapping.
|
||||||
|
* @param {boolean} [useAdvancedWrap=false] - Whether or not to use the advanced wrapping
|
||||||
|
* algorithm. If true, spaces are collapsed and whitespace is trimmed from lines. If false,
|
||||||
|
* spaces and whitespace are left as is.
|
||||||
|
* @return {this}
|
||||||
|
*/
|
||||||
|
setWordWrapWidth: function (width, useAdvancedWrap)
|
||||||
|
{
|
||||||
|
if (useAdvancedWrap === undefined) { useAdvancedWrap = false; }
|
||||||
|
|
||||||
|
this.wordWrapWidth = width;
|
||||||
|
this.wordWrapUseAdvanced = useAdvancedWrap;
|
||||||
|
|
||||||
|
return this.update(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a custom callback for wrapping lines. Pass in null to remove wrapping by callback.
|
||||||
|
*
|
||||||
|
* @param {function} callback - A custom function that will be responsible for wrapping the
|
||||||
|
* text. It will receive two arguments: text (the string to wrap), textObject (this Text
|
||||||
|
* instance). It should return the wrapped lines either as an array of lines or as a string with
|
||||||
|
* newline characters in place to indicate where breaks should happen.
|
||||||
|
* @param {object} [scope=null] - The scope that will be applied when the callback is invoked.
|
||||||
|
* @return {this}
|
||||||
|
*/
|
||||||
|
setWordWrapCallback: function (callback, scope)
|
||||||
|
{
|
||||||
|
if (scope === undefined) { scope = null; }
|
||||||
|
|
||||||
|
this.wordWrapCallback = callback;
|
||||||
|
this.wordWrapCallbackScope = scope;
|
||||||
|
|
||||||
|
return this.update(false);
|
||||||
|
},
|
||||||
|
|
||||||
setAlign: function (align)
|
setAlign: function (align)
|
||||||
{
|
{
|
||||||
if (align === undefined) { align = 'left'; }
|
if (align === undefined) { align = 'left'; }
|
||||||
|
|
|
@ -124,6 +124,228 @@ var Text = new Class({
|
||||||
this.originX = 1;
|
this.originX = 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Greedy wrapping algorithm that will wrap words as the line grows longer than its horizontal
|
||||||
|
* bounds.
|
||||||
|
*
|
||||||
|
* @param {string} text - The text to perform word wrap detection against.
|
||||||
|
* @return {string} The text after wrapping has been applied.
|
||||||
|
*/
|
||||||
|
runWordWrap: function (text)
|
||||||
|
{
|
||||||
|
var style = this.style;
|
||||||
|
if (style.wordWrapCallback)
|
||||||
|
{
|
||||||
|
var wrappedLines = style.wordWrapCallback.call(style.wordWrapCallbackScope, text, this);
|
||||||
|
if (Array.isArray(wrappedLines))
|
||||||
|
{
|
||||||
|
wrappedLines = wrappedLines.join('\n');
|
||||||
|
}
|
||||||
|
return wrappedLines;
|
||||||
|
}
|
||||||
|
else if (style.wordWrapWidth)
|
||||||
|
{
|
||||||
|
if (style.wordWrapUseAdvanced)
|
||||||
|
{
|
||||||
|
return this.advancedWordWrap(text, this.context, this.style.wordWrapWidth);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return this.basicWordWrap(text, this.context, this.style.wordWrapWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advanced wrapping algorithm that will wrap words as the line grows longer than its horizontal
|
||||||
|
* bounds. Consecutive spaces will be collapsed and replaced with a single space. Lines will be
|
||||||
|
* trimmed of white space before processing. Throws an error if wordWrapWidth is less than a
|
||||||
|
* single character.
|
||||||
|
*
|
||||||
|
* @param {string} text - The text to perform word wrap detection against.
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} wordWrapWidth
|
||||||
|
* @return {string} The wrapped text.
|
||||||
|
*/
|
||||||
|
advancedWordWrap: function (text, context, wordWrapWidth)
|
||||||
|
{
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
// Condense consecutive spaces and split into lines
|
||||||
|
var lines = text
|
||||||
|
.replace(/ +/gi, ' ')
|
||||||
|
.split(this.splitRegExp);
|
||||||
|
|
||||||
|
var linesCount = lines.length;
|
||||||
|
|
||||||
|
for (var i = 0; i < linesCount; i++)
|
||||||
|
{
|
||||||
|
var line = lines[i];
|
||||||
|
var out = '';
|
||||||
|
|
||||||
|
// Trim whitespace
|
||||||
|
line = line.replace(/^ *|\s*$/gi, '');
|
||||||
|
|
||||||
|
// If entire line is less than wordWrapWidth append the entire line and exit early
|
||||||
|
var lineWidth = context.measureText(line).width;
|
||||||
|
|
||||||
|
if (lineWidth < wordWrapWidth)
|
||||||
|
{
|
||||||
|
output += line + '\n';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, calculate new lines
|
||||||
|
var currentLineWidth = wordWrapWidth;
|
||||||
|
|
||||||
|
// Split into words
|
||||||
|
var words = line.split(' ');
|
||||||
|
|
||||||
|
for (var j = 0; j < words.length; j++)
|
||||||
|
{
|
||||||
|
var word = words[j];
|
||||||
|
var wordWithSpace = word + ' ';
|
||||||
|
var wordWidth = context.measureText(wordWithSpace).width;
|
||||||
|
|
||||||
|
if (wordWidth > currentLineWidth)
|
||||||
|
{
|
||||||
|
// Break word
|
||||||
|
if (j === 0)
|
||||||
|
{
|
||||||
|
// Shave off letters from word until it's small enough
|
||||||
|
var newWord = wordWithSpace;
|
||||||
|
|
||||||
|
while (newWord.length)
|
||||||
|
{
|
||||||
|
newWord = newWord.slice(0, -1);
|
||||||
|
wordWidth = context.measureText(newWord).width;
|
||||||
|
|
||||||
|
if (wordWidth <= currentLineWidth)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If wordWrapWidth is too small for even a single letter, shame user
|
||||||
|
// failure with a fatal error
|
||||||
|
if (!newWord.length)
|
||||||
|
{
|
||||||
|
throw new Error('This text\'s wordWrapWidth setting is less than a single character!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace current word in array with remainder
|
||||||
|
var secondPart = word.substr(newWord.length);
|
||||||
|
|
||||||
|
words[j] = secondPart;
|
||||||
|
|
||||||
|
// Append first piece to output
|
||||||
|
out += newWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If existing word length is 0, don't include it
|
||||||
|
var offset = (words[j].length) ? j : j + 1;
|
||||||
|
|
||||||
|
// Collapse rest of sentence and remove any trailing white space
|
||||||
|
var remainder = words.slice(offset).join(' ')
|
||||||
|
.replace(/[ \n]*$/gi, '');
|
||||||
|
|
||||||
|
// Prepend remainder to next line
|
||||||
|
lines[i + 1] = remainder + ' ' + (lines[i + 1] || '');
|
||||||
|
linesCount = lines.length;
|
||||||
|
|
||||||
|
break; // Processing on this line
|
||||||
|
|
||||||
|
// Append word with space to output
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out += wordWithSpace;
|
||||||
|
currentLineWidth -= wordWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append processed line to output
|
||||||
|
output += out.replace(/[ \n]*$/gi, '') + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim the end of the string
|
||||||
|
output = output.replace(/[\s|\n]*$/gi, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Greedy wrapping algorithm that will wrap words as the line grows longer than its horizontal
|
||||||
|
* bounds. Spaces are not collapsed and whitespace is not trimmed.
|
||||||
|
*
|
||||||
|
* @param {string} text - The text to perform word wrap detection against.
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} wordWrapWidth
|
||||||
|
* @return {string} The wrapped text.
|
||||||
|
*/
|
||||||
|
basicWordWrap: function (text, context, wordWrapWidth)
|
||||||
|
{
|
||||||
|
var result = '';
|
||||||
|
var lines = text.split(this.splitRegExp);
|
||||||
|
|
||||||
|
for (var i = 0; i < lines.length; i++)
|
||||||
|
{
|
||||||
|
var spaceLeft = wordWrapWidth;
|
||||||
|
var words = lines[i].split(' ');
|
||||||
|
|
||||||
|
for (var j = 0; j < words.length; j++)
|
||||||
|
{
|
||||||
|
var wordWidth = context.measureText(words[j]).width;
|
||||||
|
var wordWidthWithSpace = wordWidth + context.measureText(' ').width;
|
||||||
|
|
||||||
|
if (wordWidthWithSpace > spaceLeft)
|
||||||
|
{
|
||||||
|
// Skip printing the newline if it's the first word of the line that is greater
|
||||||
|
// than the word wrap width.
|
||||||
|
if (j > 0)
|
||||||
|
{
|
||||||
|
result += '\n';
|
||||||
|
}
|
||||||
|
result += words[j] + ' ';
|
||||||
|
spaceLeft = wordWrapWidth - wordWidth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spaceLeft -= wordWidthWithSpace;
|
||||||
|
result += words[j] + ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < lines.length - 1)
|
||||||
|
{
|
||||||
|
result += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given text through this Text object's word wrapping and returns the results as an
|
||||||
|
* array, where each element of the array corresponds to a wrapped line of text.
|
||||||
|
*
|
||||||
|
* @param {string} [text] - The text for which the wrapping will be calculated. If unspecified,
|
||||||
|
* the Text object's current text will be used.
|
||||||
|
* @return {array} An array of strings with the pieces of wrapped text.
|
||||||
|
*/
|
||||||
|
getWrappedText: function (text)
|
||||||
|
{
|
||||||
|
if (text === undefined) { text = this.text; }
|
||||||
|
|
||||||
|
var wrappedLines = this.runWordWrap(text);
|
||||||
|
|
||||||
|
return wrappedLines.split(this.splitRegExp);
|
||||||
|
},
|
||||||
|
|
||||||
setText: function (value)
|
setText: function (value)
|
||||||
{
|
{
|
||||||
if (Array.isArray(value))
|
if (Array.isArray(value))
|
||||||
|
@ -221,6 +443,37 @@ var Text = new Class({
|
||||||
return this.style.setShadowFill(enabled);
|
return this.style.setShadowFill(enabled);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the width (in pixels) to use for wrapping lines. Pass in null to remove wrapping by
|
||||||
|
* width.
|
||||||
|
*
|
||||||
|
* @param {number|null} width - The maximum width of a line in pixels. Set to null to remove
|
||||||
|
* wrapping.
|
||||||
|
* @param {boolean} [useAdvancedWrap=false] - Whether or not to use the advanced wrapping
|
||||||
|
* algorithm. If true, spaces are collapsed and whitespace is trimmed from lines. If false,
|
||||||
|
* spaces and whitespace are left as is.
|
||||||
|
* @return {this}
|
||||||
|
*/
|
||||||
|
setWordWrapWidth: function (width, useAdvancedWrap)
|
||||||
|
{
|
||||||
|
return this.style.setWordWrapWidth(width, useAdvancedWrap);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a custom callback for wrapping lines. Pass in null to remove wrapping by callback.
|
||||||
|
*
|
||||||
|
* @param {function} callback - A custom function that will be responsible for wrapping the
|
||||||
|
* text. It will receive two arguments: text (the string to wrap), textObject (this Text
|
||||||
|
* instance). It should return the wrapped lines either as an array of lines or as a string with
|
||||||
|
* newline characters in place to indicate where breaks should happen.
|
||||||
|
* @param {object} [scope=null] - The scope that will be applied when the callback is invoked.
|
||||||
|
* @return {this}
|
||||||
|
*/
|
||||||
|
setWordWrapCallback: function (callback, scope)
|
||||||
|
{
|
||||||
|
return this.style.setWordWrapCallback(callback, scope);
|
||||||
|
},
|
||||||
|
|
||||||
setAlign: function (align)
|
setAlign: function (align)
|
||||||
{
|
{
|
||||||
return this.style.setAlign(align);
|
return this.style.setAlign(align);
|
||||||
|
@ -289,12 +542,14 @@ var Text = new Class({
|
||||||
var style = this.style;
|
var style = this.style;
|
||||||
var size = style.metrics;
|
var size = style.metrics;
|
||||||
|
|
||||||
|
style.syncFont(canvas, context);
|
||||||
|
|
||||||
var outputText = this.text;
|
var outputText = this.text;
|
||||||
|
|
||||||
// if (style.wordWrap)
|
if (style.wordWrapWidth || style.wordWrapCallback)
|
||||||
// {
|
{
|
||||||
// outputText = this.runWordWrap(this.text);
|
outputText = this.runWordWrap(this.text);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Split text into lines
|
// Split text into lines
|
||||||
var lines = outputText.split(this.splitRegExp);
|
var lines = outputText.split(this.splitRegExp);
|
||||||
|
@ -325,6 +580,7 @@ var Text = new Class({
|
||||||
{
|
{
|
||||||
canvas.width = w;
|
canvas.width = w;
|
||||||
canvas.height = h;
|
canvas.height = h;
|
||||||
|
style.syncFont(canvas, context); // Resizing resets the context
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -339,7 +595,7 @@ var Text = new Class({
|
||||||
context.fillRect(0, 0, w, h);
|
context.fillRect(0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
style.syncFont(canvas, context);
|
style.syncStyle(canvas, context);
|
||||||
|
|
||||||
context.textBaseline = 'alphabetic';
|
context.textBaseline = 'alphabetic';
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@ var BaseSound = new Class({
|
||||||
this.volume = this.currentConfig.volume;
|
this.volume = this.currentConfig.volume;
|
||||||
this.rate = this.currentConfig.rate;
|
this.rate = this.currentConfig.rate;
|
||||||
this.detune = this.currentConfig.detune;
|
this.detune = this.currentConfig.detune;
|
||||||
// TODO assign other config values to buffer source
|
this.loop = this.currentConfig.loop;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
|
|
|
@ -109,6 +109,7 @@ var WebAudioSound = new Class({
|
||||||
*/
|
*/
|
||||||
// TODO add delay param
|
// TODO add delay param
|
||||||
createAndStartBufferSource: function () {
|
createAndStartBufferSource: function () {
|
||||||
|
var _this = this;
|
||||||
var seek = this.currentConfig.seek;
|
var seek = this.currentConfig.seek;
|
||||||
var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
|
var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
|
||||||
var duration = this.duration - seek;
|
var duration = this.duration - seek;
|
||||||
|
@ -117,12 +118,18 @@ var WebAudioSound = new Class({
|
||||||
this.source.buffer = this.audioBuffer;
|
this.source.buffer = this.audioBuffer;
|
||||||
this.source.connect(this.muteNode);
|
this.source.connect(this.muteNode);
|
||||||
this.source.onended = function (ev) {
|
this.source.onended = function (ev) {
|
||||||
if (ev.target === this.source) {
|
if (ev.target === _this.source) {
|
||||||
// sound ended
|
// sound ended
|
||||||
this.hasEnded = true;
|
if (_this.currentConfig.loop) {
|
||||||
|
_this.resetConfig();
|
||||||
|
_this.createAndStartBufferSource();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this.hasEnded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// else was stopped
|
// else was stopped
|
||||||
}.bind(this);
|
};
|
||||||
this.applyConfig();
|
this.applyConfig();
|
||||||
this.source.start(0, Math.max(0, offset), Math.max(0, duration));
|
this.source.start(0, Math.max(0, offset), Math.max(0, duration));
|
||||||
this.resetConfig();
|
this.resetConfig();
|
||||||
|
@ -284,4 +291,18 @@ Object.defineProperty(WebAudioSound.prototype, 'seek', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* Property indicating whether or not
|
||||||
|
* the sound or current sound marker will loop.
|
||||||
|
*
|
||||||
|
* @property {boolean} loop
|
||||||
|
*/
|
||||||
|
Object.defineProperty(WebAudioSound.prototype, 'loop', {
|
||||||
|
get: function () {
|
||||||
|
return this.currentConfig.loop;
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
this.currentConfig.loop = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
module.exports = WebAudioSound;
|
module.exports = WebAudioSound;
|
||||||
|
|
Loading…
Reference in a new issue