Initial refactor of tooltip code

This commit is contained in:
Evert Timberg 2015-12-21 15:52:33 -05:00
parent d26ab24e3c
commit d7643bf865

View File

@ -297,227 +297,231 @@
return this;
},
draw: function() {
var ctx = this._chart.ctx;
getTooltipSize: function getTooltipSize() {
var vm = this._view;
var ctx = this._chart.ctx;
if (this._view.opacity === 0) {
return;
}
// Get Dimensions
vm.position = "top";
var caretPadding = vm.caretPadding || 2;
var size = {
height: vm.yPadding * 2, // Tooltip Padding
width: 0
};
var combinedBodyLength = vm.body.length + vm.beforeBody.length + vm.afterBody.length;
// Height
var tooltipHeight = vm.yPadding * 2; // Tooltip Padding
tooltipHeight += vm.title.length * vm.titleFontSize; // Title Lines
tooltipHeight += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing
tooltipHeight += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin
tooltipHeight += combinedBodyLength * vm.bodyFontSize; // Body Lines
tooltipHeight += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
tooltipHeight += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin
tooltipHeight += vm.footer.length * (vm.footerFontSize); // Footer Lines
tooltipHeight += vm.footer.length ? (vm.footer.length - 1) * vm.footerSpacing : 0; // Footer Line Spacing
size.height += vm.title.length * vm.titleFontSize; // Title Lines
size.height += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing
size.height += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin
size.height += combinedBodyLength * vm.bodyFontSize; // Body Lines
size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
size.height += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin
size.height += vm.footer.length * (vm.footerFontSize); // Footer Lines
size.height += vm.footer.length ? (vm.footer.length - 1) * vm.footerSpacing : 0; // Footer Line Spacing
// Width
var tooltipWidth = 0;
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(line) {
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
size.width = Math.max(size.width, ctx.measureText(line).width);
});
helpers.each(vm.beforeBody, function(line) {
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
helpers.each(vm.beforeBody.concat(vm.afterBody), function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
}, this);
helpers.each(vm.body, function(line) {
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0));
}, this);
helpers.each(vm.afterBody, function(line) {
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
size.width = Math.max(size.width, ctx.measureText(line).width + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0));
}, this);
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(line) {
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
size.width = Math.max(size.width, ctx.measureText(line).width);
});
tooltipWidth += 2 * vm.xPadding;
var tooltipTotalWidth = tooltipWidth + vm.caretSize + caretPadding;
size.width += 2 * vm.xPadding;
// Smart Tooltip placement to stay on the canvas
// Top, center, or bottom
return size;
},
determineAlignment: function determineAlignment(size) {
var vm = this._view;
vm.yAlign = "center";
if (vm.y - (tooltipHeight / 2) < 0) {
if (vm.y - (size.height / 2) < 0) {
vm.yAlign = "top";
} else if (vm.y + (tooltipHeight / 2) > this._chart.height) {
} else if (vm.y + (size.height / 2) > this._chart.height) {
vm.yAlign = "bottom";
}
// Left or Right
vm.xAlign = "right";
if (vm.x + tooltipTotalWidth > this._chart.width) {
if (vm.x + size.width > this._chart.width) {
vm.xAlign = "left";
}
},
getBackgroundPoint: function getBackgroundPoint(size, caretPadding) {
var vm = this._view;
// Background Position
var tooltipX = vm.x,
tooltipY = vm.y;
var pt = {
x: vm.x,
y: vm.y
};
if (vm.yAlign === 'top') {
tooltipY = vm.y - vm.caretSize - vm.cornerRadius;
pt.y = vm.y - vm.caretSize - vm.cornerRadius;
} else if (vm.yAlign === 'bottom') {
tooltipY = vm.y - tooltipHeight + vm.caretSize + vm.cornerRadius;
pt.y = vm.y - size.height + vm.caretSize + vm.cornerRadius;
} else {
tooltipY = vm.y - (tooltipHeight / 2);
pt.y = vm.y - (size.height / 2);
}
if (vm.xAlign === 'left') {
tooltipX = vm.x - tooltipTotalWidth;
pt.x = vm.x - size.width;
} else if (vm.xAlign === 'right') {
tooltipX = vm.x + caretPadding + vm.caretSize;
pt.x = vm.x + caretPadding + vm.caretSize;
} else {
tooltipX = vm.x + (tooltipTotalWidth / 2);
pt.x = vm.x + (size.width / 2);
}
// Draw Background
return pt;
},
drawCaret: function drawCaret(opacity, caretPadding) {
var vm = this._view;
var ctx = this._chart.ctx;
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString();
ctx.beginPath();
if (vm.xAlign === 'left') {
ctx.moveTo(vm.x - caretPadding, vm.y);
ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y - vm.caretSize);
ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y + vm.caretSize);
} else {
ctx.moveTo(vm.x + caretPadding, vm.y);
ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y - vm.caretSize);
ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y + vm.caretSize);
}
ctx.closePath();
ctx.fill();
},
drawTitle: function drawTitle(pt, vm, ctx, opacity) {
if (vm.title.length) {
ctx.textAlign = vm._titleAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.titleColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(title, i) {
ctx.fillText(title, pt.x, pt.y);
pt.y += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing
if (i + 1 === vm.title.length) {
pt.y += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing
}
}, this);
}
},
drawBody: function drawBody(pt, vm, ctx, opacity) {
ctx.textAlign = vm._bodyAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
// Before Body
helpers.each(vm.beforeBody, function(beforeBody) {
ctx.fillText(beforeBody, pt.x, pt.y);
pt.y += vm.bodyFontSize + vm.bodySpacing;
});
helpers.each(vm.body, function(body, i) {
// Draw Legend-like boxes if needed
if (this._options.tooltips.mode !== 'single') {
// Fill a white rect so that colours merge nicely if the opacity is < 1
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
ctx.fillRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize);
// Border
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
ctx.strokeRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize);
// Inner square
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
ctx.fillRect(pt.x + 1, pt.y + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2);
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text
}
// Body Line
ctx.fillText(body, pt.x + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0), pt.y);
pt.y += vm.bodyFontSize + vm.bodySpacing;
}, this);
// After Body
helpers.each(vm.afterBody, function(afterBody) {
ctx.fillText(afterBody, pt.x, pt.y);
pt.y += vm.bodyFontSize;
});
pt.y -= vm.bodySpacing; // Remove last body spacing
},
drawFooter: function drawFooter(pt, vm, ctx, opacity) {
if (vm.footer.length) {
pt.y += vm.footerMarginTop;
ctx.textAlign = vm._footerAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.footerColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(footer) {
ctx.fillText(footer, pt.x, pt.y);
pt.y += vm.footerFontSize + vm.footerSpacing;
}, this);
}
},
draw: function draw() {
var ctx = this._chart.ctx;
var vm = this._view;
if (vm.opacity === 0) {
return;
}
var caretPadding = vm.caretPadding || 2;
var tooltipSize = this.getTooltipSize();
var backgroundWidth = tooltipSize.width;
// Expand to be new total size (including caret)
tooltipSize.width += vm.caretSize + caretPadding;
// Smart Tooltip placement to stay on the canvas
// Top, center, or bottom
this.determineAlignment(tooltipSize);
var pt = this.getBackgroundPoint(tooltipSize, caretPadding);
// IE11/Edge does not like very small opacities, so snap to 0
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
if (this._options.tooltips.enabled) {
// Draw Background
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString();
helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipHeight, vm.cornerRadius);
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, backgroundWidth, tooltipSize.height, vm.cornerRadius);
ctx.fill();
}
// Draw Caret
if (this._options.tooltips.enabled) {
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString();
if (vm.xAlign === 'left') {
ctx.beginPath();
ctx.moveTo(vm.x - caretPadding, vm.y);
ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y - vm.caretSize);
ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y + vm.caretSize);
ctx.closePath();
ctx.fill();
} else {
ctx.beginPath();
ctx.moveTo(vm.x + caretPadding, vm.y);
ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y - vm.caretSize);
ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y + vm.caretSize);
ctx.closePath();
ctx.fill();
}
}
// Draw Title, Body, and Footer
if (this._options.tooltips.enabled) {
var yBase = tooltipY + vm.yPadding;
var xBase = tooltipX + vm.xPadding;
// Draw Caret
this.drawCaret(opacity, caretPadding);
// Draw Title, Body, and Footer
pt.x += vm.xPadding;
pt.y += vm.yPadding;
// Titles
if (vm.title.length) {
ctx.textAlign = vm._titleAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.titleColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(title, i) {
ctx.fillText(title, xBase, yBase);
yBase += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing
if (i + 1 === vm.title.length) {
yBase += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing
}
}, this);
}
this.drawTitle(pt, vm, ctx, opacity);
// Body
ctx.textAlign = vm._bodyAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
// Before Body
helpers.each(vm.beforeBody, function(beforeBody) {
ctx.fillText(beforeBody, xBase, yBase);
yBase += vm.bodyFontSize + vm.bodySpacing;
});
helpers.each(vm.body, function(body, i) {
// Draw Legend-like boxes if needed
if (this._options.tooltips.mode !== 'single') {
// Fill a white rect so that colours merge nicely if the opacity is < 1
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
ctx.fillRect(xBase, yBase, vm.bodyFontSize, vm.bodyFontSize);
// Border
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
ctx.strokeRect(xBase, yBase, vm.bodyFontSize, vm.bodyFontSize);
// Inner square
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
ctx.fillRect(xBase + 1, yBase + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2);
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text
}
// Body Line
ctx.fillText(body, xBase + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0), yBase);
yBase += vm.bodyFontSize + vm.bodySpacing;
}, this);
// After Body
helpers.each(vm.afterBody, function(afterBody) {
ctx.fillText(afterBody, xBase, yBase);
yBase += vm.bodyFontSize;
});
yBase -= vm.bodySpacing; // Remove last body spacing
this.drawBody(pt, vm, ctx, opacity);
// Footer
if (vm.footer.length) {
yBase += vm.footerMarginTop;
ctx.textAlign = vm._footerAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.footerColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(footer) {
ctx.fillText(footer, xBase, yBase);
yBase += vm.footerFontSize + vm.footerSpacing;
}, this);
}
this.drawFooter(pt, vm, ctx, opacity);
}
},
}
});
}).call(this);