From d7643bf865def6090cea9ef5b0ade7844c1d5477 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Mon, 21 Dec 2015 15:52:33 -0500 Subject: [PATCH] Initial refactor of tooltip code --- src/core/core.tooltip.js | 318 ++++++++++++++++++++------------------- 1 file changed, 161 insertions(+), 157 deletions(-) diff --git a/src/core/core.tooltip.js b/src/core/core.tooltip.js index dfd5d704b..3fdc54d17 100644 --- a/src/core/core.tooltip.js +++ b/src/core/core.tooltip.js @@ -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; - // IE11/Edge does not like very small opacities, so snap to 0 - var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; + ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString(); + ctx.beginPath(); - if (this._options.tooltips.enabled) { - ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString(); - helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipHeight, vm.cornerRadius); - ctx.fill(); + 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); - // 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(); - } + 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; + }); - // Draw Title, Body, and Footer - - if (this._options.tooltips.enabled) { + 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); - var yBase = tooltipY + vm.yPadding; - var xBase = tooltipX + vm.xPadding; + // Border + ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString(); + ctx.strokeRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize); - // Titles + // 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); - 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); + 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); - // 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); + pt.y += vm.bodyFontSize + vm.bodySpacing; + }, this); - // Before Body - helpers.each(vm.beforeBody, function(beforeBody) { - ctx.fillText(beforeBody, xBase, yBase); - yBase += vm.bodyFontSize + vm.bodySpacing; - }); + // After Body + helpers.each(vm.afterBody, function(afterBody) { + ctx.fillText(afterBody, pt.x, pt.y); + pt.y += vm.bodyFontSize; + }); - helpers.each(vm.body, function(body, i) { + 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); - // 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); + 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; - // Border - ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString(); - ctx.strokeRect(xBase, yBase, vm.bodyFontSize, vm.bodyFontSize); + if (vm.opacity === 0) { + return; + } - // 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); + 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; - ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text - } + // Smart Tooltip placement to stay on the canvas + // Top, center, or bottom + this.determineAlignment(tooltipSize); - // Body Line - ctx.fillText(body, xBase + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0), yBase); + var pt = this.getBackgroundPoint(tooltipSize, caretPadding); - yBase += vm.bodyFontSize + vm.bodySpacing; + // IE11/Edge does not like very small opacities, so snap to 0 + var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; - }, this); + if (this._options.tooltips.enabled) { + // Draw Background + ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString(); + helpers.drawRoundedRectangle(ctx, pt.x, pt.y, backgroundWidth, tooltipSize.height, vm.cornerRadius); + ctx.fill(); - // After Body - helpers.each(vm.afterBody, function(afterBody) { - ctx.fillText(afterBody, xBase, yBase); - yBase += vm.bodyFontSize; - }); + // Draw Caret + this.drawCaret(opacity, caretPadding); + + // Draw Title, Body, and Footer + pt.x += vm.xPadding; + pt.y += vm.yPadding; - yBase -= vm.bodySpacing; // Remove last body spacing + // Titles + this.drawTitle(pt, vm, ctx, opacity); + // Body + 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); -- 2.47.3