vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

187 lines
6.1 KiB

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;