mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 05:01:41 +08:00
fixes in emoji rendering
This commit is contained in:
parent
f2ed14fa08
commit
adba0073e3
@ -18,10 +18,15 @@ export function stringToArray(string: string): string[] {
|
|||||||
return [...string].reduce((acc, char, index, array) => {
|
return [...string].reduce((acc, char, index, array) => {
|
||||||
// Handle emoji with skin tone modifiers and ZWJ sequences
|
// Handle emoji with skin tone modifiers and ZWJ sequences
|
||||||
if (/\p{Emoji}/u.test(char)) {
|
if (/\p{Emoji}/u.test(char)) {
|
||||||
if (acc.length > 0 && /\p{Emoji}/u.test(acc[acc.length - 1])) {
|
// Check if next character is a modifier or ZWJ sequence
|
||||||
// Combine with previous emoji if it's part of a sequence
|
const nextChar = array[index + 1];
|
||||||
acc[acc.length - 1] += char;
|
if (nextChar && /\p{Emoji_Modifier}|\u200D/u.test(nextChar)) {
|
||||||
|
// If we have a modifier, combine with current emoji
|
||||||
|
acc.push(char + nextChar);
|
||||||
|
// Skip the next character since we've used it
|
||||||
|
array[index + 1] = '';
|
||||||
} else {
|
} else {
|
||||||
|
// No modifier - treat as separate emoji
|
||||||
acc.push(char);
|
acc.push(char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,7 +41,8 @@ export function stringToArray(string: string): string[] {
|
|||||||
acc[acc.length - 1] += char;
|
acc[acc.length - 1] += char;
|
||||||
}
|
}
|
||||||
// Handle other characters
|
// Handle other characters
|
||||||
else {
|
else if (char) {
|
||||||
|
// Only push if not an empty string (skipped modifier)
|
||||||
acc.push(char);
|
acc.push(char);
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
@ -520,13 +526,16 @@ export class Text extends Shape<TextConfig> {
|
|||||||
* that would fit in the specified width
|
* that would fit in the specified width
|
||||||
*/
|
*/
|
||||||
let low = 0,
|
let low = 0,
|
||||||
high = line.length,
|
high = stringToArray(line).length, // Convert to array for proper emoji handling
|
||||||
match = '',
|
match = '',
|
||||||
matchWidth = 0;
|
matchWidth = 0;
|
||||||
while (low < high) {
|
while (low < high) {
|
||||||
const mid = (low + high) >>> 1,
|
const mid = (low + high) >>> 1,
|
||||||
substr = line.slice(0, mid + 1),
|
// Convert array indices to string
|
||||||
|
lineArray = stringToArray(line),
|
||||||
|
substr = lineArray.slice(0, mid + 1).join(''),
|
||||||
substrWidth = this._getTextWidth(substr) + additionalWidth;
|
substrWidth = this._getTextWidth(substr) + additionalWidth;
|
||||||
|
|
||||||
if (substrWidth <= maxWidth) {
|
if (substrWidth <= maxWidth) {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
match = substr;
|
match = substr;
|
||||||
@ -544,20 +553,24 @@ export class Text extends Shape<TextConfig> {
|
|||||||
// a fitting substring was found
|
// a fitting substring was found
|
||||||
if (wrapAtWord) {
|
if (wrapAtWord) {
|
||||||
// try to find a space or dash where wrapping could be done
|
// try to find a space or dash where wrapping could be done
|
||||||
var wrapIndex;
|
const lineArray = stringToArray(line);
|
||||||
const nextChar = line[match.length];
|
const matchArray = stringToArray(match);
|
||||||
|
const nextChar = lineArray[matchArray.length];
|
||||||
const nextIsSpaceOrDash = nextChar === SPACE || nextChar === DASH;
|
const nextIsSpaceOrDash = nextChar === SPACE || nextChar === DASH;
|
||||||
|
|
||||||
|
let wrapIndex;
|
||||||
if (nextIsSpaceOrDash && matchWidth <= maxWidth) {
|
if (nextIsSpaceOrDash && matchWidth <= maxWidth) {
|
||||||
wrapIndex = match.length;
|
wrapIndex = matchArray.length;
|
||||||
} else {
|
} else {
|
||||||
wrapIndex =
|
// Find last space or dash in the array
|
||||||
Math.max(match.lastIndexOf(SPACE), match.lastIndexOf(DASH)) +
|
const lastSpaceIndex = matchArray.lastIndexOf(SPACE);
|
||||||
1;
|
const lastDashIndex = matchArray.lastIndexOf(DASH);
|
||||||
|
wrapIndex = Math.max(lastSpaceIndex, lastDashIndex) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapIndex > 0) {
|
if (wrapIndex > 0) {
|
||||||
// re-cut the substring found at the space/dash position
|
|
||||||
low = wrapIndex;
|
low = wrapIndex;
|
||||||
match = match.slice(0, low);
|
match = lineArray.slice(0, low).join('');
|
||||||
matchWidth = this._getTextWidth(match);
|
matchWidth = this._getTextWidth(match);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -578,13 +591,14 @@ export class Text extends Shape<TextConfig> {
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line = line.slice(low);
|
|
||||||
line = line.trimLeft();
|
// Convert remaining text using array operations
|
||||||
|
const lineArray = stringToArray(line);
|
||||||
|
line = lineArray.slice(low).join('').trimLeft();
|
||||||
|
|
||||||
if (line.length > 0) {
|
if (line.length > 0) {
|
||||||
// Check if the remaining text would fit on one line
|
|
||||||
lineWidth = this._getTextWidth(line);
|
lineWidth = this._getTextWidth(line);
|
||||||
if (lineWidth <= maxWidth) {
|
if (lineWidth <= maxWidth) {
|
||||||
// if it does, add the line and break out of the loop
|
|
||||||
this._addTextLine(line);
|
this._addTextLine(line);
|
||||||
currentHeightPx += lineHeightPx;
|
currentHeightPx += lineHeightPx;
|
||||||
textWidth = Math.max(textWidth, lineWidth);
|
textWidth = Math.max(textWidth, lineWidth);
|
||||||
|
@ -161,6 +161,35 @@ describe('Text', function () {
|
|||||||
context.fillStyle = 'darkgrey';
|
context.fillStyle = 'darkgrey';
|
||||||
context.fillText('😬👧🏿', 10, 10 + 25);
|
context.fillText('😬👧🏿', 10, 10 + 25);
|
||||||
|
|
||||||
|
compareLayerAndCanvas(layer, canvas, 254, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check emoji rendering', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
var text = new Konva.Text({
|
||||||
|
text: '😁😁😁',
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
fontSize: 20,
|
||||||
|
draggable: true,
|
||||||
|
width: 65,
|
||||||
|
fill: 'black',
|
||||||
|
scaleY: 0.9999999999999973,
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(text);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var canvas = createCanvas();
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
context.textBaseline = 'middle';
|
||||||
|
context.font = 'normal normal 20px Arial';
|
||||||
|
context.fillStyle = 'black';
|
||||||
|
context.fillText('😁😁', 10, 10 + 10);
|
||||||
|
context.fillText('😁', 10, 10 + 30);
|
||||||
|
|
||||||
compareLayerAndCanvas(layer, canvas, 254);
|
compareLayerAndCanvas(layer, canvas, 254);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user