在隐藏的引导选项卡窗格上重绘Google图表

我在我的网络应用程序中使用谷歌散点图,它加载文件就绪。 我最近做了一些重组,并尝试在不同的引导选项卡中划分页面内容。

如果我在活动选项卡窗格上加载图表,它可以正常工作,图表的宽度,高度参数是正确的。 但是,如果我将图形移动到隐藏的选项卡,则图形无法正确加载函数传递的参数(特别是宽度和高度)。 目前我正在调用文档图表。

这是我的代码

HTML

在准备好文件上加载图表

    google.load("visualization", "1", {packages: ["corechart"]}); $(document).ready(function () { $('#myMoodPage').myMood({ graphDataUrl: 'url('my_mood/mood_graph_data'); ?>', graphData: , titles: ['translate('time'); ?>', 'translate('mood'); ?>', 'translate('avg'); ?>'] }); }); function updatePageCharts() { $('#myMoodPage').myMood('loadGraph'); } $("a[href='#engagement-graph']").on('shown.bs.tab', function (e) { google.load('visualization', '1', { packages: ['corechart'], callback: drawChart }); });  

这是我的主要JS文件,我在这里定义图形参数和其他东西(这个问题实际上并不相关,但为了完整性我只发布了整个函数)

 (function ($) { $.widget("ui.myMood", { options: { graphDataUrl: "", graphData: {}, titles: ["time", "mood", "avg"] }, _create: function () { var self = this; this.periodSelect = this.element.find('select[name="period"]'); this.questionSelect = this.element.find('select[name="question"]'); this.graphContainer = this.element.find(".statistics"); this.atStart = this.element.find("#atStart .value"); this.highest = this.element.find("#highest .value"); this.lowest = this.element.find("#lowest .value"); this.current = this.element.find("#current .value"); this.last30 = this.element.find("#last30 .value"); this.last30Trend = this.element.find("#last30"); this.week = this.element.find("#week .value"); this.weekTrend = this.element.find("#week"); this.graphId = "graph"; this.selectedFilterName = this.options.titles[2]; this.periodSelect.change(function () { self.loadGraph() }); this.questionSelect.change(function () { self.loadGraph(); self.selectedFilterName = self.questionSelect.find("option:selected").text() }); this.setGraphData(this.options.graphData); return this }, loadGraph: function () { var self = this; var data = { period: self.periodSelect.val(), question: self.questionSelect.val() }; var success = function (data) { self.setGraphData(data) }; $.ajax({ type: "POST", url: self.options.graphDataUrl, data: data, success: success }) }, setGraphData: function (data) { var self = this; this.atStart.text(data.atStart); this.highest.text(data.highest); this.lowest.text(data.lowest); this.current.text(data.current); this.last30.text(data.last30.val); this.last30Trend.find(".up, .down").hide(); this.last30Trend.removeClass("incr"); this.last30Trend.removeClass("decr"); if (Number(data.last30.trend) > 0) { this.last30Trend.find(".up").show(); this.last30Trend.addClass("incr") } if (Number(data.last30.trend)  0) { this.weekTrend.find(".up").show(); this.weekTrend.addClass("incr") } if (Number(data.week.trend) < 0) { this.weekTrend.find(".down").show(); this.weekTrend.addClass("decr") } var moodData = data.mood; var dataArray = new Array(); for (i in moodData) { if (moodData[i].avg == null) { moodData[i].avg = undefined } if (moodData[i].mood == null) { moodData[i].mood = undefined } if (moodData[i].team == null) { moodData[i].team = undefined } var moodText = '
'; if (moodData[i].event != null) { moodText += moodData[i].event + " " } moodText += moodData[i].mood; if (moodData[i].comment) { moodText += "
" + moodData[i].comment } moodText += "
"; var avgText = '
'; if (moodData[i].event != null) { avgText += moodData[i].event + " " } avgText += moodData[i].avg; if (moodData[i].publicComment) { avgText += "
" + moodData[i].publicComment } avgText += "
"; var row = [new Date(moodData[i].time * 1000), Number(moodData[i].mood), moodText, Number(moodData[i].avg), avgText, false]; dataArray.push(row) } if (google) { drawChart() } else { google.setOnLoadCallback(drawChart) } function drawChart() { var dataTable = new google.visualization.DataTable(); dataTable.addColumn("date", self.options.titles[0]); dataTable.addColumn("number", self.options.titles[1]); dataTable.addColumn({ type: "string", role: "tooltip", p: { html: true } }); dataTable.addColumn("number", self.selectedFilterName); dataTable.addColumn({ type: "string", role: "tooltip", p: { html: true } }); dataTable.addColumn({ type: "boolean", role: "certainty" }); dataTable.addRows(dataArray); var dataView = new google.visualization.DataView(dataTable); var chart = new google.visualization.ScatterChart(document.getElementById(self.graphId)); var options = { legend: { position: "bottom" }, interpolateNulls: true, chartArea: { left: 25, top: 25, width: '92%', height: '80%' }, backgroundColor: "#ffffff", vAxis: { minValue: 0, maxValue: 6, viewWindow: { min: 0, max: 6 } }, tooltip: { isHtml: true }, series: { 0: { color: "#9c3b8a", lineWidth: 2, pointSize: 6 }, 1: { color: "#15426c", lineWidth: 1, pointSize: 3 } } }; chart.draw(dataView, options) } } }) })(jQuery);

我尝试像这样重新加载图形,但它没有用

 $("a[href='#engagement-graph']").on('shown.bs.tab', function (e) { google.load('visualization', '1', { packages: ['corechart'], callback: drawChart }); }); 

屏幕截图2种不同的情况

在活动选项卡窗格上加载 在此处输入图像描述

加载隐藏的选项卡窗格 在此处输入图像描述

在隐藏div中绘制图表(通常是实现哪个选项卡界面)会破坏Visualization API中的维度检测算法,这就是为什么图表在绘制时除了在开头打开的选项卡之外的任何选项卡中绘制时都会奇怪。

您只能加载一次API; 随后对google.load的Visualization API调用将被忽略,这就是为什么你的“shown.bs.tab”事件处理程序没有做你想要的。

由于延迟选项卡初始化直到图表绘制后并不是Bootstrap的可行选项,我建议替换此代码:

 google.load("visualization", "1", {packages: ["corechart"]}); $("a[href='#engagement-graph']").on('shown.bs.tab', function (e) { google.load('visualization', '1', { packages: ['corechart'], callback: drawChart }); }); 

有了这个:

 function init () { if (/* boolean expression teting if chart tab is open */) { drawChart(); } else { $("a[href='#engagement-graph']").one('shown.bs.tab', drawChart); } } google.load("visualization", "1", {packages: ["corechart"], callback: init}); 

如果其容器选项卡处于打开状态,则应绘制该图表,并创建一次性事件处理程序,如果选项卡未打开,则在第一次打开选项卡时绘制图表。

您需要将此代码放在setGraphData函数中以避免数据加载的竞争条件,因此drawChart函数将在范围内。 另外,删除这些行:

 if (google) { drawChart() } else { google.setOnLoadCallback(drawChart) } 

只要jsapi的脚本标记加载, google对象就会存在,这将在执行此代码之前,因此您将始终在此处触发drawChart函数。 但是,将google对象提供并不意味着加载了可视化API。

只是因为其他人正在寻找一个更简单的解决方案。

另一种方法是实现以下内容:

 $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { var ev = document.createEvent('Event'); ev.initEvent('resize', true, true); window.dispatchEvent(ev); }); 

这将导致图表重新绘制,就像调整浏览器大小一样。