UNPKG

chartjs-plugin-funnel

Version:
193 lines (166 loc) 5.52 kB
/** * * Extend trapezium element * @author Jone Casper * @email xu.chenhui@live.com * */ "use strict"; module.exports = function (Chart) { var helpers = Chart.helpers, globalOpts = Chart.defaults.global; globalOpts.elements.trapezium = { backgroundColor: globalOpts.defaultColor, borderWidth: 0, borderColor: globalOpts.defaultColor, borderSkipped: 'bottom', type: 'isosceles' // isosceles, scalene }; // Thanks for https://github.com/substack/point-in-polygon var pointInPolygon = function (point, vs) { // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html var x = point[0], y = point[1]; var inside = false; for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) { var xi = vs[i][0], yi = vs[i][1]; var xj = vs[j][0], yj = vs[j][1]; var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) inside = !inside; } return inside; }; Chart.elements.Trapezium = Chart.elements.Rectangle.extend({ getCorners: function () { var vm = this._view; var globalOptionTrapeziumElements = globalOpts.elements.trapezium; var corners = [], type = vm.type || globalOptionTrapeziumElements.type, top = vm.y, borderWidth = vm.borderWidth || globalOptionTrapeziumElements.borderWidth, upHalfWidth = vm.upperWidth / 2, botHalfWidth = vm.bottomWidth / 2, halfStroke = borderWidth / 2; halfStroke = halfStroke < 0 ? 0 : halfStroke; // An isosceles trapezium if (type == 'isosceles') { var x = vm.x; // Corner points, from bottom-left to bottom-right clockwise // | 1 2 | // | 0 3 | corners = [ [x - botHalfWidth + halfStroke, vm.base], [x - upHalfWidth + halfStroke, top + halfStroke], [x + upHalfWidth - halfStroke, top + halfStroke], [x + botHalfWidth - halfStroke, vm.base] ]; } else if (type == 'scalene') { var x1 = vm.x1, x2 = vm.x2; corners = [ [x2 - botHalfWidth + halfStroke, vm.base], [x1 - upHalfWidth + halfStroke, top + halfStroke], [x1 + upHalfWidth - halfStroke, top + halfStroke], [x2 + botHalfWidth - halfStroke, vm.base] ]; } return corners; }, draw: function () { var ctx = this._chart.ctx; var vm = this._view; var globalOptionTrapeziumElements = globalOpts.elements.trapezium; var corners = this.getCorners(); this._cornersCache = corners; ctx.beginPath(); ctx.fillStyle = vm.backgroundColor || globalOptionTrapeziumElements.backgroundColor; ctx.strokeStyle = vm.borderColor || globalOptionTrapeziumElements.borderColor; ctx.lineWidth = vm.borderWidth || globalOptionTrapeziumElements.borderWidth; // Find first (starting) corner with fallback to 'bottom' var borders = ['bottom', 'left', 'top', 'right']; var startCorner = borders.indexOf( vm.borderSkipped || globalOptionTrapeziumElements.borderSkipped, 0); if (startCorner === -1) startCorner = 0; function cornerAt(index) { return corners[(startCorner + index) % 4]; } // Draw rectangle from 'startCorner' ctx.moveTo.apply(ctx, cornerAt(0)); for (var i = 1; i < 4; i++) ctx.lineTo.apply(ctx, cornerAt(i)); ctx.fill(); if (vm.borderWidth) { ctx.stroke(); } }, height: function () { var vm = this._view; if (!vm) { return 0; } return vm.base - vm.y; }, inRange: function (mouseX, mouseY) { var vm = this._view; if (!vm) { return false; } var corners = this._cornersCache ? this._cornersCache : this.getCorners(); return pointInPolygon([mouseX, mouseY], corners); }, inLabelRange: function (mouseX) { var x, vm = this._view; if (!vm) { return false; } if (vm.type == 'scalene') { if (vm.x1 > vm.x2) { return mouseX >= vm.x2 - vm.bottomWidth / 2 && mouseX <= vm.x1 + vm.upperWidth / 2; } else { return mouseX <= vm.x2 + vm.bottomWidth / 2 && mouseX >= vm.x1 - vm.upperWidth / 2; } } var maxWidth = Math.max(vm.upperWidth, vm.bottomWidth); return mouseX >= vm.x - maxWidth / 2 && mouseX <= vm.x + maxWidth / 2; }, tooltipPosition: function () { var vm = this._view; return { x: vm.x || vm.x2, y: vm.base - (vm.base - vm.y) / 2 }; }, getArea: function () { var vm = this._view; var total = 0; var corners = this._cornersCache ? this._cornersCache : this.getCorners(); for (var i = 0, l = corners.length; i < l; i++) { var addX = corners[i][0]; var addY = corners[i == corners.length - 1 ? 0 : i + 1][1]; var subX = corners[i == corners.length - 1 ? 0 : i + 1][0]; var subY = corners[i][1]; total += (addX * addY * 0.5); total -= (subX * subY * 0.5); } return Math.abs(total); }, getCenterPoint: function () { var corners = this._cornersCache ? this._cornersCache : this.getCorners(); var vm = this._view; var x = 0, y = 0, i, j, f, point1, point2; for (i = 0, j = corners.length - 1; i < corners.length; j = i, i++) { point1 = corners[i]; point2 = corners[j]; f = point1[0] * point2[1] - point2[0] * point1[1]; x += (point1[0] + point2[0]) * f; y += (point1[1] + point2[1]) * f; } f = this.getArea() * 6; return {x: x / f, y: y / f}; }, }); };