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 ],
|
||||
fixedHeight: [ 'fixedHeight', 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({
|
||||
|
@ -90,7 +94,15 @@ var TextStyle = new Class({
|
|||
|
||||
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
|
||||
|
@ -124,6 +136,10 @@ var TextStyle = new Class({
|
|||
syncFont: function (canvas, context)
|
||||
{
|
||||
context.font = this._font;
|
||||
},
|
||||
|
||||
syncStyle: function (canvas, context)
|
||||
{
|
||||
context.textBaseline = 'alphabetic';
|
||||
|
||||
context.fillStyle = this.color;
|
||||
|
@ -335,6 +351,47 @@ var TextStyle = new Class({
|
|||
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)
|
||||
{
|
||||
if (align === undefined) { align = 'left'; }
|
||||
|
|
|
@ -124,6 +124,228 @@ var Text = new Class({
|
|||
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)
|
||||
{
|
||||
if (Array.isArray(value))
|
||||
|
@ -221,6 +443,37 @@ var Text = new Class({
|
|||
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)
|
||||
{
|
||||
return this.style.setAlign(align);
|
||||
|
@ -289,12 +542,14 @@ var Text = new Class({
|
|||
var style = this.style;
|
||||
var size = style.metrics;
|
||||
|
||||
style.syncFont(canvas, context);
|
||||
|
||||
var outputText = this.text;
|
||||
|
||||
// if (style.wordWrap)
|
||||
// {
|
||||
// outputText = this.runWordWrap(this.text);
|
||||
// }
|
||||
if (style.wordWrapWidth || style.wordWrapCallback)
|
||||
{
|
||||
outputText = this.runWordWrap(this.text);
|
||||
}
|
||||
|
||||
// Split text into lines
|
||||
var lines = outputText.split(this.splitRegExp);
|
||||
|
@ -325,6 +580,7 @@ var Text = new Class({
|
|||
{
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
style.syncFont(canvas, context); // Resizing resets the context
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -339,7 +595,7 @@ var Text = new Class({
|
|||
context.fillRect(0, 0, w, h);
|
||||
}
|
||||
|
||||
style.syncFont(canvas, context);
|
||||
style.syncStyle(canvas, context);
|
||||
|
||||
context.textBaseline = 'alphabetic';
|
||||
|
||||
|
|
|
@ -265,7 +265,7 @@ var BaseSound = new Class({
|
|||
this.volume = this.currentConfig.volume;
|
||||
this.rate = this.currentConfig.rate;
|
||||
this.detune = this.currentConfig.detune;
|
||||
// TODO assign other config values to buffer source
|
||||
this.loop = this.currentConfig.loop;
|
||||
},
|
||||
/**
|
||||
* @protected
|
||||
|
|
|
@ -109,6 +109,7 @@ var WebAudioSound = new Class({
|
|||
*/
|
||||
// TODO add delay param
|
||||
createAndStartBufferSource: function () {
|
||||
var _this = this;
|
||||
var seek = this.currentConfig.seek;
|
||||
var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
|
||||
var duration = this.duration - seek;
|
||||
|
@ -117,12 +118,18 @@ var WebAudioSound = new Class({
|
|||
this.source.buffer = this.audioBuffer;
|
||||
this.source.connect(this.muteNode);
|
||||
this.source.onended = function (ev) {
|
||||
if (ev.target === this.source) {
|
||||
if (ev.target === _this.source) {
|
||||
// sound ended
|
||||
this.hasEnded = true;
|
||||
if (_this.currentConfig.loop) {
|
||||
_this.resetConfig();
|
||||
_this.createAndStartBufferSource();
|
||||
}
|
||||
else {
|
||||
_this.hasEnded = true;
|
||||
}
|
||||
}
|
||||
// else was stopped
|
||||
}.bind(this);
|
||||
};
|
||||
this.applyConfig();
|
||||
this.source.start(0, Math.max(0, offset), Math.max(0, duration));
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue