垂直显示嵌入的组合图表

Display vertically an embedded combo chart

我正在尝试使用 EmbeddedComboChartBuilder 显示组合成一条线的条形图。 为此,我使用了以下代码和数据:

function createChartSerie() {
    
    var ss = SpreadsheetApp.openById('XXX...');
    var sheet = ss.getSheetByName("sheet1");
    
    var chartTableXRange = sheet.getRange("A1:A24");
    var chartTableYRange = sheet.getRange("B1:C24");
      
    var chart = sheet.newChart()
      .setOption('useFirstColumnAsDomain', true)
      .setOption('hAxis', {
          title: 'X',
          format: '#,##'
        })
        .setOption('vAxes', {0: {
          title: 'Y', format: '#,##'
        }})
        .setChartType(Charts.ChartType.COMBO)
        .addRange(chartTableXRange) // x axis
        .addRange(chartTableYRange) //first y data
        .setPosition(5, 5, 0, 0)
        .setOption('orientation', 'vertical')
        .setOption('series', {
        0: {
          type: 'line', 
          color: 'red'
        },
        1: {
          type: 'bars', 
          color: 'blue'
        }
      })
        .build();
    
    sheet.insertChart(chart);
    }

用于显示组合图的数据范围:

A   B   C
1   0.0001  0
1.5 0.0009  0.0006
2   0.0044  0.0037
2.5 0.0175  0.0133
3   0.054   0.0236
3.5 0.1296  0.0533
4   0.242   0.0073
4.5 0.3522  0.2468
5   0.399   0.0843
5.5 0.3522  0.3352
6   0.242   0.2201
6.5 0.1296  0.0607
7   0.054   0.0256
7.5 0.0175  0.0006
8   0.0044  0.003
8.5 0.0009  0.0005
9   0.0001  0.0001

似乎选项.setOption('orientation', 'vertical')没有效果,如下图所示:

此选项在与 google 可视化一起使用时效果很好,就像您在 this JSFiddle 中看到的那样。

回答

很遗憾 Sheets API 不支持此选项 (see the embedded charts resource)。

好像有一个feature request in Google’s Issue Tracker。您可以单击白色开始 (☆),以便 Google 知道您希望完成此操作。

解决方法

有 2 种解决方法:在网络上使用 Google Charts 本身(与 WebApps 和模态一起使用),并使用 Google Charts 生成图表,将页面上的 SVG 转换为 PNG 和将其嵌入到电子表格中。生成 PNG 需要制作模态,因此此设置说明适用于两种解决方法。

第 1 步:制作一个使用 Google Charts 制作图表的页面

获取一些模拟数据并制作图表,根据自己的喜好进行设计,并确保一切正常。

第 2 步:使用模式显示它

添加一个名为 Chart 的新 HTML 文件(您可以更改名称,但需要在代码中进行更改)并粘贴您制作的代码。

有了模拟图表后,我们需要使用模式来显示它。我们首先需要在电子表格中添加一个菜单:

function onOpen() {
 SpreadsheetApp.getUi()
   .createMenu('Chart generator')
   .addItem('Create and insert chart', 'showChart')
   .addToUi()
}

然后我们添加显示模态的函数:

function showChart() {
 const html = HtmlService.createHtmlOutputFromFile('Chart')
   .setWidth(900)
   .setHeight(600)
 
 SpreadsheetApp.getUi().showModalDialog(html, 'Chart')
}

您还可以使用相同的模板设置 WebApp:

function doGet(e) {
 return HtmlService.createHtmlOutputFromFile('Chart')
  .setWidth(900)
  .setHeight(600)
}

第 3 步:从电子表格中获取数据

现在是删除模拟数据并获取实际数据的时候了。为此,我们将使用 google.script.run 函数异步加载数据。此示例使用异步逻辑,因此它可以并行加载图表库和数据。

google.charts.load('current', {'packages':['corechart']})
const readyPromise = toAsync(resolve => google.charts.setOnLoadCallback(resolve))
const dataPromise = toAsync((resolve, reject) => {
 google.script.run
   .withSuccessHandler(resolve)
   .withFailureHandler(reject)
   .getData()
})

toAsync 是一个辅助函数,它确保在出现失败回调未处理的错误时拒绝:

function toAsync(callback) {
 return new Promise((resolve, reject) => {
   try {
     callback(resolve, reject)
   } catch(e) {
     reject(e)
   }
 })
}

然后我们需要在 Google Apps 脚本中添加实际从电子表格中读取数据的函数:

function getData() {
 return SpreadsheetApp.getActiveSpreadsheet()
   .getSheetByName('Sheet1')
   .getRange('A1:C18')
   .getValues()
}

然后在主函数中简单地使用两者:

async function drawVisualization() {
 const el = document.getElementById('chart_div')
 
 await readyPromise
 const data = google.visualization.arrayToDataTable(await dataPromise)
 const options = { … }
 new google.visualization.ComboChart(el).draw(data, options)
}

确保调用处理承诺拒绝,否则您可能会在不知不觉中消除错误:

document.addEventListener('DOMContentLoaded', () => drawVisualization().catch(console.error))

(可选)第 4 步:将图表嵌入为 PNG

Google 图表生成 SVG。不幸的是,我们无法嵌入 SVG,因此我们需要将其转换为 PNG。最简单的方法是使用像 canvg 这样的外部库,它允许我们将 SVG 绘制成 canvas。然后我们可以将 canvas 导出为 PNG。

将 canvas 添加到您的 body 并将图书馆添加到您的头上。

然后您可以使用(附加到 drawVisualization)在 canvas 上绘制:

const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
v = canvg.Canvg.fromString(ctx, el.getElementsByTagName('svg')[0].outerHTML)
v.start()
await v.ready()

这将绘制它并等待它完成。

现在我们需要一种将生成的图像发送到 GAS 以便它可以嵌入的方法。值得庆幸的是,我们可以获得 PNG 的 base64 并将其发送到那里:

const base64 = canvas.toDataURL('image/png').split(';base64,')[1]
 
await toAsync((resolve, reject) => {
 google.script.run
   .withSuccessHandler(resolve)
   .withFailureHandler(reject)
   .insertChart(base64)
})

并且在 Google Apps 脚本中,我们定义 insertChart:

function insertChart(base64png) {
 const blob = Utilities.newBlob(
   Utilities.base64Decode(base64png,  Utilities.Charset.US_ASCII),
   'image/png',
   'chart.png'
 )
 
 SpreadsheetApp.getActiveSpreadsheet()
   .getSheetByName('Sheet1')
   .insertImage(blob, 5, 5, 0, 0)
}

(可选)第 5 步:在生成图表时关闭模态并隐藏图表

既然插入了,我喜欢自动关闭模态框。为此,只需将其附加到 drawVisualization:

google.script.host.close()

我也喜欢在处理时不显示图表。一点 CSS 就足够了:

canvas, #chart_div {
 visibility: hidden;
}
body {
 overflow: hidden;
}

您不能使用 display: none,因为那样会生成错误大小的图表。设置溢出是为了避免滚动条变成空

您还可以将屏幕标题甚至加载动画更改为模态。

参考资料