]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Initial refactor of tooltip code
authorEvert Timberg <evert.timberg@gmail.com>
Mon, 21 Dec 2015 20:52:33 +0000 (15:52 -0500)
committerEvert Timberg <evert.timberg@gmail.com>
Mon, 21 Dec 2015 20:52:33 +0000 (15:52 -0500)
src/core/core.tooltip.js

index dfd5d704b0a569e51dd2546295cb4cd973783033..3fdc54d17bc0dd8f962ee1da933b96748221c5cf 100644 (file)
 
                        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);