UNPKG

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
"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);