From: Evert Timberg Date: Sun, 20 Sep 2015 14:30:12 +0000 (-0400) Subject: Use hidden iframe to detect resize + obey max width when getting maximum size. Update... X-Git-Tag: 2.0.0-alpha4~21^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=39252d463388259eb2758a0603b9e2ec4ff4a16c;p=thirdparty%2FChart.js.git Use hidden iframe to detect resize + obey max width when getting maximum size. Updated the bar sample to start off hidden. Use the 'show' button to display the graph --- diff --git a/samples/bar.html b/samples/bar.html index eb8796ac5..7f1d8e7ea 100644 --- a/samples/bar.html +++ b/samples/bar.html @@ -5,10 +5,15 @@ Bar Chart + -
+ @@ -16,6 +21,7 @@ +

Legend

@@ -64,6 +70,13 @@ data: barChartData, options: { responsive: true, + scales: { + xAxes: [{ + // So that bars fill the entire width of the grid + categorySpacing: 0, + spacing: 0 + }] + } } }); @@ -100,7 +113,7 @@ $('#addData').click(function() { if (barChartData.datasets.length > 0) { - barChartData.labels.push('dataset #' + barChartData.labels.length); + barChartData.labels.push('data #' + barChartData.labels.length); for (var index = 0; index < barChartData.datasets.length; ++index) { window.myBar.addData(randomScalingFactor(), index); @@ -123,6 +136,10 @@ }); updateLegend(); }); + + $('#show').click(function() { + document.getElementById('container').style.display = ''; + }); diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 806248485..614b0579d 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -123,9 +123,11 @@ resize: function resize(silent) { this.stop(); - var canvas = this.chart.canvas, - newWidth = helpers.getMaximumWidth(this.chart.canvas), - newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas); + var canvas = this.chart.canvas; + var newWidth = helpers.getMaximumWidth(this.chart.canvas); + var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0) + ? newWidth / this.chart.aspectRatio + : helpers.getMaximumHeight(this.chart.canvas); canvas.width = this.chart.width = newWidth; canvas.height = this.chart.height = newHeight; @@ -348,19 +350,16 @@ destroy: function destroy() { this.clear(); helpers.unbindEvents(this, this.events); - var canvas = this.chart.canvas; + helpers.removeResizeListener(this.chart.canvas.parentNode); - // Reset canvas height/width attributes starts a fresh with the canvas context + // Reset canvas height/width attributes + var canvas = this.chart.canvas; canvas.width = this.chart.width; canvas.height = this.chart.height; - // < IE9 doesn't support removeProperty - if (canvas.style.removeProperty) { - canvas.style.removeProperty('width'); - canvas.style.removeProperty('height'); - } else { - canvas.style.removeAttribute('width'); - canvas.style.removeAttribute('height'); + // if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here + if (this.chart.originalDevicePixelRatio !== undefined) { + canvas.scale(1 / this.chart.originalDevicePixelRatio, 1 / this.chart.originalDevicePixelRatio); } delete Chart.instances[this.id]; diff --git a/src/core/core.helpers.js b/src/core/core.helpers.js index ab58b0789..b81bf9609 100644 --- a/src/core/core.helpers.js +++ b/src/core/core.helpers.js @@ -690,17 +690,54 @@ removeEvent(chartInstance.chart.canvas, eventName, handler); }); }, + getConstraintWidth = helpers.getConstraintWidth = function(domNode) { // returns Number or undefined if no constraint + var constrainedWidth; + var constrainedWNode = document.defaultView.getComputedStyle(domNode)['max-width']; + var constrainedWContainer = document.defaultView.getComputedStyle(domNode.parentNode)['max-width']; + var hasCWNode = constrainedWNode !== null && constrainedWNode !== "none"; + var hasCWContainer = constrainedWContainer !== null && constrainedWContainer !== "none"; + + if (hasCWNode || hasCWContainer) { + constrainedWidth = Math.min((hasCWNode ? parseInt(constrainedWNode, 10) : Number.POSITIVE_INFINITY), (hasCWContainer ? parseInt(constrainedWContainer, 10) : Number.POSITIVE_INFINITY)); + } + return constrainedWidth; + }, + getConstraintHeight = helpers.getConstraintHeight = function(domNode) { // returns Number or undefined if no constraint + + var constrainedHeight; + var constrainedHNode = document.defaultView.getComputedStyle(domNode)['max-height']; + var constrainedHContainer = document.defaultView.getComputedStyle(domNode.parentNode)['max-height']; + var hasCHNode = constrainedHNode !== null && constrainedHNode !== "none"; + var hasCHContainer = constrainedHContainer !== null && constrainedHContainer !== "none"; + + if (constrainedHNode || constrainedHContainer) { + constrainedHeight = Math.min((hasCHNode ? parseInt(constrainedHNode, 10) : Number.POSITIVE_INFINITY), (hasCHContainer ? parseInt(constrainedHContainer, 10) : Number.POSITIVE_INFINITY)); + } + return constrainedHeight; + }, getMaximumWidth = helpers.getMaximumWidth = function(domNode) { - var container = domNode.parentNode, - padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right')); - // TODO = check cross browser stuff with this. - return container.clientWidth - padding; + var container = domNode.parentNode; + var padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right')); + + var w = container.clientWidth - padding; + var cw = getConstraintWidth(domNode); + if (cw !== undefined) { + w = Math.min(w, cw); + } + + return w; }, getMaximumHeight = helpers.getMaximumHeight = function(domNode) { - var container = domNode.parentNode, - padding = parseInt(getStyle(container, 'padding-bottom')) + parseInt(getStyle(container, 'padding-top')); - // TODO = check cross browser stuff with this. - return container.clientHeight - padding; + var container = domNode.parentNode; + var padding = parseInt(getStyle(container, 'padding-top')) + parseInt(getStyle(container, 'padding-bottom')); + + var h = container.clientHeight - padding; + var ch = getConstraintHeight(domNode); + if (ch !== undefined) { + h = Math.min(h, ch); + } + + return h; }, getStyle = helpers.getStyle = function(el, property) { return el.currentStyle ? @@ -709,16 +746,19 @@ }, getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support retinaScale = helpers.retinaScale = function(chart) { - var ctx = chart.ctx, - width = chart.canvas.width, - height = chart.canvas.height; + var ctx = chart.ctx; + var width = chart.canvas.width; + var height = chart.canvas.height; - if (window.devicePixelRatio) { - ctx.canvas.style.width = width + "px"; - ctx.canvas.style.height = height + "px"; + if (window.devicePixelRatio !== 1) { ctx.canvas.height = height * window.devicePixelRatio; ctx.canvas.width = width * window.devicePixelRatio; ctx.scale(window.devicePixelRatio, window.devicePixelRatio); + + // Store the device pixel ratio so that we can go backwards in `destroy`. + // The devicePixelRatio changes with zoom, so there are no guarantees that it is the same + // when destroy is called + chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || window.devicePixelRatio; } }, //-- Canvas methods @@ -757,6 +797,48 @@ } return window.Color(color); }, + addResizeListener = helpers.addResizeListener = function(node, callback) { + // Hide an iframe before the node + var hiddenIframe = document.createElement('iframe'); + var hiddenIframeClass = 'chartjs-hidden-iframe'; + + if (hiddenIframe.classlist) { + // can use classlist + hiddenIframe.classlist.add(hiddenIframeClass); + } else { + hiddenIframe.setAttribute('class', hiddenIframeClass) + } + + // Set the style + hiddenIframe.style.width = '100%'; + hiddenIframe.style.display = 'block'; + hiddenIframe.style.border = 0; + hiddenIframe.style.height = 0; + hiddenIframe.style.margin = 0; + hiddenIframe.style.position = 'absolute'; + hiddenIframe.style.left = 0; + hiddenIframe.style.right = 0; + hiddenIframe.style.top = 0; + hiddenIframe.style.bottom = 0; + + // Insert the iframe so that contentWindow is available + node.insertBefore(hiddenIframe, node.firstChild); + + var timer = 0; + (hiddenIframe.contentWindow || hiddenIframe).onresize = function() { + if (callback) { + callback(); + } + } + }, + removeResizeListener = helpers.removeResizeListener = function(node) { + var hiddenIframe = node.querySelector('.chartjs-hidden-iframe'); + + // Remove the resize detect iframe + if (hiddenIframe) { + hiddenIframe.remove(); + } + }, isArray = helpers.isArray = function(obj) { if (!Array.isArray) { return Object.prototype.toString.call(arg) === '[object Array]'; diff --git a/src/core/core.js b/src/core/core.js index 42c214e14..512b4f42e 100755 --- a/src/core/core.js +++ b/src/core/core.js @@ -19,7 +19,6 @@ //Occupy the global variable of Chart, and create a simple base class var Chart = function(context, config) { - var chart = this; this.config = config; // Support a jQuery'd canvas element @@ -32,32 +31,36 @@ context = context.getContext("2d"); } + this.ctx = context; this.canvas = context.canvas; - this.ctx = context; + // Figure out what the size of the chart will be. + // If the canvas has a specified width and height, we use those else + // we look to see if the canvas node has a CSS width and height. + // If there is still no height, fill the parent container + this.width = context.canvas.width || parseInt(Chart.helpers.getStyle(context.canvas, 'width')) || Chart.helpers.getMaximumWidth(context.canvas); + this.height = context.canvas.height || parseInt(Chart.helpers.getStyle(context.canvas, 'height')) || Chart.helpers.getMaximumHeight(context.canvas); - //Variables global to the chart - var computeDimension = function(element, dimension) { - if (element['offset' + dimension]) { - return element['offset' + dimension]; - } else { - return document.defaultView.getComputedStyle(element).getPropertyValue(dimension); - } - }; - - var width = this.width = computeDimension(context.canvas, 'Width') || context.canvas.width; - var height = this.height = computeDimension(context.canvas, 'Height') || context.canvas.height; + this.aspectRatio = this.width / this.height; - // Firefox requires this to work correctly - context.canvas.width = width; - context.canvas.height = height; + if (isNaN(this.aspectRatio) || isFinite(this.aspectRatio) === false) { + // If the canvas has no size, try and figure out what the aspect ratio will be. + // Some charts prefer square canvases (pie, radar, etc). If that is specified, use that + // else use the canvas default ratio of 2 + this.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2; + } - width = this.width = context.canvas.width; - height = this.height = context.canvas.height; - this.aspectRatio = this.width / this.height; - //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. + // High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. Chart.helpers.retinaScale(this); + // Always bind this so that if the responsive state changes we still work + var _this = this; + Chart.helpers.addResizeListener(context.canvas.parentNode, function() { + if (config.options.responsive) { + _this.controller.resize(); + } + }); + if (config) { this.controller = new Chart.Controller(this); return this.controller;