konva
Version:
<p align="center"> <img src="https://raw.githubusercontent.com/konvajs/konvajs.github.io/master/apple-touch-icon-180x180.png" alt="Konva logo" height="180" /> </p>
359 lines (358 loc) • 15.2 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var Util_1 = require("../Util");
var Factory_1 = require("../Factory");
var Shape_1 = require("../Shape");
var Path_1 = require("./Path");
var Text_1 = require("./Text");
var Validators_1 = require("../Validators");
var Global_1 = require("../Global");
var EMPTY_STRING = '', NORMAL = 'normal';
function _fillFunc(context) {
context.fillText(this.partialText, 0, 0);
}
function _strokeFunc(context) {
context.strokeText(this.partialText, 0, 0);
}
var TextPath = (function (_super) {
__extends(TextPath, _super);
function TextPath(config) {
var _this = _super.call(this, config) || this;
_this.dummyCanvas = Util_1.Util.createCanvasElement();
_this.dataArray = [];
_this.dataArray = Path_1.Path.parsePathData(_this.attrs.data);
_this.on('dataChange.konva', function () {
this.dataArray = Path_1.Path.parsePathData(this.attrs.data);
this._setTextData();
});
_this.on('textChange.konva alignChange.konva letterSpacingChange.konva kerningFuncChange.konva', _this._setTextData);
if (config && config['getKerning']) {
Util_1.Util.warn('getKerning TextPath API is deprecated. Please use "kerningFunc" instead.');
_this.kerningFunc(config['getKerning']);
}
_this._setTextData();
return _this;
}
TextPath.prototype._sceneFunc = function (context) {
context.setAttr('font', this._getContextFont());
context.setAttr('textBaseline', this.textBaseline());
context.setAttr('textAlign', 'left');
context.save();
var textDecoration = this.textDecoration();
var fill = this.fill();
var fontSize = this.fontSize();
var glyphInfo = this.glyphInfo;
if (textDecoration === 'underline') {
context.beginPath();
}
for (var i = 0; i < glyphInfo.length; i++) {
context.save();
var p0 = glyphInfo[i].p0;
context.translate(p0.x, p0.y);
context.rotate(glyphInfo[i].rotation);
this.partialText = glyphInfo[i].text;
context.fillStrokeShape(this);
if (textDecoration === 'underline') {
if (i === 0) {
context.moveTo(0, fontSize / 2 + 1);
}
context.lineTo(fontSize, fontSize / 2 + 1);
}
context.restore();
}
if (textDecoration === 'underline') {
context.strokeStyle = fill;
context.lineWidth = fontSize / 20;
context.stroke();
}
context.restore();
};
TextPath.prototype._hitFunc = function (context) {
context.beginPath();
var glyphInfo = this.glyphInfo;
if (glyphInfo.length >= 1) {
var p0 = glyphInfo[0].p0;
context.moveTo(p0.x, p0.y);
}
for (var i = 0; i < glyphInfo.length; i++) {
var p1 = glyphInfo[i].p1;
context.lineTo(p1.x, p1.y);
}
context.setAttr('lineWidth', this.fontSize());
context.setAttr('strokeStyle', this.colorKey);
context.stroke();
};
TextPath.prototype.getTextWidth = function () {
return this.textWidth;
};
TextPath.prototype.getTextHeight = function () {
Util_1.Util.warn('text.getTextHeight() method is deprecated. Use text.height() - for full height and text.fontSize() - for one line height.');
return this.textHeight;
};
TextPath.prototype.setText = function (text) {
return Text_1.Text.prototype.setText.call(this, text);
};
TextPath.prototype._getContextFont = function () {
return Text_1.Text.prototype._getContextFont.call(this);
};
TextPath.prototype._getTextSize = function (text) {
var dummyCanvas = this.dummyCanvas;
var _context = dummyCanvas.getContext('2d');
_context.save();
_context.font = this._getContextFont();
var metrics = _context.measureText(text);
_context.restore();
return {
width: metrics.width,
height: parseInt(this.attrs.fontSize, 10)
};
};
TextPath.prototype._setTextData = function () {
var that = this;
var size = this._getTextSize(this.attrs.text);
var letterSpacing = this.letterSpacing();
var align = this.align();
var kerningFunc = this.kerningFunc();
this.textWidth = size.width;
this.textHeight = size.height;
var textFullWidth = Math.max(this.textWidth + ((this.attrs.text || '').length - 1) * letterSpacing, 0);
this.glyphInfo = [];
var fullPathWidth = 0;
for (var l = 0; l < that.dataArray.length; l++) {
if (that.dataArray[l].pathLength > 0) {
fullPathWidth += that.dataArray[l].pathLength;
}
}
var offset = 0;
if (align === 'center') {
offset = Math.max(0, fullPathWidth / 2 - textFullWidth / 2);
}
if (align === 'right') {
offset = Math.max(0, fullPathWidth - textFullWidth);
}
var charArr = this.text().split('');
var spacesNumber = this.text().split(' ').length - 1;
var p0, p1, pathCmd;
var pIndex = -1;
var currentT = 0;
var getNextPathSegment = function () {
currentT = 0;
var pathData = that.dataArray;
for (var j = pIndex + 1; j < pathData.length; j++) {
if (pathData[j].pathLength > 0) {
pIndex = j;
return pathData[j];
}
else if (pathData[j].command === 'M') {
p0 = {
x: pathData[j].points[0],
y: pathData[j].points[1]
};
}
}
return {};
};
var findSegmentToFitCharacter = function (c) {
var glyphWidth = that._getTextSize(c).width + letterSpacing;
if (c === ' ' && align === 'justify') {
glyphWidth += (fullPathWidth - textFullWidth) / spacesNumber;
}
var currLen = 0;
var attempts = 0;
p1 = undefined;
while (Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 &&
attempts < 25) {
attempts++;
var cumulativePathLength = currLen;
while (pathCmd === undefined) {
pathCmd = getNextPathSegment();
if (pathCmd &&
cumulativePathLength + pathCmd.pathLength < glyphWidth) {
cumulativePathLength += pathCmd.pathLength;
pathCmd = undefined;
}
}
if (pathCmd === {} || p0 === undefined) {
return undefined;
}
var needNewSegment = false;
switch (pathCmd.command) {
case 'L':
if (Path_1.Path.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
p1 = Path_1.Path.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
}
else {
pathCmd = undefined;
}
break;
case 'A':
var start = pathCmd.points[4];
var dTheta = pathCmd.points[5];
var end = pathCmd.points[4] + dTheta;
if (currentT === 0) {
currentT = start + 0.00000001;
}
else if (glyphWidth > currLen) {
currentT += ((Math.PI / 180.0) * dTheta) / Math.abs(dTheta);
}
else {
currentT -= ((Math.PI / 360.0) * dTheta) / Math.abs(dTheta);
}
if ((dTheta < 0 && currentT < end) ||
(dTheta >= 0 && currentT > end)) {
currentT = end;
needNewSegment = true;
}
p1 = Path_1.Path.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
break;
case 'C':
if (currentT === 0) {
if (glyphWidth > pathCmd.pathLength) {
currentT = 0.00000001;
}
else {
currentT = glyphWidth / pathCmd.pathLength;
}
}
else if (glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength;
}
else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
}
if (currentT > 1.0) {
currentT = 1.0;
needNewSegment = true;
}
p1 = Path_1.Path.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
break;
case 'Q':
if (currentT === 0) {
currentT = glyphWidth / pathCmd.pathLength;
}
else if (glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength;
}
else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
}
if (currentT > 1.0) {
currentT = 1.0;
needNewSegment = true;
}
p1 = Path_1.Path.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
break;
}
if (p1 !== undefined) {
currLen = Path_1.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
}
if (needNewSegment) {
needNewSegment = false;
pathCmd = undefined;
}
}
};
var testChar = 'C';
var glyphWidth = that._getTextSize(testChar).width + letterSpacing;
for (var k = 0; k < offset / glyphWidth; k++) {
findSegmentToFitCharacter(testChar);
if (p0 === undefined || p1 === undefined) {
break;
}
p0 = p1;
}
for (var i = 0; i < charArr.length; i++) {
findSegmentToFitCharacter(charArr[i]);
if (p0 === undefined || p1 === undefined) {
break;
}
var width = Path_1.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
var kern = 0;
if (kerningFunc) {
try {
kern = kerningFunc(charArr[i - 1], charArr[i]) * this.fontSize();
}
catch (e) {
kern = 0;
}
}
p0.x += kern;
p1.x += kern;
this.textWidth += kern;
var midpoint = Path_1.Path.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
var rotation = Math.atan2(p1.y - p0.y, p1.x - p0.x);
this.glyphInfo.push({
transposeX: midpoint.x,
transposeY: midpoint.y,
text: charArr[i],
rotation: rotation,
p0: p0,
p1: p1
});
p0 = p1;
}
};
TextPath.prototype.getSelfRect = function () {
var points = [];
this.glyphInfo.forEach(function (info) {
points.push(info.p0.x);
points.push(info.p0.y);
points.push(info.p1.x);
points.push(info.p1.y);
});
var minX = points[0];
var maxX = points[0];
var minY = points[0];
var maxY = points[0];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2];
y = points[i * 2 + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
var fontSize = this.fontSize();
return {
x: Math.round(minX) - fontSize / 2,
y: Math.round(minY) - fontSize / 2,
width: Math.round(maxX - minX) + fontSize,
height: Math.round(maxY - minY) + fontSize
};
};
return TextPath;
}(Shape_1.Shape));
exports.TextPath = TextPath;
TextPath.prototype._fillFunc = _fillFunc;
TextPath.prototype._strokeFunc = _strokeFunc;
TextPath.prototype._fillFuncHit = _fillFunc;
TextPath.prototype._strokeFuncHit = _strokeFunc;
TextPath.prototype.className = 'TextPath';
TextPath.prototype._attrsAffectingSize = ['text', 'fontSize', 'data'];
Global_1._registerNode(TextPath);
Factory_1.Factory.addGetterSetter(TextPath, 'data');
Factory_1.Factory.addGetterSetter(TextPath, 'fontFamily', 'Arial');
Factory_1.Factory.addGetterSetter(TextPath, 'fontSize', 12, Validators_1.getNumberValidator());
Factory_1.Factory.addGetterSetter(TextPath, 'fontStyle', NORMAL);
Factory_1.Factory.addGetterSetter(TextPath, 'align', 'left');
Factory_1.Factory.addGetterSetter(TextPath, 'letterSpacing', 0, Validators_1.getNumberValidator());
Factory_1.Factory.addGetterSetter(TextPath, 'textBaseline', 'middle');
Factory_1.Factory.addGetterSetter(TextPath, 'fontVariant', NORMAL);
Factory_1.Factory.addGetterSetter(TextPath, 'text', EMPTY_STRING);
Factory_1.Factory.addGetterSetter(TextPath, 'textDecoration', null);
Factory_1.Factory.addGetterSetter(TextPath, 'kerningFunc', null);
Util_1.Collection.mapMethods(TextPath);