|
|
@ -7,8 +7,20 @@ let util = require('../../../../util'); |
|
|
|
class Label { |
|
|
|
constructor(body,options) { |
|
|
|
this.body = body; |
|
|
|
this.setOptions(options); |
|
|
|
|
|
|
|
this.fontOptions = {}; |
|
|
|
this.defaultOptions = { |
|
|
|
color: '#343434', |
|
|
|
size: 14, // px
|
|
|
|
face: 'arial', |
|
|
|
background: 'none', |
|
|
|
stroke: 0, // px
|
|
|
|
strokeColor: 'white', |
|
|
|
align:'horizontal' |
|
|
|
} |
|
|
|
util.extend(this.fontOptions, this.defaultOptions); |
|
|
|
|
|
|
|
this.setOptions(options); |
|
|
|
this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; // could be cached
|
|
|
|
} |
|
|
|
|
|
|
@ -17,6 +29,18 @@ class Label { |
|
|
|
if (options.label !== undefined) { |
|
|
|
this.labelDirty = true; |
|
|
|
} |
|
|
|
if (options.font) { |
|
|
|
if (typeof options.font === 'string') { |
|
|
|
let optionsArray = options.font.split(" "); |
|
|
|
this.fontOptions.size = optionsArray[0].replace("px",''); |
|
|
|
this.fontOptions.face = optionsArray[1]; |
|
|
|
this.fontOptions.color = optionsArray[2]; |
|
|
|
} |
|
|
|
else if (typeof options.font == 'object') { |
|
|
|
util.extend(this.fontOptions, options.font); |
|
|
|
} |
|
|
|
this.fontOptions.size = Number(this.fontOptions.size); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -34,7 +58,7 @@ class Label { |
|
|
|
return; |
|
|
|
|
|
|
|
// check if we have to render the label
|
|
|
|
let viewFontSize = Number(this.options.font.size) * this.body.view.scale; |
|
|
|
let viewFontSize = this.fontOptions.size * this.body.view.scale; |
|
|
|
if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) |
|
|
|
return; |
|
|
|
|
|
|
@ -53,12 +77,12 @@ class Label { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_drawBackground(ctx) { |
|
|
|
if (this.options.font.background !== undefined && this.options.font.background !== "none") { |
|
|
|
ctx.fillStyle = this.options.font.background; |
|
|
|
if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { |
|
|
|
ctx.fillStyle = this.fontOptions.background; |
|
|
|
|
|
|
|
let lineMargin = 2; |
|
|
|
|
|
|
|
switch (this.options.font.align) { |
|
|
|
switch (this.fontOptions.align) { |
|
|
|
case 'middle': |
|
|
|
ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); |
|
|
|
break; |
|
|
@ -84,7 +108,7 @@ class Label { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_drawText(ctx, selected, x, y, baseline = 'middle') { |
|
|
|
let fontSize = Number(this.options.font.size); |
|
|
|
let fontSize = this.fontOptions.size; |
|
|
|
let viewFontSize = fontSize * this.body.view.scale; |
|
|
|
// this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel)
|
|
|
|
if (viewFontSize >= this.options.scaling.label.maxVisible) { |
|
|
@ -96,20 +120,20 @@ class Label { |
|
|
|
[x, yLine] = this._setAlignment(ctx, x, yLine, baseline); |
|
|
|
|
|
|
|
// configure context for drawing the text
|
|
|
|
ctx.font = (selected ? 'bold ' : '') + fontSize + "px " + this.options.font.face; |
|
|
|
ctx.font = (selected ? 'bold ' : '') + fontSize + "px " + this.fontOptions.face; |
|
|
|
ctx.fillStyle = fontColor; |
|
|
|
ctx.textAlign = 'center'; |
|
|
|
|
|
|
|
// set the strokeWidth
|
|
|
|
if (this.options.font.stroke > 0) { |
|
|
|
ctx.lineWidth = this.options.font.stroke; |
|
|
|
if (this.fontOptions.stroke > 0) { |
|
|
|
ctx.lineWidth = this.fontOptions.stroke; |
|
|
|
ctx.strokeStyle = strokeColor; |
|
|
|
ctx.lineJoin = 'round'; |
|
|
|
} |
|
|
|
|
|
|
|
// draw the text
|
|
|
|
for (let i = 0; i < this.lineCount; i++) { |
|
|
|
if (this.options.font.stroke > 0) { |
|
|
|
if (this.fontOptions.stroke > 0) { |
|
|
|
ctx.strokeText(this.lines[i], x, yLine); |
|
|
|
} |
|
|
|
ctx.fillText(this.lines[i], x, yLine); |
|
|
@ -120,16 +144,16 @@ class Label { |
|
|
|
_setAlignment(ctx, x, yLine, baseline) { |
|
|
|
// check for label alignment (for edges)
|
|
|
|
// TODO: make alignment for nodes
|
|
|
|
if (this.options.font.align !== 'horizontal') { |
|
|
|
if (this.fontOptions.align !== 'horizontal') { |
|
|
|
x = 0; |
|
|
|
yLine = 0; |
|
|
|
|
|
|
|
let lineMargin = 2; |
|
|
|
if (this.options.font.align === 'top') { |
|
|
|
if (this.fontOptions.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') { |
|
|
|
else if (this.fontOptions.align === 'bottom') { |
|
|
|
ctx.textBaseline = 'hanging'; |
|
|
|
yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers
|
|
|
|
} |
|
|
@ -153,8 +177,8 @@ class Label { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getColor(viewFontSize) { |
|
|
|
let fontColor = this.options.font.color || '#000000'; |
|
|
|
let strokeColor = this.options.font.strokeColor || '#ffffff'; |
|
|
|
let fontColor = this.fontOptions.color || '#000000'; |
|
|
|
let strokeColor = this.fontOptions.strokeColor || '#ffffff'; |
|
|
|
if (viewFontSize <= this.options.scaling.label.drawThreshold) { |
|
|
|
let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); |
|
|
|
fontColor = util.overrideOpacity(fontColor, opacity); |
|
|
@ -173,7 +197,7 @@ class Label { |
|
|
|
getTextSize(ctx, selected = false) { |
|
|
|
let size = { |
|
|
|
width: this._processLabel(ctx,selected), |
|
|
|
height: this.options.font.size * this.lineCount |
|
|
|
height: this.fontOptions.size * this.lineCount |
|
|
|
}; |
|
|
|
return size; |
|
|
|
} |
|
|
@ -191,12 +215,12 @@ class Label { |
|
|
|
if (this.labelDirty === true) { |
|
|
|
this.size.width = this._processLabel(ctx,selected); |
|
|
|
} |
|
|
|
this.size.height = this.options.font.size * this.lineCount; |
|
|
|
this.size.height = this.fontOptions.size * this.lineCount; |
|
|
|
this.size.left = x - this.size.width * 0.5; |
|
|
|
this.size.top = y - this.size.height * 0.5; |
|
|
|
this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size; |
|
|
|
this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; |
|
|
|
if (baseline == "hanging") { |
|
|
|
this.size.top += 0.5 * this.options.font.size; |
|
|
|
this.size.top += 0.5 * this.fontOptions.size; |
|
|
|
this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
|
|
|
|
this.size.yLine += 4; // distance from node
|
|
|
|
} |
|
|
@ -219,7 +243,7 @@ class Label { |
|
|
|
if (this.options.label !== undefined) { |
|
|
|
lines = String(this.options.label).split('\n'); |
|
|
|
lineCount = lines.length; |
|
|
|
ctx.font = (selected ? 'bold ' : '') + this.options.font.size + "px " + this.options.font.face; |
|
|
|
ctx.font = (selected ? 'bold ' : '') + this.fontOptions.size + "px " + this.fontOptions.face; |
|
|
|
width = ctx.measureText(lines[0]).width; |
|
|
|
for (let i = 1; i < lineCount; i++) { |
|
|
|
let lineWidth = ctx.measureText(lines[i]).width; |
|
|
|