提交 ddab25f6 编写于 作者: S sushuang

support auto sampling in progressive, which makes the incremental graphic...

support auto sampling in progressive, which makes the incremental graphic elements uniformly distributed.
上级 3ebcdadd
...@@ -60,6 +60,7 @@ var CandlestickSeries = SeriesModel.extend({ ...@@ -60,6 +60,7 @@ var CandlestickSeries = SeriesModel.extend({
progressive: 5e3, progressive: 5e3,
progressiveThreshold: 1e4, progressiveThreshold: 1e4,
progressiveChunkMode: 'mod',
animationUpdate: false, animationUpdate: false,
animationEasing: 'linear', animationEasing: 'linear',
......
...@@ -45,7 +45,6 @@ var CandlestickView = ChartView.extend({ ...@@ -45,7 +45,6 @@ var CandlestickView = ChartView.extend({
}, },
_renderNormal: function (seriesModel) { _renderNormal: function (seriesModel) {
// var largePoints = data.getLayout('largePoints');
var data = seriesModel.getData(); var data = seriesModel.getData();
var oldData = this._data; var oldData = this._data;
var group = this.group; var group = this.group;
...@@ -115,7 +114,8 @@ var CandlestickView = ChartView.extend({ ...@@ -115,7 +114,8 @@ var CandlestickView = ChartView.extend({
var data = seriesModel.getData(); var data = seriesModel.getData();
var isSimpleBox = data.getLayout('isSimpleBox'); var isSimpleBox = data.getLayout('isSimpleBox');
for (var dataIndex = params.start; dataIndex < params.end; dataIndex++) { var dataIndex;
while ((dataIndex = params.next()) != null) {
var el; var el;
var itemLayout = data.getItemLayout(dataIndex); var itemLayout = data.getItemLayout(dataIndex);
......
...@@ -42,8 +42,8 @@ export default { ...@@ -42,8 +42,8 @@ export default {
}; };
function normalProgress(params, data) { function normalProgress(params, data) {
var dataIndex;
for (var dataIndex = params.start; dataIndex < params.end; dataIndex++) { while ((dataIndex = params.next()) != null) {
var axisDimVal = data.get(cDim, dataIndex); var axisDimVal = data.get(cDim, dataIndex);
var openVal = data.get(openDim, dataIndex); var openVal = data.get(openDim, dataIndex);
...@@ -126,15 +126,15 @@ export default { ...@@ -126,15 +126,15 @@ export default {
} }
function largeProgress(params, data) { function largeProgress(params, data) {
var segCount = params.end - params.start;
// Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...] // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]
var points = new LargeArr(segCount * 5); var points = new LargeArr(params.count * 5);
var offset = 0;
for ( var point;
var dataIndex = params.start, offset = 0, point, tmpIn = [], tmpOut = []; var tmpIn = [];
dataIndex < params.end; var tmpOut = [];
dataIndex++ var dataIndex;
) {
while ((dataIndex = params.next()) != null) {
var axisDimVal = data.get(cDim, dataIndex); var axisDimVal = data.get(cDim, dataIndex);
var openVal = data.get(openDim, dataIndex); var openVal = data.get(openDim, dataIndex);
var closeVal = data.get(closeDim, dataIndex); var closeVal = data.get(closeDim, dataIndex);
......
...@@ -36,7 +36,8 @@ export default { ...@@ -36,7 +36,8 @@ export default {
function progress(params, data) { function progress(params, data) {
for (var dataIndex = params.start; dataIndex < params.end; dataIndex++) { var dataIndex;
while ((dataIndex = params.next()) != null) {
var itemModel = data.getItemModel(dataIndex); var itemModel = data.getItemModel(dataIndex);
var sign = data.getItemLayout(dataIndex).sign; var sign = data.getItemLayout(dataIndex).sign;
......
...@@ -93,7 +93,11 @@ proto.getPerformArgs = function (task, isBlock) { ...@@ -93,7 +93,11 @@ proto.getPerformArgs = function (task, isBlock) {
&& (!pCtx || pCtx.progressiveRender) && (!pCtx || pCtx.progressiveRender)
&& task.__idxInPipeline > pipeline.bockIndex; && task.__idxInPipeline > pipeline.bockIndex;
return {step: incremental ? pipeline.step : null}; var step = incremental ? pipeline.step : null;
var modDataCount = pCtx && pCtx.modDataCount;
var modBy = modDataCount != null ? Math.ceil(modDataCount / step): null;
return {step: step, modBy: modBy, modDataCount: modDataCount};
}; };
proto.getPipeline = function (pipelineId) { proto.getPipeline = function (pipelineId) {
...@@ -123,8 +127,13 @@ proto.updateStreamModes = function (seriesModel, view) { ...@@ -123,8 +127,13 @@ proto.updateStreamModes = function (seriesModel, view) {
var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold');
// TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.
// see `test/candlestick-large3.html`
var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
seriesModel.pipelineContext = pipeline.context = { seriesModel.pipelineContext = pipeline.context = {
progressiveRender: progressiveRender, progressiveRender: progressiveRender,
modDataCount: modDataCount,
large: large large: large
}; };
}; };
...@@ -145,7 +154,7 @@ proto.restorePipelines = function (ecModel) { ...@@ -145,7 +154,7 @@ proto.restorePipelines = function (ecModel) {
progressiveEnabled: progressive progressiveEnabled: progressive
&& !(seriesModel.preventIncremental && seriesModel.preventIncremental()), && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),
bockIndex: -1, bockIndex: -1,
step: progressive || 700, // ??? Temporarily number step: Math.round(progressive || 700),
count: 0 count: 0
}); });
......
...@@ -38,6 +38,8 @@ var taskProto = Task.prototype; ...@@ -38,6 +38,8 @@ var taskProto = Task.prototype;
* @param {Object} performArgs * @param {Object} performArgs
* @param {number} [performArgs.step] Specified step. * @param {number} [performArgs.step] Specified step.
* @param {number} [performArgs.skip] Skip customer perform call. * @param {number} [performArgs.skip] Skip customer perform call.
* @param {number} [performArgs.modBy] Sampling window size.
* @param {number} [performArgs.modDataCount] Sampling count.
*/ */
taskProto.perform = function (performArgs) { taskProto.perform = function (performArgs) {
var upTask = this._upstream; var upTask = this._upstream;
...@@ -60,12 +62,30 @@ taskProto.perform = function (performArgs) { ...@@ -60,12 +62,30 @@ taskProto.perform = function (performArgs) {
planResult = this._plan(this.context); planResult = this._plan(this.context);
} }
// Support sharding by mod, which changes the render sequence and makes the rendered graphic
// elements uniformed distributed when progress, especially when moving or zooming.
var lastModBy = normalizeModBy(this._modBy);
var lastModDataCount = this._modDataCount || 0;
var modBy = normalizeModBy(performArgs && performArgs.modBy);
var modDataCount = performArgs && performArgs.modDataCount || 0;
if (lastModBy !== modBy || lastModDataCount !== modDataCount) {
planResult = 'reset';
}
function normalizeModBy(val) {
!(val >= 1) && (val = 1); // jshint ignore:line
return val;
}
var forceFirstProgress; var forceFirstProgress;
if (this._dirty || planResult === 'reset') { if (this._dirty || planResult === 'reset') {
this._dirty = false; this._dirty = false;
forceFirstProgress = reset(this, skip); forceFirstProgress = reset(this, skip);
} }
this._modBy = modBy;
this._modDataCount = modDataCount;
var step = performArgs && performArgs.step; var step = performArgs && performArgs.step;
if (upTask) { if (upTask) {
...@@ -92,9 +112,12 @@ taskProto.perform = function (performArgs) { ...@@ -92,9 +112,12 @@ taskProto.perform = function (performArgs) {
this._dueEnd this._dueEnd
); );
!skip && (forceFirstProgress || start < end) && ( if (!skip && (forceFirstProgress || start < end)) {
this._progress({start: start, end: end}, this.context) iterator.reset(start, end, modBy, modDataCount);
); this._progress({
start: start, end: end, step: 1, count: end - start, next: iterator.next
}, this.context);
}
this._dueIndex = end; this._dueIndex = end;
// If no `outputDueEnd`, assume that output data and // If no `outputDueEnd`, assume that output data and
...@@ -120,6 +143,47 @@ taskProto.perform = function (performArgs) { ...@@ -120,6 +143,47 @@ taskProto.perform = function (performArgs) {
return this.unfinished(); return this.unfinished();
}; };
var iterator = (function () {
var end;
var current;
var modBy;
var modDataCount;
var winCount;
var it = {
reset: function (s, e, sStep, sCount) {
current = s;
end = e;
modBy = sStep;
modDataCount = sCount;
winCount = Math.ceil(modDataCount / modBy);
it.next = (modBy > 1 && modDataCount > 0) ? modNext : sequentialNext;
}
};
return it;
function sequentialNext() {
return current < end ? current++ : null;
}
function modNext() {
var dataIndex = (current % winCount) * modBy + Math.ceil(current / winCount);
var result = current >= end
? null
: dataIndex < modDataCount
? dataIndex
// If modDataCount is smaller than data.count() (consider `appendData` case),
// Use normal linear rendering mode.
: current;
current++;
return result;
}
})();
taskProto.dirty = function () { taskProto.dirty = function () {
this._dirty = true; this._dirty = true;
this._onDirty && this._onDirty(this.context); this._onDirty && this._onDirty(this.context);
...@@ -144,6 +208,7 @@ function reset(taskIns, skip) { ...@@ -144,6 +208,7 @@ function reset(taskIns, skip) {
} }
taskIns._progress = progress; taskIns._progress = progress;
taskIns._modBy = taskIns._modDataCount = null;
var downstream = taskIns._downstream; var downstream = taskIns._downstream;
downstream && downstream.dirty(); downstream && downstream.dirty();
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="../dist/echarts.js"></script> <script src="lib/esl.js"></script>
<script src="lib/config.js"></script> <script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script> <script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script> <script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script> <script src="lib/testHelper.js"></script>
<script src="lib/frameInsight.js"></script>
<link rel="stylesheet" href="lib/reset.css" /> <link rel="stylesheet" href="lib/reset.css" />
</head> </head>
<body> <body>
...@@ -21,6 +22,7 @@ ...@@ -21,6 +22,7 @@
<div id="main0"></div> <div id="main0"></div>
<div id="panel0"></div> <div id="panel0"></div>
<div id="duration"></div>
...@@ -28,14 +30,15 @@ ...@@ -28,14 +30,15 @@
<script> <script>
require(['echarts'], function (echarts) {
// The data count is from a real requirement. // The data count is from a real requirement.
var rawDataCount = 2e5; var rawDataCount = 2e5;
var progressive = 4e3;
function run() { function run() {
var data = generateOHLC(rawDataCount); var data = generateOHLC(rawDataCount);
var result = reorder(data); // var result = reorder(data);
init(result.data, result.categoryData); init(data);
} }
function generateOHLC(count) { function generateOHLC(count) {
...@@ -76,27 +79,6 @@ ...@@ -76,27 +79,6 @@
return data; return data;
} }
function reorder(data) {
var categoryData = new Array(data.length);
var categoryMap = {};
for (var i = 0; i < data.length; i++) {
categoryData[i] = data[i][0];
categoryMap[categoryData[i]] = i;
}
var newData = new Array(data.length);
var step = Math.round(data.length / progressive);
var newOffset = 0;
for (var offset = 0; offset < step; offset++) {
for (var i = offset; i < data.length; i += step) {
var item = data[i].slice();
newData[newOffset++] = item;
item[0] = categoryMap[item[0]];
}
}
return {data: newData, categoryData: categoryData};
}
function calculateMA(dayCount, data) { function calculateMA(dayCount, data) {
var result = []; var result = [];
for (var i = 0, len = data.length; i < len; i++) { for (var i = 0, len = data.length; i < len; i++) {
...@@ -113,7 +95,9 @@ ...@@ -113,7 +95,9 @@
return result; return result;
} }
function init(rawData, categoryData) { function init(rawData) {
frameInsight.init(echarts, 'duration');
var option = { var option = {
dataset: { dataset: {
...@@ -165,12 +149,12 @@ ...@@ -165,12 +149,12 @@
type: 'category', type: 'category',
scale: true, scale: true,
boundaryGap : false, boundaryGap : false,
// inverse: true,
axisLine: {onZero: false}, axisLine: {onZero: false},
splitLine: {show: false}, splitLine: {show: false},
splitNumber: 20, splitNumber: 20,
min: 'dataMin', min: 'dataMin',
max: 'dataMax', max: 'dataMax'
data: categoryData
}, },
// { // {
// type: 'category', // type: 'category',
...@@ -222,14 +206,16 @@ ...@@ -222,14 +206,16 @@
], ],
series: [ series: [
{ {
name: 'Fake index', name: 'Data Amount: ' + echarts.format.addCommas(rawDataCount),
type: 'candlestick', type: 'candlestick',
// progressiveMode: 'linear',
// data: data, // data: data,
encode: { encode: {
x: 0, x: 0,
y: [1, 4, 3, 2] y: [1, 4, 3, 2]
}, },
progressive: progressive // progressive: false
// progressive: progressive
// tooltip: { // tooltip: {
// formatter: function (param) { // formatter: function (param) {
// var param = param[0]; // var param = param[0];
...@@ -291,7 +277,7 @@ ...@@ -291,7 +277,7 @@
var panel = document.getElementById('panel0'); var panel = document.getElementById('panel0');
var chart = testHelper.create(echarts, 'main0', { var chart = testHelper.create(echarts, 'main0', {
title: 'Fake OHLC data', title: 'Progressive by mod',
option: option, option: option,
height: 550 height: 550
}); });
...@@ -337,6 +323,8 @@ ...@@ -337,6 +323,8 @@
run(); run();
});
</script> </script>
......
...@@ -33,20 +33,54 @@ ...@@ -33,20 +33,54 @@
require(['echarts'], function (echarts) { require(['echarts'], function (echarts) {
// The data count is from a real requirement. // The data count is from a real requirement.
var rawDataCount = 2e5; var rawDataChunkSize = 1e4;
var chunkCount = 20;
var minute = 60 * 1000;
var xValue = +new Date(2011, 0, 1);
var baseValue = Math.random() * 12000;
var xValueMin = 0;
var xValueMax = rawDataChunkSize * chunkCount;
var yValueMin = Infinity;
var yValueMax = -Infinity;
var rawData = [];
for (var i = 0; i < chunkCount; i++) {
rawData.push(generateOHLC(rawDataChunkSize));
}
yValueMax = Math.ceil(yValueMax);
yValueMin = Math.floor(yValueMin);
function run() { function run() {
var data = generateOHLC(rawDataCount);
// var result = reorder(data); frameInsight.init(echarts, 'duration');
init(data);
// var data = generateOHLC(rawDataChunkSize);
var chart = window.chart = init();
var loadedChunkIndex = 0;
appendData();
function appendData() {
if (loadedChunkIndex >= chunkCount) {
return;
}
setTimeout(function () {
chart.appendData({seriesIndex: 0, data: rawData[loadedChunkIndex]});
loadedChunkIndex++;
appendData();
}, 300);
}
} }
function generateOHLC(count) { function generateOHLC(count) {
var data = []; var data = [];
var xValue = +new Date(2011, 0, 1);
var minute = 60 * 1000;
var baseValue = Math.random() * 12000;
var tmpVals = new Array(4); var tmpVals = new Array(4);
var dayRange = 12; var dayRange = 12;
...@@ -55,6 +89,12 @@ ...@@ -55,6 +89,12 @@
for (var j = 0; j < 4; j++) { for (var j = 0; j < 4; j++) {
tmpVals[j] = (Math.random() - 0.5) * dayRange + baseValue; tmpVals[j] = (Math.random() - 0.5) * dayRange + baseValue;
if (tmpVals[j] < yValueMin) {
yValueMin = tmpVals[j];
}
if (tmpVals[j] > yValueMax) {
yValueMax = tmpVals[j];
}
} }
tmpVals.sort(); tmpVals.sort();
...@@ -95,14 +135,9 @@ ...@@ -95,14 +135,9 @@
return result; return result;
} }
function init(rawData) { function init() {
frameInsight.init(echarts, 'duration');
var option = { var option = {
dataset: {
source: rawData
},
backgroundColor: '#eee', backgroundColor: '#eee',
// animation: false, // animation: false,
legend: { legend: {
...@@ -153,8 +188,8 @@ ...@@ -153,8 +188,8 @@
axisLine: {onZero: false}, axisLine: {onZero: false},
splitLine: {show: false}, splitLine: {show: false},
splitNumber: 20, splitNumber: 20,
min: 'dataMin', min: xValueMin,
max: 'dataMax' max: xValueMax
}, },
// { // {
// type: 'category', // type: 'category',
...@@ -176,7 +211,9 @@ ...@@ -176,7 +211,9 @@
scale: true, scale: true,
splitArea: { splitArea: {
show: true show: true
} },
min: yValueMin,
max: yValueMax
}, },
// { // {
// scale: true, // scale: true,
...@@ -192,27 +229,28 @@ ...@@ -192,27 +229,28 @@
{ {
type: 'inside', type: 'inside',
// xAxisIndex: [0, 1], // xAxisIndex: [0, 1],
start: 10, // start: 10,
end: 100 // end: 100
}, },
{ {
show: true, show: true,
// xAxisIndex: [0, 1], // xAxisIndex: [0, 1],
type: 'slider', type: 'slider',
bottom: 10, bottom: 10,
start: 10, // start: 10,
end: 100 // end: 100
} }
], ],
series: [ series: [
{ {
name: 'Fake index',
type: 'candlestick', type: 'candlestick',
// progressiveMode: 'linear',
// data: data, // data: data,
encode: { encode: {
x: 0, x: 0,
y: [1, 4, 3, 2] y: [1, 4, 3, 2]
}, },
// progressiveChunkMode: 'sequential'
// progressive: false // progressive: false
// progressive: progressive // progressive: progressive
// tooltip: { // tooltip: {
...@@ -276,11 +314,13 @@ ...@@ -276,11 +314,13 @@
var panel = document.getElementById('panel0'); var panel = document.getElementById('panel0');
var chart = testHelper.create(echarts, 'main0', { var chart = testHelper.create(echarts, 'main0', {
title: 'Fake OHLC data', title: 'Append data and progressive by mod',
option: option, option: option,
height: 550 height: 550
}); });
return chart;
// chart && chart.on('brushSelected', renderBrushed); // chart && chart.on('brushSelected', renderBrushed);
// function renderBrushed(params) { // function renderBrushed(params) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册