FlexChart 101

入门

在KnockoutJS应用中开始使用FlexChart的步骤:

  1. 添加对KnockoutJS, Wijmo和Wijmo的KnockoutJS绑定的引用。
  2. 添加一个视图模型提供数据和逻辑。
  3. 向当前页面添加一个Wijmo FlexChart控件,并将它绑定到你的数据。
  4. (可选)添加一些CSS来自定义输入控件的外观。
<html> <head> <link rel="stylesheet" type="text/css" href="css/bootstrap.css"/> <link rel="stylesheet" type="text/css" href="css/wijmo.css" /> <link rel="stylesheet" href="styles/app.css" /> <script src="scripts/knockout.js" type="text/javascript"></script> <script src="scripts/wijmo.js" type="text/javascript"></script> <script src="scripts/wijmo.input.js" type="text/javascript"></script> <script src="scripts/wijmo.chart.js" type="text/javascript"></script> <script src="scripts/wijmo.knockout.js" type="text/javascript"></script> <script src="scripts/bindings/appBindings.js"></script> <script src="scripts/app.js"></script> <script src="scripts/viewmodels/appVM.js"></script> </head> <body> <!-- this is the chart --> <div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country' }"> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' }"></div> </div> </body> </html>
// create and apply application view model function viewModel1() { // generate some random data var countries = 'US,Germany,UK,Japan,Italy,Greece'.split(','), data = []; for (var i = 0; i < countries.length; i++) { data.push({ country: countries[i], downloads: Math.round(Math.random() * 20000), sales: Math.random() * 10000, expenses: Math.random() * 5000 }); } // add data array to scope this.data = data; }; (function () { ko.applyBindings(new viewModel1()); })();
/* set default chart style */ .wj-flexchart { height: 400px; background-color: white; box-shadow: 4px 4px 10px 0px rgba(50, 50, 50, 0.75); padding: 8px; margin-bottom: 12px; }

Result (live):

图表类型

FlexChart控件有三个属性允许你来自定义图表的类型:

  1. chartType: 对于所有系列对象选择默认的图表类型。个别系列对象可以覆盖它。
  2. stacking: 确定系列对象是否独立地绘制,堆积或百分比堆积。
  3. rotated: 翻转X轴和Y轴,导致X变为垂直,Y变为水平。

以下这个样例让你看见当你改变这些属性的时候发生了什么:

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country', chartType: chartProps.chartType, stacking: chartProps.stacking, rotated: chartProps.rotated}"> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' }"></div> </div> <div data-bind="wjMenu: { value: chartProps.chartType, header: 'Chart Type' }"> <span data-bind="wjMenuItem: { value: 'Column' }">Column</span> <span data-bind="wjMenuItem: { value: 'Bar' }">Bar</span> <span data-bind="wjMenuItem: { value: 'Scatter' }">Scatter</span> <span data-bind="wjMenuItem: { value: 'Line' }">Line</span> <span data-bind="wjMenuItem: { value: 'LineSymbols' }">LineSymbols</span> <span data-bind="wjMenuItem: { value: 'Area' }">Area</span> <span data-bind="wjMenuItem: { value: 'Spline' }">Spline</span> <span data-bind="wjMenuItem: { value: 'SplineSymbols' }">SplineSymbols</span> <span data-bind="wjMenuItem: { value: 'SplineArea' }">SplineArea</span> </div> <div data-bind="wjMenu: { value: chartProps.stacking, header: 'Stacking' }"> <span data-bind="wjMenuItem: { value: 'None' }">None</span> <span data-bind="wjMenuItem: { value: 'Stacked' }">Stacked</span> <span data-bind="wjMenuItem: { value: 'Stacked100pc' }">Stacked 100%</span> </div> <div data-bind="wjMenu: { value: chartProps.rotated, header: 'Rotated' }"> <span data-bind="wjMenuItem: { value: false }">False</span> <span data-bind="wjMenuItem: { value: true }">True</span> </div>
// add chart properties to view model this.chartProps = { chartType: ko.observable('Column'), stacking: ko.observable('None'), rotated: ko.observable(false), ............ };

Result (live):

Column Bar Scatter Line LineSymbols Area Spline SplineSymbols SplineArea
None Stacked Stacked 100%
False True

混合图表类型

你可以对每个图表系列使用不同的图表类型,这是通过对它这个系列本身设置chartType属性。 这会覆盖图表的默认图表类型。

在以下的样例,这个图表的chartType属性被设为Column, 但是Downloads系列使用了LineAndSymbol类型覆盖它:

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country', chartType: 'Column' }"> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads', chartType: 'LineSymbols' }"></div> </div>

Result (live):

图例和标题

使用legend属性来自定义图表图例的外观, 使用headerfooter和坐标的title属性来向你的图表添加标题。

你可以使用CSS来确定图例和标题的样式。下面的CSS标签显示用于自定义图例和标题的外观的规则。 注意它们是SVG元素,所以你必须使用”fill”这样的CSS属性而不是”color”。

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country', header: chartProps.header, footer: chartProps.footer }"> <div data-bind="wjFlexChartLegend : { position: chartProps.legendPosition }"></div> <div data-bind="wjFlexChartAxis: { wjProperty: 'axisX', title: chartProps.titleX }"></div> <div data-bind="wjFlexChartAxis: { wjProperty: 'axisY', title: chartProps.titleY }"></div> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' }"></div> </div> <dl class="dl-horizontal"> <dt>Header</dt><dd><input data-bind="value: chartProps.header, valueUpdate: 'input'" class="form-control"/></dd> <dt>Footer</dt><dd><input data-bind="value: chartProps.footer, valueUpdate: 'input'" class="form-control"/></dd> <dt>X-Axis Title</dt><dd><input data-bind="value: chartProps.titleX, valueUpdate: 'input'" class="form-control"/></dd> <dt>Y-Axis Title</dt><dd><input data-bind="value: chartProps.titleY, valueUpdate: 'input'" class="form-control"/></dd> <dt></dt> <dd> <div data-bind="wjMenu: { value: chartProps.legendPosition, header: 'Legend' }"> <span data-bind="wjMenuItem: { value: 'None' }">None</span> <span data-bind="wjMenuItem: { value: 'Left' }">Left</span> <span data-bind="wjMenuItem: { value: 'Top' }">Top</span> <span data-bind="wjMenuItem: { value: 'Right' }">Right</span> <span data-bind="wjMenuItem: { value: 'Bottom' }">Bottom</span> </div> </dd> </dl>
this.chartProps = { chartType: ko.observable('Column'), stacking: ko.observable('None'), legendPosition: ko.observable('Right'), rotated: ko.observable(false), header: ko.observable('Sample Chart'), footer: ko.observable('copyright (c) ComponentOne'), titleX: ko.observable('country'), titleY: ko.observable('amount'), };
.wj-flexchart .wj-title { font-weight: bold; } .wj-flexchart .wj-header .wj-title { font-size: 18pt; fill: #80044d; } .wj-flexchart .wj-footer .wj-title { fill: #80044d; } .wj-flexchart .wj-axis-x .wj-title, .wj-flexchart .wj-axis-y .wj-title { font-style: italic; }

Result (live):

Header
Footer
X-Axis Title
Y-Axis Title
None Left Top Right Bottom

工具提示

FlexChart对工具提示有内置的支持。 默认情况下,当用户触摸或者悬停鼠标到一个数据点时,控件会显示工具提示。

工具提示的内容是使用一个可能包含下列参数的模板生成:

默认情况下,tooltip模板被设为<b>{seriesName}</b><br/>{x} {y},你可以在上面的图表看到它是如何工作的。 在这个样例中,我们设tooltip模板为<b>{seriesName}</b> <img src='resources/{x}.png'/><br/>{y}, 它使用国家的国旗代替了国家的名字。

你可以通过设置模板为空字符串来禁用图表tooltips。

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country', tooltipContent: '<img src="resources/{x}.png"/> <b>{seriesName}</b><br/>{y}' }"> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' }"></div> </div>

Result (live):

样式系列

FlexChart为每个基于默认调色板的系列自动选取颜色,你可以通过设置palette属性来重写它。 但你也可以重写默认的属性,通过设置任意一个系列的style属性为一个对象, 它制定了SVG样式属性,包括fill, stroke, strokeThickness等等。

Series.style属性对在Wijmo中通过CSS设置样式的一般规则来说,是一个例外。 这个例外反映一个事实,许多图表有动态系列,这是不可能提前布置样式的。 比如,一个股票图表可能会展示用户在运行程序时才选中的系列。

这个样例中的图表使用了stylesymbolStyle属性来为每个系列选择样式属性:

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country' }"> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales', style: {fill:'green', stroke:'darkgreen', 'stroke-width': '1'} }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses', style: {fill:'red', stroke:'darkred', 'stroke-width': '1'} }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' , chartType: 'LineSymbols', style: { stroke:'orange', 'stroke-width': '5'}, symbolStyle: {fill:'gold', stroke:'gold' } }"></div> </div>

Result (live):

自定义轴

使用axis属性来自定义图表的坐标轴,包括范围(最小值和最大值),便签格式,刻度间隔和网格线。

Axis类有布尔属性,允许你打开或关闭功能(如axisLine, labels, majorTickMarksmajorGrid)。 你可以使用CSS来设置这些已经打开的功能的样式。

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country' }"> <div data-bind="wjFlexChartAxis: { wjProperty: 'axisX', axisLine: true, majorGrid: true }"></div> <div data-bind="wjFlexChartAxis: { wjProperty: 'axisY', format: 'c0', max: 10000, majorUnit: 2000, axisLine: true, majorGrid: true }"></div> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> </div>

Result (live):

主题

FlexChart的外观是在CSS中定义的。除了默认的主题,我们有十几个专业设计的主题。 它们定制了所有Wijmo控件的外观来达到一致的,有吸引力的效果。

为了自定义图表的外观,检查你想要提供样式的元素并创建一下CSS规则来应用到这些元素中。

例如,如果你在IE或者谷歌浏览器上右击X轴上的一个标签,你会发现它是一个拥有”wj-label”类的元素, 它被包含在拥有”wj-flexchart”类的顶层控件元素中。这个样例中第一条CSS规则使用这条信息来自定义X标签。 规则选择器添加了额外的要求,父类元素必须拥有"wj-flexchart"类"custom-flex-chart"类。 如果没有的话,这个规则会用于这个页面所有的图表。

<div class="custom-flex-chart" data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country' }"> <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' }"></div> </div>
/* custom chart theme */ .custom-flex-chart.wj-flexchart .wj-axis-x .wj-label { font-family: Courier New, Courier, monospace; font-weight: bold; } .custom-flex-chart.wj-flexchart .wj-legend .wj-label { font-family: Courier New, Courier, monospace; font-weight: bold; } .custom-flex-chart.wj-flexchart .wj-legend > rect { fill: #f8f8f8; stroke: #c0c0c0; } .custom-flex-chart.wj-flexchart .wj-plot-area > rect { fill: #f8f8f8; stroke: #c0c0c0; }

Result (live):

选择模式

FlexChart允许你通过单击或者触摸选择系列或数据点。使用selectionMode属性来指定是否允许选择系列, 是否选择数据点或者无法选择(选择默认是关闭的)

设置selectionMode属性为Series或者Point会导致用户在单击鼠标的时候, FlexChart自动更新Selection属性, 并且将"wj-state-selected"类应用到选中的图表元素中。

Selection属性返回当前选中的系列。要得到当前选中的数据点, 得到当前选定的项并在选中的系列中使用Series.collectionView.currentItem属性,正如样例所示。

<div data-bind="wjFlexChart: { itemsSource: data, bindingX: 'country', tooltipContent: '', chartType: chartProps.chartType, selectionMode: chartProps.selectionMode, selection: chartProps.selection, selectionChanged: selectionChangedEH }"< <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales' }"<</div< <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses' }"<</div< <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads' }"<</div< </div< <div data-bind="wjMenu: { value: chartProps.selectionMode, header: 'Selection Mode' }"< <span data-bind="wjMenuItem: { value: 'None' }"<None</span< <span data-bind="wjMenuItem: { value: 'Series' }"<Series</span< <span data-bind="wjMenuItem: { value: 'Point' }"<Point</span< </div< <div data-bind="wjMenu: { value: chartProps.chartType, header: 'Chart Type' }"< <span data-bind="wjMenuItem: { value: 'Column' }"<Column</span< <span data-bind="wjMenuItem: { value: 'Bar' }"<Bar</span< <span data-bind="wjMenuItem: { value: 'Scatter' }"<Scatter</span< <span data-bind="wjMenuItem: { value: 'Line' }"<Line</span< <span data-bind="wjMenuItem: { value: 'LineSymbols' }"<LineSymbols</span< <span data-bind="wjMenuItem: { value: 'Area' }"<Area</span< <span data-bind="wjMenuItem: { value: 'Spline' }"<Spline</span< <span data-bind="wjMenuItem: { value: 'SplineSymbols' }"<SplineSymbols</span< <span data-bind="wjMenuItem: { value: 'SplineArea' }"<SplineArea</span< </div< <div data-bind="if: chartProps.selectionMode() != 'None' && chartProps.selection()"< <h4< Current Selection</h4< <p< Series: <b<<span data-bind="text: chartProps.selection().name"<</span<</b<</p< <div data-bind="ifnot: chartProps.selectionMode() != 'Point' || chartProps.selectionPoint() == null"< <dl class="dl-horizontal" data-bind="with: chartProps.selectionPoint"< <dt<Country</dt<<dd data-bind="text: country" <</dd< <dt<Sales</dt<<dd data-bind="text: $parent.format(sales, 'n2')"<</dd< <dt<Expenses</dt<<dd data-bind="text: $parent.format(expenses, 'n2')"<</dd< <dt<Downloads</dt<<dd data-bind="text: $parent.format(downloads, 'n0')"<</dd< </dl< </div< </div<
this.chartProps = { chartType: ko.observable('Column'), stacking: ko.observable('None'), legendPosition: ko.observable('Right'), rotated: ko.observable(false), header: ko.observable('Sample Chart'), footer: ko.observable('copyright (c) ComponentOne'), titleX: ko.observable('country'), titleY: ko.observable('amount'), selectionMode: ko.observable('Series'), selection: ko.observable(null), selectionPoint: ko.observable() }; // update the selectionPoint observable on selection change this.selectionChangedEH = function (data, sender, args) { var curSel = sender.selection; self.chartProps.selectionPoint(curSel && curSel.collectionView.currentItem); }

Result (live):

None Series Point
Column Bar Scatter Line LineSymbols Area Spline SplineSymbols SplineArea

Current Selection

Series:

Country
Sales
Expenses
Downloads

切换系列

Series类有一个visibility属性,让你决定一个系列是否应该展现在图表和图例中, 或者只在图例中,或者完全隐藏。

这个样例演示你应该如何使用visibility属性来通过两种方法切换系列的可见性:

  1. 单击图例入口:
    图表会设置图表的option.legendToggle属性为true, 当它的图例入口被单击的时候就会切换一个系列的visibility属性。
  2. 使用checkbox:
    页面使用Knockout可写的计算观测值绑定输入控件到绑定到每个系列visibility属性的观测值, 并且在布尔值和SeriesVisibility值之间转换。
<div data-bind="wjFlexChart: { itemsSource: data, legendToggle: true }"< <div data-bind="wjFlexChartSeries: { name: 'Sales', binding: 'sales', visibility: visibility1 }"<</div< <div data-bind="wjFlexChartSeries: { name: 'Expenses', binding: 'expenses', visibility: visibility2 }"<</div< <div data-bind="wjFlexChartSeries: { name: 'Downloads', binding: 'downloads', visibility: visibility3 }"<</div< </div< <!-- toggle series with checkboxes --< Sales <input type="checkbox" data-bind="checked: ko.computed(null, visibility1, visibilityToBool)"/<<br /< Expenses <input type="checkbox" data-bind="checked: ko.computed(null, visibility2, visibilityToBool)"/<<br /< Downloads <input type="checkbox" data-bind="checked: ko.computed(null, visibility3, visibilityToBool)"/<<br /<
this.visibility1 = ko.observable(); this.visibility2 = ko.observable(); this.visibility3 = ko.observable(); // SeriesVisibility-to-boolean writable computed observable. 'this' references a source SeriesVisibility observable. this.visibilityToBool = { read: function () { var vis = this(); return vis === wijmo.chart.SeriesVisibility.Visible || vis === wijmo.chart.SeriesVisibility.Plot; }, write: function (value) { this(value ? wijmo.chart.SeriesVisibility.Visible : wijmo.chart.SeriesVisibility.Legend); } }

Result (live):

Sales
Expenses
Downloads

动态图表

FlexChart内部使用了ICollectionView,因此你对数据源做的任何更改都会自动反映在图表中。

在这个样例中,我们使用一个计时器来向数据源中增加项目,丢弃旧项目以保持总数为200。 结果就是一个动态的图表,当新数据到来时图表向右滚动。

<div data-bind="wjFlexChart: { itemsSource: trafficData, chartType: 'Area', stacking: 'Stacked', bindingX: 'time' }"> <div data-bind="wjFlexChartAxis: { wjProperty: 'axisX', format: 'mm:ss' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Trucks', binding: 'trucks' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Ships', binding: 'ships' }"></div> <div data-bind="wjFlexChartSeries: { name: 'Planes', binding: 'planes' }"></div> </div> <dl class="dl-horizontal"> <dt>Update Speed</dt> <dd> <div class="btn-group"> <button type="button" class="btn btn-default" data-bind="click: setInterval.bind($data, 200)">Slow</button> <button type="button" class="btn btn-default" data-bind="click: setInterval.bind($data, 100)">Medium</button> <button type="button" class="btn btn-default" data-bind="click: setInterval.bind($data, 50)">Fast</button> <button type="button" class="btn btn-default" data-bind="click: setInterval.bind($data, 0)">Stop</button> </div> </dd> </dl>
var toAddData; this.trafficData = new wijmo.collections.ObservableArray(); this.setInterval = function (interval) { if (toAddData) { clearTimeout(toAddData); toAddData = null; } self.interval = interval; if (interval) { toAddData = setTimeout(addTrafficItem); } }; this.setInterval(500); function addTrafficItem() { // add random data, limit array length ... // keep adding ... }

Result (live):

Update Speed