图表基础应用

SpreadJS 提供强大的图表功能,支持 28 种图表类型,包括柱状图、折线图、饼图等。用户可以通过简单的 API 调用快速创建图表,并对图表的标题、图例、数据标签、系列样式等进行深度自定义,实现数据的可视化呈现。

概述 本 Demo 展示了 SpreadJS 图表的基本创建、管理和自定义功能。Demo 包含三个工作表:"常规图表"展示基本的图表创建,"自定义图表"演示图表样式自定义,"表格绑定图表"展示图表与表格数据的联动。用户可以通过界面交互选择不同的图表类型,创建图表,并进行行列切换、删除等操作。 实现思路 初始化三个工作表,并准备不同类型的数据源 使用 sheet.charts.add() 方法创建图表,指定图表类型、位置、大小和数据范围 通过图表 API 自定义图表样式,包括标题、图例、数据标签、系列等元素 实现图表管理功能:添加图表、切换行列、删除图表等 处理特殊数据场景:空白单元格显示方式、#N/A 值处理、隐藏行列 代码解析 创建基本图表 这段代码使用 charts.add() 方法创建图表。参数依次为:图表名称、图表类型、x 坐标、y 坐标、宽度、高度、数据范围。数据范围支持多种格式,包括单元格范围字符串(如 "A1:H4")和表结构引用(如 "Table1[[#All],[A]]")。 自定义图表样式 图表样式自定义通过获取图表元素的配置对象,修改属性后再设置回去的方式实现。title() 方法用于获取或设置标题,dataLabels() 方法用于配置数据标签的显示内容和位置。 动态添加图表 这段代码根据用户选择的单元格范围和图表类型动态创建图表。使用 rangeToFormula 方法将选中的范围转换为公式字符串,支持多个不连续区域作为数据源。 处理空白单元格和隐藏行列 displayBlanksAs() 方法控制空白单元格的显示方式,支持三种模式:gaps(间隔)、zero(零值)、connected(连接)。ignoreHidden() 方法控制是否在图表中显示隐藏的行列数据。 运行效果 第一个工作表展示基本的簇状柱形图,数据为浏览器市场份额统计 第二个工作表展示了自定义样式的图表,包括修改标题、图例位置、数据标签格式、系列颜色等 第三个工作表展示了图表与表格数据的绑定,点击按钮可以更新表格数据,图表自动刷新 用户可以选中单元格范围,选择图表类型后点击"添加图表"按钮创建新图表 点击"切换行/列"可以交换图表的行列数据方向 通过下拉框和复选框可以控制空白单元格的显示方式和隐藏行列的处理 API 参考 charts.add() 方法 name:图表名称 chartType:图表类型,使用 GC.Spread.Sheets.Charts.ChartType 枚举 x、y:图表在工作表中的位置坐标 width、height:图表的宽度和高度 dataRange:可选,数据范围,支持字符串格式或表结构引用 dataOrientation:可选,数据方向,GC.Spread.Sheets.Charts.RowCol.rows 或 GC.Spread.Sheets.Charts.RowCol.columns colorScheme:可选,颜色方案数组 chart.switchDataOrientation() 方法 切换图表的行列数据方向,返回布尔值表示是否切换成功。 chart.displayBlanksAs() 方法 控制空白单元格的显示方式: GC.Spread.Sheets.Charts.DisplayBlanksAs.gaps:显示为间隔 GC.Spread.Sheets.Charts.DisplayBlanksAs.zero:显示为零值 GC.Spread.Sheets.Charts.DisplayBlanksAs.connected:显示为连接线 chart.ignoreHidden() 方法 设置是否忽略隐藏的行列数据,参数为布尔值。
window.onload = function () { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 3 }); initSpread(spread); _getElementById("insertChart").addEventListener('click', function () { insertChart(spread) }); _getElementById("switchRowColumn").addEventListener('click', function () { switchRowColumn(spread) }); _getElementById("removeChart").addEventListener('click', function () { removeChart(spread) }); _getElementById("removeAllCharts").addEventListener('click', function () { removeAllChart(spread) }); _getElementById("groupSelect").addEventListener('change', function () { changeTypeSelect(); }); _getElementById("displayBlanksCells").addEventListener('change', function () { displayBlanksCells(spread, this.value); }); _getElementById("showNAAsBlanks").addEventListener('click', function () { showNAAsBlanks(spread, this.checked); }); _getElementById("ignoreHidden").addEventListener('click', function () { ignoreHiddenRowAndColumn(spread, this.checked); }); changeTypeSelect(); }; var chartType = [ [{ typeDesc: '簇状柱形图', type: GC.Spread.Sheets.Charts.ChartType.columnClustered }, { typeDesc: '堆积柱形图', type: GC.Spread.Sheets.Charts.ChartType.columnStacked }, { typeDesc: '百分比堆积柱形图', type: GC.Spread.Sheets.Charts.ChartType.columnStacked100 }], [{ typeDesc: '折线图', type: GC.Spread.Sheets.Charts.ChartType.line }, { typeDesc: '堆积折线图', type: GC.Spread.Sheets.Charts.ChartType.lineStacked }, { typeDesc: '百分比堆积折线图', type: GC.Spread.Sheets.Charts.ChartType.lineStacked100 }, { typeDesc: '带标记点的折线图', type: GC.Spread.Sheets.Charts.ChartType.lineMarkers }, { typeDesc: '带标记点的堆积折线图', type: GC.Spread.Sheets.Charts.ChartType.lineMarkersStacked }, { typeDesc: '带标记点的百分比堆积折线图', type: GC.Spread.Sheets.Charts.ChartType.lineMarkersStacked100 }], [{ typeDesc: '饼图', type: GC.Spread.Sheets.Charts.ChartType.pie }, { typeDesc: '圆环图', type: GC.Spread.Sheets.Charts.ChartType.doughnut }], [{ typeDesc: '簇状条形图', type: GC.Spread.Sheets.Charts.ChartType.barClustered }, { typeDesc: '堆积条形图', type: GC.Spread.Sheets.Charts.ChartType.barStacked }, { typeDesc: '百分比堆积条形图', type: GC.Spread.Sheets.Charts.ChartType.barStacked100 }], [{ typeDesc: '面积图', type: GC.Spread.Sheets.Charts.ChartType.area }, { typeDesc: '堆积面积图', type: GC.Spread.Sheets.Charts.ChartType.areaStacked }, { typeDesc: '百分比堆积面积图', type: GC.Spread.Sheets.Charts.ChartType.areaStacked100 }], [{ typeDesc: '散点图', type: GC.Spread.Sheets.Charts.ChartType.xyScatter }, { typeDesc: '带平滑线和标记点的散点图', type: GC.Spread.Sheets.Charts.ChartType.xyScatterSmooth }, { typeDesc: '带平滑线的散点图', type: GC.Spread.Sheets.Charts.ChartType.xyScatterSmoothNoMarkers }, { typeDesc: '带直线和标记点的散点图', type: GC.Spread.Sheets.Charts.ChartType.xyScatterLines }, { typeDesc: '带直线的散点图', type: GC.Spread.Sheets.Charts.ChartType.xyScatterLinesNoMarkers }, { typeDesc: '气泡图', type: GC.Spread.Sheets.Charts.ChartType.bubble }], [{ typeDesc: '高低收盘图', type: GC.Spread.Sheets.Charts.ChartType.stockHLC }, { typeDesc: '开盘高低收盘图', type: GC.Spread.Sheets.Charts.ChartType.stockOHLC }, { typeDesc: '成交量高低收盘图', type: GC.Spread.Sheets.Charts.ChartType.stockVHLC }, { typeDesc: '成交量开盘高低收盘图', type: GC.Spread.Sheets.Charts.ChartType.stockVOHLC }] ]; function initSpread(spread) { var sheet1 = spread.sheets[0]; sheet1.name("常规图表"); var sheet2 = spread.sheets[1]; sheet2.name("自定义图表"); var sheet3 = spread.sheets[2]; sheet3.name("表格绑定图表"); initSheet(sheet1); initSheet(sheet2); initBindingTable(sheet3); //add chart initChart(sheet1); initChart(sheet2); initBindingChart(sheet3); //custom chart customChartStyle(sheet2); } function initSheet(sheet) { sheet.suspendPaint(); //prepare data for chart var dataArray = [ ["", 'Chrome', 'FireFox', 'IE', 'Safari', 'Edge', 'Opera', 'Other'], ["2015", 0.5651, 0.1734, 0.1711, 0.427, 0, 0.184, 0.293], ["2016", 0.6230, 0.1531, 0.1073, 0.464, 0.311, 0.166, 0.225], ["2017", 0.6360, 0.1304, 0.834, 0.589, 0.443, 0.223, 0.246] ]; sheet.setArray(0, 0, dataArray); sheet.resumePaint(); } function initBindingTable(sheet) { sheet.suspendPaint(); var data = { name: '琼斯', region: '东部', sales: [{ orderDate: '1/6/2013', item: '铅笔', units: 95, cost: 1.99 }, { orderDate: '4/1/2013', item: '活页夹', units: 60, cost: 4.99 }, { orderDate: '6/8/2013', item: '钢笔套装', units: 16, cost: 17.99 }, { orderDate: '7/8/2013', item: '圆珠笔', units: 16, cost: 8.99 }, { orderDate: '8/8/2013', item: '毛笔', units: 16, cost: 18.99 }, { orderDate: '9/8/2013', item: '画笔', units: 16, cost: 10.99 } ] }; var tableColumns = [], names = ['orderDate', 'item', 'units', 'cost'], labels = ['订单日期', '商品', '数量', '成本']; var table = sheet.tables.add('tableRecords', 0, 0, 4, 4); table.autoGenerateColumns(false); names.forEach(function (name, index) { var tableColumn = new GC.Spread.Sheets.Tables.TableColumn(); tableColumn.name(labels[index]); tableColumn.dataField(name); tableColumns.push(tableColumn); }); table.bindColumns(tableColumns); table.bindingPath('sales'); var source = new GC.Spread.Sheets.Bindings.CellBindingSource(data); sheet.setDataSource(source); var button = sheet.shapes.addFormControl("button", GC.Spread.Sheets.Shapes.FormControlType.button, 500, 10, 150, 50); button.text("更新表格数据源"); var style = button.style(); style.textEffect.color = "rgb(0, 0, 0)"; style.textEffect.font = "bold 15px Calibri"; style.textFrame.vAlign = GC.Spread.Sheets.VerticalAlign.center; style.textFrame.hAlign = GC.Spread.Sheets.HorizontalAlign.center; button.style(style); sheet.bind(GC.Spread.Sheets.Events.FormControlButtonClicked, function (s, args) { var length = Math.floor(Math.random() * 20) + 1 data.sales = generateTestData(length, 10, 20); var source = new GC.Spread.Sheets.Bindings.CellBindingSource(data); sheet.setDataSource(source); }); sheet.resumePaint(); } function initChart(sheet) { //add common chart sheet.charts.add('Chart1', GC.Spread.Sheets.Charts.ChartType.columnClustered, 0, 100, 800, 300, "A1:H4"); } function initBindingChart(sheet) { sheet.charts.add('Chart1', GC.Spread.Sheets.Charts.ChartType.columnClustered, 250, 80, 800, 300, "tableRecords[[#Headers], [#Data], [商品]],tableRecords[[#Headers], [#Data], [数量]],tableRecords[[#Headers], [#Data], [成本]]"); } function customChartStyle(sheet) { var changeChart = sheet.charts.all()[0]; changeChartStyle(changeChart); } function changeChartStyle(chart) { //change orientation switchOrientation(chart); //change legend changeChartLegend(chart); //change chartArea changeChartArea(chart); //change chartTitle changeChartTitle(chart); //change dataLabels changeChartDataLabels(chart); //change axisTitles changeChartAxisTitles(chart); //change axesLine changeChartAxesLine(chart); //change series changeSeries(chart); //change gridLine changeGridLine(chart); //change seriesBorder changeSeriesBorder(chart); } function switchOrientation(chart) { chart.switchDataOrientation(); } function ignoreHiddenRowAndColumn(spread, value) { var activeSheet = spread.getActiveSheet(); var activeChart = getActiveChart(activeSheet); activeChart && activeChart.ignoreHidden(value); } function displayBlanksCells(spread, value) { var activeSheet = spread.getActiveSheet(); var activeChart = getActiveChart(activeSheet); var index = parseInt(value); if (index !== null && index !== undefined) { activeChart && activeChart.displayBlanksAs(index); } } function showNAAsBlanks(spread, value) { var activeSheet = spread.getActiveSheet(); var activeChart = getActiveChart(activeSheet); activeChart && activeChart.displayNaAsBlank(value); } function changeChartLegend(chart) { var legend = chart.legend(); legend.visible = true; var legendPosition = GC.Spread.Sheets.Charts.LegendPosition; legend.position = legendPosition.top; chart.legend(legend); } function changeChartArea(chart) { var chartArea = chart.chartArea(); chartArea.backColor = "rgba(93,93,93,1)"; chartArea.color = "rgba(255,255,255,1)"; chartArea.fontSize = 14; chart.chartArea(chartArea); } function changeChartTitle(chart) { var title = chart.title(); title.text = "浏览器市场份额"; title.fontSize = 18; chart.title(title); } function changeChartDataLabels(chart) { var dataLabels = chart.dataLabels(); dataLabels.showValue = true; dataLabels.showSeriesName = false; dataLabels.showCategoryName = false; dataLabels.format = "0.00%"; var dataLabelPosition = GC.Spread.Sheets.Charts.DataLabelPosition; dataLabels.position = dataLabelPosition.outsideEnd; chart.dataLabels(dataLabels); var series0 = chart.series().get(0); series0.dataLabels = { showSeriesName: true, showCategoryName: true, separator: ";", position: GC.Spread.Sheets.Charts.DataLabelPosition.Center, color: "red", backColor: "white", borderColor: "blue", borderWidth: 2 }; chart.series().set(0, series0); var series2 = chart.series().get(2); series2.dataLabels = { showSeriesName: true, separator: "/", position: GC.Spread.Sheets.Charts.DataLabelPosition.insideEnd, color: "orange", backColor: "white", borderColor: "green", borderWidth: 1 }; chart.series().set(2, series2); var series4 = chart.series().get(4); series4.dataLabels = { showCategoryName: true, separator: ":", position: GC.Spread.Sheets.Charts.DataLabelPosition.above, color: "blue", backColor: "white", borderColor: "red", borderWidth: 2.5 }; chart.series().set(4, series4); } function changeChartAxisTitles(chart) { var axes = chart.axes(); axes.primaryCategory.title.text = 'Year'; axes.primaryCategory.title.fontSize = 14; chart.axes(axes); } function changeChartAxesLine(chart) { var axes = chart.axes(); axes.primaryValue.format = "0%"; chart.axes(axes); } function changeSeries(chart) { var series = chart.series(); var seriesItem = series.get(6); seriesItem.backColor = "#a3cf62"; series.set(6, seriesItem); } function changeGridLine(chart) { var axes = chart.axes(); axes.primaryCategory.majorGridLine.visible = false; axes.primaryValue.majorGridLine.visible = false; chart.axes(axes); } function changeSeriesBorder(chart) { var series = chart.series().get(); for (var i = 0; i < series.length; i++) { var seriesItem = series[i]; seriesItem.border.color = 'rgb(255,255,255)'; seriesItem.border.width = 1; chart.series().set(i, seriesItem); } } function insertChart(spread) { var activeSheet = spread.getActiveSheet(); var dataRanges = activeSheet.getSelections(); var dataFormulas = []; if (dataRanges) { dataRanges.forEach((dataRange) => { if (!judgeIsEmptyOneCell(activeSheet, dataRange)) { var rangeToFormula = GC.Spread.Sheets.CalcEngine.rangeToFormula; dataFormulas.push(rangeToFormula(dataRange)); } }) } var groupIndex = parseInt(_getElementById('groupSelect').value); var typeIndex = parseInt(_getElementById('typeSelect').value); if (groupIndex < chartType.length) { var typeArray = chartType[groupIndex]; if (typeIndex < typeArray.length) { var type = typeArray[typeIndex].type; try { activeSheet.charts.add('', type, 30, 120, 500, 300, dataFormulas.join(','), GC.Spread.Sheets.Charts.RowCol.rows); } catch (e) { alert(e.message); } } } } function switchRowColumn(spread) { var activeSheet = spread.getActiveSheet(); var activeChart = getActiveChart(activeSheet); if (activeChart) { var isSwitched = activeChart.switchDataOrientation(); if (!isSwitched) { alert("无法切换行 / 列"); } } } function removeChart(spread) { var activeSheet = spread.getActiveSheet(); var activeChart = getActiveChart(activeSheet); if (activeChart) { activeSheet.charts.remove(activeChart.name()); } } function removeAllChart(spread) { var activeSheet = spread.getActiveSheet(); activeSheet.charts.clear(); } function getActiveChart(sheet) { var activeChart = null; sheet.charts.all().forEach(function (chart) { if (chart.isSelected()) { activeChart = chart; } }); return activeChart; } function judgeIsEmptyOneCell(sheet, range) { if (range.rowCount === 1 && range.colCount === 1) { var cell = sheet.getCell(range.row, range.col); if (!cell.text()) { return true; } } return false; } function changeTypeSelect() { var index = parseInt(_getElementById('groupSelect').value); if (index !== null && index !== undefined && index < chartType.length) { _getElementById('typeSelect').innerHTML = ''; var typeArray = chartType[index]; for (var i = 0; i < typeArray.length; i++) { var item = typeArray[i]; var option = document.createElement('option'); var value = document.createAttribute('value'); value.nodeValue = i; option.setAttributeNode(value); option.innerHTML = item.typeDesc; _getElementById('typeSelect').appendChild(option); } } } function generateTestData(length, maxUnits, maxCost) { const testData = []; const items = [ '苹果', '香蕉', '橙子', '草莓', '葡萄', '芒果', '西瓜', '菠萝', '猕猴桃', '梨', '桃子', '蓝莓', '樱桃', '柠檬', '青柠', '李子', '树莓', '黑莓', '石榴', '椰子' ]; for (var i = 0; i < length; i++) { const orderDate = new Date().toISOString().slice(0, 10); const item = items[Math.floor(Math.random() * items.length)]; const units = Math.floor(Math.random() * maxUnits) + 1; const cost = Math.floor(Math.random() * maxCost) + 1; const order = { orderDate: orderDate, item: item, units: units, cost: cost }; testData.push(order); } return testData; } function _getElementById(id) { return document.getElementById(id); }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta name="spreadjs culture" content="zh-cn" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div class="sample-tutorial"> <div id="ss" class="sample-spreadsheets"></div> <div class="options-container"> <p>高亮显示一系列单元格,选择一个图表组和图表类型,然后点击“添加图表”将图表添加到工作表中。 使用“切换行/列”来交换坐标轴,或者使用“移除图表”或“移除所有图表”来移除图表。 </p> <div class="option-row"> <label>组:</label> <select id="groupSelect" style="width: 160px"> <option value="0" selected="selected">柱状图</option> <option value="1">折线图</option> <option value="2">饼图</option> <option value="3">条形图</option> <option value="4">面积图</option> <option value="5">散点图</option> <option value="6">股票图</option> </select> </div> <div class="option-row"> <label>类型:</label> <select id="typeSelect" style="width: 160px"></select> </div> <div class="option-row"> <input type="button" style="width: 150px;margin:5px ;margin-bottom: 15px;" value="添加图表" id="insertChart" /> <input type="button" style="width: 150px;margin:5px" value="切换行/列" id="switchRowColumn" /> <input type="button" style="width: 150px;margin:5px" value="移除图表" id="removeChart" /> <input type="button" style="width: 150px;margin:5px" value="移除所有图表" id="removeAllCharts" /> </div> <div class="option-row"> <label>将空白单元格显示为:</label> <select id="displayBlanksCells" style="width: 80px"> <option value="1" selected="selected">间隔</option> <option value="2">零</option> <option value="0">连接</option> </select> </div> <div class="option-row"> <input type="checkbox" id="showNAAsBlanks"></input> <label for="showNAAsBlanks">将 #N/A 单元格显示为空白单元格</label> </div> <div class="option-row"> <input type="checkbox" id="ignoreHidden" checked></input> <label for="ignoreHidden">忽略隐藏的行和列</label> </div> </div> </div> </body> </html>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 280px); height: 100%; overflow: hidden; float: left; } .options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .option-row { font-size: 14px; padding: 2px; margin-top: 2px; } .sample-options { z-index: 1000; } label { margin-bottom: 6px; } p{ padding:2px 10px; background-color:#F4F8EB; } input { padding: 2px 6px; } input[type=button] { margin-top: 6px; display: block; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }