mirror of
https://github.com/konvajs/konva.git
synced 2025-08-20 08:55:44 +08:00
new charRenderFunc property for Konva.text, clean tests
This commit is contained in:
parent
464432a2a5
commit
d2ecf2064e
@ -913,7 +913,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
* @example
|
* @example
|
||||||
* shape.getAncestors().forEach(function(node) {
|
* shape.getAncestors().forEach(function(node) {
|
||||||
* console.log(node.getId());
|
* console.log(node.id());
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
getAncestors() {
|
getAncestors() {
|
||||||
|
|||||||
@ -13,6 +13,18 @@ import { _registerNode } from '../Global';
|
|||||||
|
|
||||||
import { GetSet } from '../types';
|
import { GetSet } from '../types';
|
||||||
|
|
||||||
|
export interface CharRenderProps {
|
||||||
|
char: string;
|
||||||
|
index: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
lineIndex: number;
|
||||||
|
column: number;
|
||||||
|
isLastInLine: boolean;
|
||||||
|
width: number;
|
||||||
|
context: Context;
|
||||||
|
}
|
||||||
|
|
||||||
export function stringToArray(string: string): string[] {
|
export function stringToArray(string: string): string[] {
|
||||||
// Use Unicode-aware splitting
|
// Use Unicode-aware splitting
|
||||||
return [...string].reduce((acc, char, index, array) => {
|
return [...string].reduce((acc, char, index, array) => {
|
||||||
@ -223,6 +235,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
align = this.align(),
|
align = this.align(),
|
||||||
totalWidth = this.getWidth(),
|
totalWidth = this.getWidth(),
|
||||||
letterSpacing = this.letterSpacing(),
|
letterSpacing = this.letterSpacing(),
|
||||||
|
charRenderFunc = this.charRenderFunc(),
|
||||||
fill = this.fill(),
|
fill = this.fill(),
|
||||||
textDecoration = this.textDecoration(),
|
textDecoration = this.textDecoration(),
|
||||||
shouldUnderline = textDecoration.indexOf('underline') !== -1,
|
shouldUnderline = textDecoration.indexOf('underline') !== -1,
|
||||||
@ -318,10 +331,14 @@ export class Text extends Shape<TextConfig> {
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// As `letterSpacing` isn't supported on Safari, we use this polyfill.
|
// As `letterSpacing` isn't supported on Safari, we use this polyfill.
|
||||||
// The exception is for RTL text, which we rely on native as it cannot
|
// The exception is for RTL text, which we rely on native as it cannot
|
||||||
// be supported otherwise.
|
// be supported otherwise.
|
||||||
if (direction !== RTL && (letterSpacing !== 0 || align === JUSTIFY)) {
|
if (
|
||||||
|
direction !== RTL &&
|
||||||
|
(letterSpacing !== 0 || align === JUSTIFY || charRenderFunc)
|
||||||
|
) {
|
||||||
// var words = text.split(' ');
|
// var words = text.split(' ');
|
||||||
const spacesNumber = text.split(' ').length - 1;
|
const spacesNumber = text.split(' ').length - 1;
|
||||||
const array = stringToArray(text);
|
const array = stringToArray(text);
|
||||||
@ -338,7 +355,31 @@ export class Text extends Shape<TextConfig> {
|
|||||||
this._partialTextX = lineTranslateX;
|
this._partialTextX = lineTranslateX;
|
||||||
this._partialTextY = translateY + lineTranslateY;
|
this._partialTextY = translateY + lineTranslateY;
|
||||||
this._partialText = letter;
|
this._partialText = letter;
|
||||||
|
|
||||||
|
if (charRenderFunc) {
|
||||||
|
context.save();
|
||||||
|
const previousLines = textArr.slice(0, n);
|
||||||
|
const previousGraphemes = previousLines.reduce(
|
||||||
|
(acc, line) => acc + stringToArray(line.text).length,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const charIndex = li + previousGraphemes;
|
||||||
|
charRenderFunc({
|
||||||
|
char: letter,
|
||||||
|
index: charIndex,
|
||||||
|
x: lineTranslateX,
|
||||||
|
y: translateY + lineTranslateY,
|
||||||
|
lineIndex: n,
|
||||||
|
column: li,
|
||||||
|
isLastInLine: lastLine,
|
||||||
|
width: this.measureSize(letter).width,
|
||||||
|
context,
|
||||||
|
});
|
||||||
|
}
|
||||||
context.fillStrokeShape(this);
|
context.fillStrokeShape(this);
|
||||||
|
if (charRenderFunc) {
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
lineTranslateX += this.measureSize(letter).width + letterSpacing;
|
lineTranslateX += this.measureSize(letter).width + letterSpacing;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -713,6 +754,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
text: GetSet<string, this>;
|
text: GetSet<string, this>;
|
||||||
wrap: GetSet<string, this>;
|
wrap: GetSet<string, this>;
|
||||||
ellipsis: GetSet<boolean, this>;
|
ellipsis: GetSet<boolean, this>;
|
||||||
|
charRenderFunc: GetSet<null | ((props: CharRenderProps) => void), this>;
|
||||||
}
|
}
|
||||||
|
|
||||||
Text.prototype._fillFunc = _fillFunc;
|
Text.prototype._fillFunc = _fillFunc;
|
||||||
@ -995,3 +1037,21 @@ Factory.addGetterSetter(Text, 'text', '', getStringValidator());
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Factory.addGetterSetter(Text, 'textDecoration', '');
|
Factory.addGetterSetter(Text, 'textDecoration', '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get/set per-character render hook. The callback is invoked for each grapheme before drawing.
|
||||||
|
* It can mutate the provided context (e.g. translate, rotate, change styles) and should return void.
|
||||||
|
* Note: per-character rendering may disable native kerning/ligatures.
|
||||||
|
* @name Konva.Text#charRenderFunc
|
||||||
|
* @method
|
||||||
|
* @param {(props: {char: string, index: number, x: number, y: number, lineIndex: number, column: number, isLastInLine: boolean, width: number, context: Konva.Context}) => void} charRenderFunc
|
||||||
|
* @returns {(props: CharRenderProps) => void}
|
||||||
|
* @example
|
||||||
|
* // apply small x-translation to every second character
|
||||||
|
* text.charRenderFunc(function(props) {
|
||||||
|
* if (props.index % 2 === 1) {
|
||||||
|
* props.context.translate(2, 0);
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
Factory.addGetterSetter(Text, 'charRenderFunc', undefined);
|
||||||
|
|||||||
@ -48,37 +48,40 @@
|
|||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
|
|
||||||
var canvas = document.createElement('canvas');
|
const text = new Konva.Text({
|
||||||
// use external library to parse and draw gif animation
|
text: 'Hello, how are you doing today? Would you like to start using konva.js',
|
||||||
function onDrawFrame(ctx, frame) {
|
width: 400,
|
||||||
// update canvas size
|
fontSize: 50,
|
||||||
canvas.width = frame.width;
|
x: 100,
|
||||||
canvas.height = frame.height;
|
y: 100,
|
||||||
// update canvas that we are using for Konva.Image
|
draggable: true,
|
||||||
ctx.drawImage(frame.buffer, 0, 0);
|
});
|
||||||
// redraw the layer
|
layer.add(text);
|
||||||
layer.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
gifler('https://konvajs.org/assets/yoda.gif').frames(canvas, onDrawFrame);
|
const anim = new Konva.Animation((frame) => {
|
||||||
|
text.charRenderFunc(({ char, index, context }) => {
|
||||||
function testKonvaImage() {
|
const animationDuration = 4000;
|
||||||
setInterval(() => {
|
const animationTime = frame.time % animationDuration;
|
||||||
const image = new Konva.Image({
|
const length = text.text().length;
|
||||||
image: canvas,
|
const durationPerChar = animationDuration / length;
|
||||||
x: Math.random() * width,
|
const localStartTime = index * durationPerChar;
|
||||||
y: Math.random() * height,
|
const localTime = animationTime - localStartTime;
|
||||||
});
|
const inAnimation = localTime > 0;
|
||||||
layer.add(image);
|
if (!inAnimation) {
|
||||||
|
context.setAttr('globalAlpha', 0);
|
||||||
setTimeout(() => {
|
return;
|
||||||
image.image(canvas);
|
}
|
||||||
image.destroy();
|
const afterAnimation = localTime > durationPerChar;
|
||||||
}, 500);
|
if (afterAnimation) {
|
||||||
}, 10);
|
return;
|
||||||
}
|
}
|
||||||
|
const animationAlpha = Math.abs(localTime / durationPerChar);
|
||||||
testKonvaImage();
|
const oldOpacity = context.globalAlpha;
|
||||||
|
context.setAttr('globalAlpha', oldOpacity * animationAlpha * 0.5);
|
||||||
|
context.translate(0, -15 + (localTime / durationPerChar) * 15);
|
||||||
|
});
|
||||||
|
}, layer);
|
||||||
|
anim.start();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -171,9 +171,7 @@ describe('Layer', function () {
|
|||||||
|
|
||||||
circle.colorKey = '#000000';
|
circle.colorKey = '#000000';
|
||||||
|
|
||||||
circle.on('mouseover', function () {
|
circle.on('mouseover', function () {});
|
||||||
console.log('mouseover');
|
|
||||||
});
|
|
||||||
|
|
||||||
layer.add(circle);
|
layer.add(circle);
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
|
|||||||
@ -2386,9 +2386,7 @@ describe('MouseEvents', function () {
|
|||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
|
|
||||||
stage.on('mousedown mousemove mouseup click', function (e) {
|
stage.on('mousedown mousemove mouseup click', function (e) {});
|
||||||
console.log('state', e.type);
|
|
||||||
});
|
|
||||||
|
|
||||||
var rect = new Konva.Rect({
|
var rect = new Konva.Rect({
|
||||||
width: 50,
|
width: 50,
|
||||||
@ -2402,7 +2400,6 @@ describe('MouseEvents', function () {
|
|||||||
|
|
||||||
var clicks = 0;
|
var clicks = 0;
|
||||||
rect.on('click', function () {
|
rect.on('click', function () {
|
||||||
console.log('click');
|
|
||||||
clicks += 1;
|
clicks += 1;
|
||||||
if (clicks === 2) {
|
if (clicks === 2) {
|
||||||
debugger;
|
debugger;
|
||||||
|
|||||||
@ -831,7 +831,6 @@ describe('Caching', function () {
|
|||||||
group.cache();
|
group.cache();
|
||||||
|
|
||||||
const canvas = group._cache.get('canvas').scene;
|
const canvas = group._cache.get('canvas').scene;
|
||||||
console.log(canvas.width / 2);
|
|
||||||
assert.equal(canvas.width, 106 * canvas.pixelRatio);
|
assert.equal(canvas.width, 106 * canvas.pixelRatio);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -924,11 +924,11 @@ describe('Node', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
circle1.on('mousemove', function () {
|
circle1.on('mousemove', function () {
|
||||||
console.log('mousemove circle1');
|
// console.log('mousemove circle1');
|
||||||
});
|
});
|
||||||
|
|
||||||
circle2.on('mousemove', function () {
|
circle2.on('mousemove', function () {
|
||||||
console.log('mousemove circle2');
|
// console.log('mousemove circle2');
|
||||||
});
|
});
|
||||||
|
|
||||||
layer1.add(circle1);
|
layer1.add(circle1);
|
||||||
@ -1101,11 +1101,11 @@ describe('Node', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
circle1.on('mousemove', function () {
|
circle1.on('mousemove', function () {
|
||||||
console.log('mousemove circle1');
|
// console.log('mousemove circle1');
|
||||||
});
|
});
|
||||||
|
|
||||||
circle2.on('mousemove', function () {
|
circle2.on('mousemove', function () {
|
||||||
console.log('mousemove circle2');
|
// console.log('mousemove circle2');
|
||||||
});
|
});
|
||||||
|
|
||||||
group.add(circle2);
|
group.add(circle2);
|
||||||
@ -1887,12 +1887,6 @@ describe('Node', function () {
|
|||||||
|
|
||||||
circle.on('click', function () {
|
circle.on('click', function () {
|
||||||
clicks.push('circle');
|
clicks.push('circle');
|
||||||
|
|
||||||
/*
|
|
||||||
var evt = window.event;
|
|
||||||
var rightClick = evt.which ? evt.which == 3 : evt.button == 2;
|
|
||||||
console.log(rightClick);
|
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
var foo;
|
var foo;
|
||||||
circle.on('customEvent', function (evt) {
|
circle.on('customEvent', function (evt) {
|
||||||
|
|||||||
@ -1186,7 +1186,6 @@ describe('Path', function () {
|
|||||||
SVGPath.setAttribute('d', data);
|
SVGPath.setAttribute('d', data);
|
||||||
for (var i = 0.001; i < path.getLength(); i += 1) {
|
for (var i = 0.001; i < path.getLength(); i += 1) {
|
||||||
var p = path.getPointAtLength(i);
|
var p = path.getPointAtLength(i);
|
||||||
console.log(p);
|
|
||||||
var circle = new Konva.Circle({
|
var circle = new Konva.Circle({
|
||||||
x: p.x + path.x(),
|
x: p.x + path.x(),
|
||||||
y: p.y + path.y(),
|
y: p.y + path.y(),
|
||||||
@ -1196,7 +1195,6 @@ describe('Path', function () {
|
|||||||
});
|
});
|
||||||
layer.add(circle);
|
layer.add(circle);
|
||||||
const position = SVGPath.getPointAtLength(i);
|
const position = SVGPath.getPointAtLength(i);
|
||||||
console.log(position);
|
|
||||||
assert(Math.abs(p.x - position.x) <= 1);
|
assert(Math.abs(p.x - position.x) <= 1);
|
||||||
assert(Math.abs(p.y - position.y) <= 1);
|
assert(Math.abs(p.y - position.y) <= 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -398,7 +398,7 @@ describe('Stage', function () {
|
|||||||
'17) getAllIntersections should return one shape'
|
'17) getAllIntersections should return one shape'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 266, y: 114 })[0].getId(),
|
stage.getAllIntersections({ x: 266, y: 114 })[0].id(),
|
||||||
'greenCircle',
|
'greenCircle',
|
||||||
'19) first intersection should be greenCircle'
|
'19) first intersection should be greenCircle'
|
||||||
);
|
);
|
||||||
@ -409,7 +409,7 @@ describe('Stage', function () {
|
|||||||
'18) getAllIntersections should return one shape'
|
'18) getAllIntersections should return one shape'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 414, y: 115 })[0].getId(),
|
stage.getAllIntersections({ x: 414, y: 115 })[0].id(),
|
||||||
'redCircle',
|
'redCircle',
|
||||||
'20) first intersection should be redCircle'
|
'20) first intersection should be redCircle'
|
||||||
);
|
);
|
||||||
@ -420,12 +420,12 @@ describe('Stage', function () {
|
|||||||
'1) getAllIntersections should return two shapes'
|
'1) getAllIntersections should return two shapes'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[0].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[0].id(),
|
||||||
'redCircle',
|
'redCircle',
|
||||||
'2) first intersection should be redCircle'
|
'2) first intersection should be redCircle'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[1].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[1].id(),
|
||||||
'greenCircle',
|
'greenCircle',
|
||||||
'3) second intersection should be greenCircle'
|
'3) second intersection should be greenCircle'
|
||||||
);
|
);
|
||||||
@ -440,7 +440,7 @@ describe('Stage', function () {
|
|||||||
'4) getAllIntersections should return one shape'
|
'4) getAllIntersections should return one shape'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[0].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[0].id(),
|
||||||
'redCircle',
|
'redCircle',
|
||||||
'5) first intersection should be redCircle'
|
'5) first intersection should be redCircle'
|
||||||
);
|
);
|
||||||
@ -455,12 +455,12 @@ describe('Stage', function () {
|
|||||||
'6) getAllIntersections should return two shapes'
|
'6) getAllIntersections should return two shapes'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[0].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[0].id(),
|
||||||
'redCircle',
|
'redCircle',
|
||||||
'7) first intersection should be redCircle'
|
'7) first intersection should be redCircle'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[1].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[1].id(),
|
||||||
'greenCircle',
|
'greenCircle',
|
||||||
'8) second intersection should be greenCircle'
|
'8) second intersection should be greenCircle'
|
||||||
);
|
);
|
||||||
@ -475,7 +475,7 @@ describe('Stage', function () {
|
|||||||
'9) getAllIntersections should return one shape'
|
'9) getAllIntersections should return one shape'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[0].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[0].id(),
|
||||||
'greenCircle',
|
'greenCircle',
|
||||||
'10) first intersection should be greenCircle'
|
'10) first intersection should be greenCircle'
|
||||||
);
|
);
|
||||||
@ -490,12 +490,12 @@ describe('Stage', function () {
|
|||||||
'11) getAllIntersections should return two shapes'
|
'11) getAllIntersections should return two shapes'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[0].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[0].id(),
|
||||||
'redCircle',
|
'redCircle',
|
||||||
'12) first intersection should be redCircle'
|
'12) first intersection should be redCircle'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
stage.getAllIntersections({ x: 350, y: 118 })[1].getId(),
|
stage.getAllIntersections({ x: 350, y: 118 })[1].id(),
|
||||||
'greenCircle',
|
'greenCircle',
|
||||||
'13) second intersection should be greenCircle'
|
'13) second intersection should be greenCircle'
|
||||||
);
|
);
|
||||||
@ -507,12 +507,12 @@ describe('Stage', function () {
|
|||||||
'14) getAllIntersections should return two shapes'
|
'14) getAllIntersections should return two shapes'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
layer.getAllIntersections({ x: 350, y: 118 })[0].getId(),
|
layer.getAllIntersections({ x: 350, y: 118 })[0].id(),
|
||||||
'redCircle',
|
'redCircle',
|
||||||
'15) first intersection should be redCircle'
|
'15) first intersection should be redCircle'
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
layer.getAllIntersections({ x: 350, y: 118 })[1].getId(),
|
layer.getAllIntersections({ x: 350, y: 118 })[1].id(),
|
||||||
'greenCircle',
|
'greenCircle',
|
||||||
'16) second intersection should be greenCircle'
|
'16) second intersection should be greenCircle'
|
||||||
);
|
);
|
||||||
@ -1424,7 +1424,10 @@ describe('Stage', function () {
|
|||||||
mimeType: 'image/jpeg',
|
mimeType: 'image/jpeg',
|
||||||
quality: 0.5,
|
quality: 0.5,
|
||||||
});
|
});
|
||||||
assert.isTrue(blob instanceof Blob && blob.type === 'image/jpeg', "can't change type of blob");
|
assert.isTrue(
|
||||||
|
blob instanceof Blob && blob.type === 'image/jpeg',
|
||||||
|
"can't change type of blob"
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
assert.fail('error creating blob');
|
assert.fail('error creating blob');
|
||||||
|
|||||||
@ -215,7 +215,7 @@ describe('Text', function () {
|
|||||||
var canvas = createCanvas();
|
var canvas = createCanvas();
|
||||||
var context = canvas.getContext('2d');
|
var context = canvas.getContext('2d');
|
||||||
context.textBaseline = 'middle';
|
context.textBaseline = 'middle';
|
||||||
context.letterSpacing = '10px';
|
(context as any).letterSpacing = '10px';
|
||||||
context.font = 'normal normal 50px Arial';
|
context.font = 'normal normal 50px Arial';
|
||||||
context.fillStyle = 'darkgrey';
|
context.fillStyle = 'darkgrey';
|
||||||
context.fillText('आपकी दौड़ के लिए परफेक्ट जूते!', 10, 10 + 25);
|
context.fillText('आपकी दौड़ के लिए परफेक्ट जूते!', 10, 10 + 25);
|
||||||
@ -1625,14 +1625,14 @@ describe('Text', function () {
|
|||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
var grd = ctx.createPattern(imageObj, 'repeat');
|
var grd = ctx.createPattern(imageObj, 'repeat');
|
||||||
grd.setTransform({
|
(grd as any).setTransform({
|
||||||
a: 1,
|
a: 1,
|
||||||
b: 0,
|
b: 0,
|
||||||
c: 0,
|
c: 0,
|
||||||
d: 1,
|
d: 1,
|
||||||
e: -50,
|
e: -50,
|
||||||
f: 0,
|
f: 0,
|
||||||
});
|
} as any);
|
||||||
ctx.fillStyle = grd;
|
ctx.fillStyle = grd;
|
||||||
|
|
||||||
ctx.fillText(text.text(), 0, 15);
|
ctx.fillText(text.text(), 0, 15);
|
||||||
@ -1668,19 +1668,13 @@ describe('Text', function () {
|
|||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
var grd = ctx.createPattern(imageObj, 'repeat');
|
var grd = ctx.createPattern(imageObj, 'repeat');
|
||||||
const matrix =
|
// node-canvas expects its own DOMMatrix type; cast to any for cross-env test
|
||||||
typeof DOMMatrix === 'undefined'
|
const matrix: any =
|
||||||
? {
|
typeof (global as any).DOMMatrix === 'undefined'
|
||||||
a: 0.5, // Horizontal scaling. A value of 1 results in no scaling.
|
? { a: 0.5, b: 0, c: 0, d: 0.5, e: 0, f: 0 }
|
||||||
b: 0, // Vertical skewing.
|
: new (global as any).DOMMatrix([0.5, 0, 0, 0.5, 0, 0]);
|
||||||
c: 0, // Horizontal skewing.
|
|
||||||
d: 0.5,
|
|
||||||
e: 0, // Horizontal translation (moving).
|
|
||||||
f: 0, // Vertical translation (moving).
|
|
||||||
}
|
|
||||||
: new DOMMatrix([0.5, 0, 0, 0.5, 0, 0]);
|
|
||||||
|
|
||||||
grd.setTransform(matrix);
|
(grd as any).setTransform(matrix);
|
||||||
|
|
||||||
ctx.fillStyle = grd;
|
ctx.fillStyle = grd;
|
||||||
|
|
||||||
@ -1791,4 +1785,32 @@ describe('Text', function () {
|
|||||||
|
|
||||||
assert.equal(layer.getContext().getTrace(), trace);
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('charRenderFunc draws per character and can mutate context', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var text = new Konva.Text({
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
text: 'AB',
|
||||||
|
fontSize: 20,
|
||||||
|
charRenderFunc: function (props) {
|
||||||
|
if (props.index === 1) {
|
||||||
|
// shift only the second character
|
||||||
|
props.context.translate(0, 10);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
layer.add(text);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var trace = layer.getContext().getTrace();
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
trace,
|
||||||
|
'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,10,10);font=normal normal 20px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();save();fillStyle=black;fillText(A,0,10);restore();save();translate(0,10);fillStyle=black;fillText(B,13.34,10);restore();restore();restore();'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "lib",
|
"outDir": "lib",
|
||||||
"module": "CommonJS",
|
"module": "ESNext",
|
||||||
"target": "ES2018",
|
"target": "ES2018",
|
||||||
// "sourceMap": true,
|
// "sourceMap": true,
|
||||||
"noEmitOnError": true,
|
"noEmitOnError": true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user