let util = require('../../../../util'); /** * Created by Alex on 3/17/2015. */ class Label { constructor(body,options) { this.body = body; this.setOptions(options); this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; // could be cached } setOptions(options) { this.options = options; if (options.label !== undefined) { this.labelDirty = true; } } draw(ctx, x, y, selected, baseline = 'middle') { if (this.options.label !== undefined) { // check if we have to render the label let relativeFontSize = Number(this.options.font.size) * this.body.view.scale; if (this.options.label && relativeFontSize >= this.options.scaling.label.drawThreshold - 1) { // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) let fontSize = Number(this.options.font.size); if (relativeFontSize >= this.options.scaling.label.maxVisible) { fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; } // notify the canvas of the fontsize and thickness ctx.font = (selected ? "bold " : "") + fontSize + "px " + this.options.font.face; // update the size cache if required if (this.labelDirty == true) { this.calculateLabelSize(ctx, selected, x, y, baseline); } // create some of the local variables let yLine = this.size.yLine; let lines = String(this.options.label).split('\n'); let lineCount = lines.length; // create the fontfill background this._drawLabelRect(ctx); // draw text this._drawLabelText(ctx, x, yLine, lines, lineCount, fontSize, baseline, relativeFontSize); } } } getTextSize(ctx, selected) { if (this.options.label !== undefined) { this._calculateLabelSize(ctx,selected); } else { this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; } return this.size; } _calculateLabelSize(ctx,selected,x,y,baseline) { ctx.font = (selected ? "bold " : "") + this.options.font.size + "px " + this.options.font.face; let lines = String(this.options.label).split('\n'); let lineCount = lines.length; let yLine = y + (1 - lineCount) * 0.5 * this.options.font.size; let width = ctx.measureText(lines[0]).width; for (let i = 1; i < lineCount; i++) { let lineWidth = ctx.measureText(lines[i]).width; width = lineWidth > width ? lineWidth : width; } let height = this.options.font.size * lineCount; let left = x - width * 0.5; let top = y - height * 0.5; if (baseline == "hanging") { top += 0.5 * this.options.font.size; top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers yLine += 4; // distance from node } // cache this.size = {top: top, left: left, width: width, height: height, yLine: yLine}; } calculateLabelSize(ctx,selected,x=0,y=0,baseline='middle') { if (this.labelDirty == true) { this._calculateLabelSize(ctx, selected, x, y, baseline); } } /** * Draws the label rectangle * @param {CanvasRenderingContext2D} ctx * @private */ _drawLabelRect(ctx) { if (this.options.font.background !== undefined && this.options.font.background !== "none") { ctx.fillStyle = this.options.font.background; let lineMargin = 2; switch (this.options.font.align) { case 'middle': ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); break; case 'top': ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height); break; case 'bottom': ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); break default: ctx.fillRect(this.size.left, this.size.top, this.size.width, this.size.height); break; } } } /** * Draws the label text * @param {CanvasRenderingContext2D} ctx * @param {Number} x * @param {Number} yLine * @param {Array} lines * @param {Number} lineCount * @param {Number} fontSize * @private */ _drawLabelText(ctx, x, yLine, lines, lineCount, fontSize, baseline = 'middle', relativeFontSize = this.options.font.size) { // fade in when relative scale is between threshold and threshold - 1 let fontColor = this.options.font.color || "#000000"; let strokeColor = this.options.font.strokeColor; if (relativeFontSize <= this.options.scaling.label.drawThreshold) { let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - relativeFontSize))); fontColor = util.overrideOpacity(fontColor, opacity); strokeColor = util.overrideOpacity(strokeColor, opacity); } // draw text ctx.fillStyle = fontColor; ctx.textAlign = 'center'; // check for label alignment (for edges) // TODO: make alignment for nodes if (this.options.font.align !== 'horizontal') { x = 0; yLine = 0; let lineMargin = 2; if (this.options.font.align === 'top') { ctx.textBaseline = 'alphabetic'; yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers } else if (this.options.font.align === 'bottom') { ctx.textBaseline = 'hanging'; yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers } else { ctx.textBaseline = 'middle'; } } else { ctx.textBaseline = baseline; } // check for strokeWidth if (this.options.font.stroke > 0) { ctx.lineWidth = this.options.font.stroke; ctx.strokeStyle = strokeColor; ctx.lineJoin = 'round'; } for (let i = 0; i < lineCount; i++) { if (this.options.font.stroke > 0) { ctx.strokeText(lines[i], x, yLine); } ctx.fillText(lines[i], x, yLine); yLine += fontSize; } } } export default Label;