You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2018/04/05 20:35:38 UTC

[incubator-echarts] 08/09: optimize candlestick.

This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 096a88e5aba1cf452787711edd22cbc51c7e6ad9
Author: sushuang <su...@gmail.com>
AuthorDate: Thu Apr 5 03:21:16 2018 +0800

    optimize candlestick.
---
 src/chart/boxplot/BoxplotView.js                   |   4 +-
 src/chart/candlestick/CandlestickSeries.js         |  11 +-
 src/chart/candlestick/CandlestickView.js           | 280 +++++++++++----------
 src/chart/candlestick/candlestickLayout.js         |  50 +++-
 src/chart/candlestick/candlestickVisual.js         |  12 +-
 src/chart/candlestick/helper.js                    |  28 ---
 test/candlestick-large.html                        |  12 +-
 ...dlestick-large.html => candlestick-large2.html} | 177 +++++++------
 ...dlestick-large.html => candlestick-large3.html} | 155 ++++++------
 test/lib/frameInsight.js                           |   4 +-
 10 files changed, 395 insertions(+), 338 deletions(-)

diff --git a/src/chart/boxplot/BoxplotView.js b/src/chart/boxplot/BoxplotView.js
index 4e356a1..5032356 100644
--- a/src/chart/boxplot/BoxplotView.js
+++ b/src/chart/boxplot/BoxplotView.js
@@ -70,7 +70,9 @@ var BoxplotView = ChartView.extend({
         data && data.eachItemGraphicEl(function (el) {
             el && group.remove(el);
         });
-    }
+    },
+
+    dispose: zrUtil.noop
 
 });
 
diff --git a/src/chart/candlestick/CandlestickSeries.js b/src/chart/candlestick/CandlestickSeries.js
index e9ec988..682cb7e 100644
--- a/src/chart/candlestick/CandlestickSeries.js
+++ b/src/chart/candlestick/CandlestickSeries.js
@@ -1,7 +1,6 @@
 import * as zrUtil from 'zrender/src/core/util';
 import SeriesModel from '../../model/Series';
 import {seriesModelMixin} from '../helper/whiskerBoxCommon';
-// import {calculateCandleWidth} from './helper';
 
 var CandlestickSeries = SeriesModel.extend({
 
@@ -57,7 +56,10 @@ var CandlestickSeries = SeriesModel.extend({
         barWidth: null,
 
         large: true,
-        largeThreshold: 500,
+        largeThreshold: 600,
+
+        progressive: 5e3,
+        progressiveThreshold: 1e4,
 
         animationUpdate: false,
         animationEasing: 'linear',
@@ -77,11 +79,6 @@ var CandlestickSeries = SeriesModel.extend({
         return itemLayout && selectors.rect(itemLayout.brushRect);
     }
 
-    // isLargeMode: function (data) {
-    //     // Experience number
-    //     return calculateCandleWidth(this, data) <= 1;
-    // }
-
 });
 
 zrUtil.mixin(CandlestickSeries, seriesModelMixin, true);
diff --git a/src/chart/candlestick/CandlestickView.js b/src/chart/candlestick/CandlestickView.js
index 6f8d960..527f62a 100644
--- a/src/chart/candlestick/CandlestickView.js
+++ b/src/chart/candlestick/CandlestickView.js
@@ -5,18 +5,51 @@ import Path from 'zrender/src/graphic/Path';
 
 var NORMAL_ITEM_STYLE_PATH = ['itemStyle'];
 var EMPHASIS_ITEM_STYLE_PATH = ['emphasis', 'itemStyle'];
+var SKIP_PROPS = ['color', 'color0', 'borderColor', 'borderColor0'];
+
 
 var CandlestickView = ChartView.extend({
 
     type: 'candlestick',
 
     render: function (seriesModel, ecModel, api) {
-        var group = this.group;
+        this._updateDrawMode(seriesModel);
+
+        this._isLargeDraw
+            ? this._renderLarge(seriesModel)
+            : this._renderNormal(seriesModel);
+    },
+
+    incrementalPrepareRender: function (seriesModel, ecModel, api) {
+        this._clear();
+        this._updateDrawMode(seriesModel);
+    },
+
+    incrementalRender: function (params, seriesModel, ecModel, api) {
+        this._isLargeDraw
+             ? this._incrementalRenderLarge(params, seriesModel)
+             : this._incrementalRenderNormal(params, seriesModel);
+    },
+
+    _updateDrawMode: function (seriesModel) {
+        var isLargeDraw = seriesModel.pipelineContext.large;
+        if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) {
+            this._isLargeDraw = isLargeDraw;
+            this._clear();
+        }
+    },
+
+    _clear: function () {
+        this.group.removeAll();
+        this._data = null;
+    },
+
+    _renderNormal: function (seriesModel) {
+        // var largePoints = data.getLayout('largePoints');
         var data = seriesModel.getData();
         var oldData = this._data;
-        var pipelineContext = seriesModel.pipelineContext;
-        var isLargeRender = pipelineContext.large;
-        var largePoints = isLargeRender && data.getLayout('largePoints');
+        var group = this.group;
+        var isSimpleBox = data.getLayout('isSimpleBox');
 
         // There is no old data only when first rendering or switching from
         // stream mode to normal mode, where previous elements should be removed.
@@ -27,42 +60,39 @@ var CandlestickView = ChartView.extend({
         data.diff(oldData)
             .add(function (newIdx) {
                 if (data.hasValue(newIdx)) {
-                    var symbolEl = isLargeRender
-                        ? createLargeBox(data, largePoints, newIdx)
-                        : createNormalBox(data, newIdx, true);
-                    data.setItemGraphicEl(newIdx, symbolEl);
-                    group.add(symbolEl);
+                    var el;
+
+                    var itemLayout = data.getItemLayout(newIdx);
+                    el = createNormalBox(itemLayout, newIdx, true);
+                    graphic.initProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx);
+
+                    setBoxCommon(el, data, newIdx, isSimpleBox);
+
+                    group.add(el);
+                    data.setItemGraphicEl(newIdx, el);
                 }
             })
             .update(function (newIdx, oldIdx) {
-                var symbolEl = oldData.getItemGraphicEl(oldIdx);
+                var el = oldData.getItemGraphicEl(oldIdx);
 
                 // Empty data
                 if (!data.hasValue(newIdx)) {
-                    group.remove(symbolEl);
+                    group.remove(el);
                     return;
                 }
 
-                if (symbolEl && symbolEl.__largeWhiskerBox ^ isLargeRender) {
-                    group.remove(symbolEl);
-                    symbolEl = null;
-                }
-
-                if (!symbolEl) {
-                    symbolEl = isLargeRender
-                        ? createLargeBox(data, largePoints, newIdx)
-                        : createNormalBox(data, newIdx);
+                var itemLayout = data.getItemLayout(newIdx);
+                if (!el) {
+                    el = createNormalBox(itemLayout, newIdx);
                 }
                 else {
-                    isLargeRender
-                        ? updateLargeBoxData(symbolEl, data, newIdx)
-                        : updateNormalBoxData(symbolEl, data, newIdx);
+                    graphic.updateProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx);
                 }
 
-                // Add back
-                group.add(symbolEl);
+                setBoxCommon(el, data, newIdx, isSimpleBox);
 
-                data.setItemGraphicEl(newIdx, symbolEl);
+                group.add(el);
+                data.setItemGraphicEl(newIdx, el);
             })
             .remove(function (oldIdx) {
                 var el = oldData.getItemGraphicEl(oldIdx);
@@ -73,26 +103,34 @@ var CandlestickView = ChartView.extend({
         this._data = data;
     },
 
-    incrementalPrepareRender: function (seriesModel, ecModel, api) {
-        this.group.removeAll();
-        this._data = null;
+    _renderLarge: function (seriesModel) {
+        var group = this.group;
+
+        group.removeAll();
+
+        createLarge(seriesModel, group);
     },
 
-    incrementalRender: function (params, seriesModel, ecModel, api) {
+    _incrementalRenderNormal: function (params, seriesModel) {
         var data = seriesModel.getData();
-        var pipelineContext = seriesModel.pipelineContext;
-        var isLargeRender = pipelineContext.large;
-        var largePoints = isLargeRender && data.getLayout('largePoints');
-
-        for (var idx = params.start; idx < params.end; idx++) {
-            var symbolEl = isLargeRender
-                ? createLargeBox(data, largePoints, idx, params.start)
-                : createNormalBox(data, idx, this.styleUpdater, true);
-            symbolEl.incremental = true;
-            this.group.add(symbolEl);
+        var isSimpleBox = data.getLayout('isSimpleBox');
+
+        for (var dataIndex = params.start; dataIndex < params.end; dataIndex++) {
+            var el;
+
+            var itemLayout = data.getItemLayout(dataIndex);
+            el = createNormalBox(itemLayout, dataIndex);
+            setBoxCommon(el, data, dataIndex, isSimpleBox);
+
+            el.incremental = true;
+            this.group.add(el);
         }
     },
 
+    _incrementalRenderLarge: function (params, seriesModel) {
+        createLarge(seriesModel, this.group, true);
+    },
+
     remove: function (ecModel) {
         var group = this.group;
         var data = this._data;
@@ -100,45 +138,44 @@ var CandlestickView = ChartView.extend({
         data && data.eachItemGraphicEl(function (el) {
             el && group.remove(el);
         });
-    }
-
-});
-
-
+    },
 
+    dispose: zrUtil.noop
 
+});
 
-// ---------------------
-// -- Normal Renderer --
-// ---------------------
 
 var NormalBoxPath = Path.extend({
 
-    type: 'candlestickBox',
+    type: 'normalCandlestickBox',
 
     shape: {},
 
     buildPath: function (ctx, shape) {
         var ends = shape.points;
 
-        ctx.moveTo(ends[0][0], ends[0][1]);
-        ctx.lineTo(ends[1][0], ends[1][1]);
-        ctx.lineTo(ends[2][0], ends[2][1]);
-        ctx.lineTo(ends[3][0], ends[3][1]);
-        ctx.closePath();
-
-        ctx.moveTo(ends[4][0], ends[4][1]);
-        ctx.lineTo(ends[5][0], ends[5][1]);
-        ctx.moveTo(ends[6][0], ends[6][1]);
-        ctx.lineTo(ends[7][0], ends[7][1]);
+        if (this.__simpleBox) {
+            ctx.moveTo(ends[4][0], ends[4][1]);
+            ctx.lineTo(ends[6][0], ends[6][1]);
+        }
+        else {
+            ctx.moveTo(ends[0][0], ends[0][1]);
+            ctx.lineTo(ends[1][0], ends[1][1]);
+            ctx.lineTo(ends[2][0], ends[2][1]);
+            ctx.lineTo(ends[3][0], ends[3][1]);
+            ctx.closePath();
+
+            ctx.moveTo(ends[4][0], ends[4][1]);
+            ctx.lineTo(ends[5][0], ends[5][1]);
+            ctx.moveTo(ends[6][0], ends[6][1]);
+            ctx.lineTo(ends[7][0], ends[7][1]);
+        }
     }
 });
 
-function createNormalBox(data, dataIndex, isInit) {
-    var itemLayout = data.getItemLayout(dataIndex);
+function createNormalBox(itemLayout, dataIndex, isInit) {
     var ends = itemLayout.ends;
-
-    var el = new NormalBoxPath({
+    return new NormalBoxPath({
         shape: {
             points: isInit
                 ? transInit(ends, itemLayout)
@@ -146,24 +183,9 @@ function createNormalBox(data, dataIndex, isInit) {
         },
         z2: 100
     });
-
-    updateNormalBoxData(el, data, dataIndex, isInit);
-
-    return el;
 }
 
-function updateNormalBoxData(el, data, dataIndex, isInit) {
-    var seriesModel = data.hostModel;
-    var itemLayout = data.getItemLayout(dataIndex);
-    var updateMethod = graphic[isInit ? 'initProps' : 'updateProps'];
-
-    updateMethod(
-        el,
-        {shape: {points: itemLayout.ends}},
-        seriesModel,
-        dataIndex
-    );
-
+function setBoxCommon(el, data, dataIndex, isSimpleBox) {
     var itemModel = data.getItemModel(dataIndex);
     var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH);
     var color = data.getItemVisual(dataIndex, 'color');
@@ -171,15 +193,15 @@ function updateNormalBoxData(el, data, dataIndex, isInit) {
 
     // Color must be excluded.
     // Because symbol provide setColor individually to set fill and stroke
-    var itemStyle = normalItemStyleModel.getItemStyle(
-        ['color', 'color0', 'borderColor', 'borderColor0']
-    );
+    var itemStyle = normalItemStyleModel.getItemStyle(SKIP_PROPS);
 
     el.useStyle(itemStyle);
     el.style.strokeNoScale = true;
     el.style.fill = color;
     el.style.stroke = borderColor;
 
+    el.__simpleBox = isSimpleBox;
+
     var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH).getItemStyle();
     graphic.setHoverStyle(el, hoverStyle);
 }
@@ -194,70 +216,72 @@ function transInit(points, itemLayout) {
 
 
 
+var LargeBoxPath = Path.extend({
 
+    type: 'largeCandlestickBox',
 
+    shape: {},
 
-// --------------------
-// -- Large Renderer --
-// --------------------
+    buildPath: function (ctx, shape) {
+        // Drawing lines is more efficient than drawing
+        // a whole line or drawing rects.
+        var points = shape.points;
+        for (var i = 0; i < points.length;) {
+            if (this.__sign === points[i++]) {
+                var x = points[i++];
+                ctx.moveTo(x, points[i++]);
+                ctx.lineTo(x, points[i++]);
+            }
+            else {
+                i += 3;
+            }
+        }
+    }
+});
 
-var NORMAL_STYLE_ACCESS_PATH = ['itemStyle'];
-var EMPHASIS_STYLE_ACCESS_PATH = ['emphasis', 'itemStyle'];
+function createLarge(seriesModel, group, incremental) {
+    var data = seriesModel.getData();
+    var largePoints = data.getLayout('largePoints');
 
-function createLargeBox(data, largePoints, dataIndex, segmentStart) {
-    var boxEl = new graphic.Line({
-        shape: largeBoxMakeShape(largePoints, dataIndex, segmentStart)
+    var elP = new LargeBoxPath({
+        shape: {points: largePoints},
+        __sign: 1
     });
+    group.add(elP);
+    var elN = new LargeBoxPath({
+        shape: {points: largePoints},
+        __sign: -1
+    });
+    group.add(elN);
 
-    boxEl.__largeWhiskerBox = true;
-
-    largeBoxSetStyle(boxEl, data, dataIndex);
-
-    return boxEl;
-}
+    setLargeStyle(1, elP, seriesModel, data);
+    setLargeStyle(-1, elN, seriesModel, data);
 
-function largeBoxMakeShape(largePoints, dataIndex, segmentStart) {
-    var baseIdx = (dataIndex - (segmentStart || 0)) * 5;
-    return {
-        x1: largePoints[baseIdx + 1],
-        y1: largePoints[baseIdx + 2],
-        x2: largePoints[baseIdx + 3],
-        y2: largePoints[baseIdx + 4]
-    };
+    if (incremental) {
+        elP.incremental = true;
+        elN.incremental = true;
+    }
 }
 
-function updateLargeBoxData(boxEl, data, dataIndex) {
-    graphic.updateProps(
-        boxEl,
-        {shape: largeBoxMakeShape(data.getLayout('largePoints'), dataIndex, 0)},
-        data.hostModel,
-        dataIndex
-    );
+function setLargeStyle(sign, el, seriesModel, data) {
+    var normalItemStyleModel = seriesModel.getModel(NORMAL_ITEM_STYLE_PATH);
+    var suffix = sign > 0 ? 'P' : 'N';
 
-    largeBoxSetStyle(boxEl, data, dataIndex);
-}
-
-function largeBoxSetStyle(boxEl, data, dataIndex) {
-    var itemModel = data.getItemModel(dataIndex);
-    var normalItemStyleModel = itemModel.getModel(NORMAL_STYLE_ACCESS_PATH);
-    var color = data.getItemVisual(dataIndex, 'color');
-    var borderColor = data.getItemVisual(dataIndex, 'borderColor') || color;
+    var color = data.getVisual('color' + suffix);
+    var borderColor = data.getVisual('borderColor' + suffix) || color;
 
     // Color must be excluded.
     // Because symbol provide setColor individually to set fill and stroke
-    var itemStyle = normalItemStyleModel.getItemStyle(
-        ['color', 'color0', 'borderColor', 'borderColor0']
-    );
+    var itemStyle = normalItemStyleModel.getItemStyle(SKIP_PROPS);
 
-    boxEl.useStyle(itemStyle);
-    boxEl.style.stroke = borderColor;
-
-    var hoverStyle = itemModel.getModel(EMPHASIS_STYLE_ACCESS_PATH).getItemStyle();
-    graphic.setHoverStyle(boxEl, hoverStyle);
+    el.useStyle(itemStyle);
+    el.style.fill = null;
+    el.style.stroke = borderColor;
+    // No different
+    // el.style.lineWidth = .5;
 }
 
 
 
-
 export default CandlestickView;
 
diff --git a/src/chart/candlestick/candlestickLayout.js b/src/chart/candlestick/candlestickLayout.js
index 6fa8fda..3aa8d5c 100644
--- a/src/chart/candlestick/candlestickLayout.js
+++ b/src/chart/candlestick/candlestickLayout.js
@@ -1,6 +1,7 @@
 import {subPixelOptimize} from '../../util/graphic';
 import createRenderPlanner from '../helper/createRenderPlanner';
-import {calculateCandleWidth} from './helper';
+import {parsePercent} from '../../util/number';
+import {retrieve2} from 'zrender/src/core/util';
 
 var LargeArr = typeof Float32Array !== 'undefined' ? Float32Array : Array;
 
@@ -12,9 +13,6 @@ export default {
 
     reset: function (seriesModel) {
 
-        var pipelineContext = seriesModel.pipelineContext;
-        var isLargeRender = pipelineContext.large;
-
         var coordSys = seriesModel.coordinateSystem;
         var data = seriesModel.getData();
         var candleWidth = calculateCandleWidth(seriesModel, data);
@@ -28,12 +26,20 @@ export default {
         var lowestDim = vDims[2];
         var highestDim = vDims[3];
 
+        data.setLayout({
+            candleWidth: candleWidth,
+            // The value is experimented visually.
+            isSimpleBox: candleWidth <= 1.3
+        });
+
         if (cDim == null || vDims.length < 4) {
             return;
         }
 
-        return {progress: isLargeRender ? largeProgress : normalProgress};
-
+        return {
+            progress: seriesModel.pipelineContext.large
+                ? largeProgress : normalProgress
+        };
 
         function normalProgress(params, data) {
 
@@ -121,7 +127,7 @@ export default {
 
         function largeProgress(params, data) {
             var segCount = params.end - params.start;
-            // Structure: [sign, x1, y1, x2, y2, sign, x1, y1, x2, y2, ...]
+            // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]
             var points = new LargeArr(segCount * 5);
 
             for (
@@ -149,15 +155,12 @@ export default {
                 point = coordSys.dataToPoint(tmpIn, null, tmpOut);
                 points[offset++] = point ? point[0] : NaN;
                 points[offset++] = point ? point[1] : NaN;
-
                 tmpIn[vDimIdx] = highestVal;
                 point = coordSys.dataToPoint(tmpIn, null, tmpOut);
-                points[offset++] = point ? point[0] : NaN;
                 points[offset++] = point ? point[1] : NaN;
             }
 
             data.setLayout('largePoints', points);
-            data.setLayout('candleWidth', candleWidth);
         }
     }
 };
@@ -180,3 +183,30 @@ function getSign(data, dataIndex, openVal, closeVal, closeDim) {
 
     return sign;
 }
+
+function calculateCandleWidth(seriesModel, data) {
+    var baseAxis = seriesModel.getBaseAxis();
+    var extent;
+
+    var bandWidth = baseAxis.type === 'category'
+        ? baseAxis.getBandWidth()
+        : (
+            extent = baseAxis.getExtent(),
+            Math.abs(extent[1] - extent[0]) / data.count()
+        );
+
+    var barMaxWidth = parsePercent(
+        retrieve2(seriesModel.get('barMaxWidth'), bandWidth),
+        bandWidth
+    );
+    var barMinWidth = parsePercent(
+        retrieve2(seriesModel.get('barMinWidth'), 1),
+        bandWidth
+    );
+    var barWidth = seriesModel.get('barWidth');
+
+    return barWidth != null
+        ? parsePercent(barWidth, bandWidth)
+        // Put max outer to ensure bar visible in spite of overlap.
+        : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth);
+}
diff --git a/src/chart/candlestick/candlestickVisual.js b/src/chart/candlestick/candlestickVisual.js
index ad94894..9273480 100644
--- a/src/chart/candlestick/candlestickVisual.js
+++ b/src/chart/candlestick/candlestickVisual.js
@@ -17,9 +17,7 @@ export default {
     reset: function (seriesModel, ecModel) {
 
         var data = seriesModel.getData();
-        var pipelineContext = seriesModel.pipelineContext;
-        var isLargeRender = pipelineContext.large;
-        var largePoints = isLargeRender && data.getLayout('largePoints');
+        var isLargeRender = seriesModel.pipelineContext.large;
 
         data.setVisual({
             legendSymbol: 'roundRect',
@@ -34,12 +32,13 @@ export default {
             return;
         }
 
+        return !isLargeRender && {progress: progress};
+
+
         function progress(params, data) {
             for (var dataIndex = params.start; dataIndex < params.end; dataIndex++) {
                 var itemModel = data.getItemModel(dataIndex);
-                var sign = isLargeRender
-                    ? largePoints[(dataIndex - params.start) * 5]
-                    : data.getItemLayout(dataIndex).sign;
+                var sign = data.getItemLayout(dataIndex).sign;
 
                 data.setItemVisual(
                     dataIndex,
@@ -63,7 +62,6 @@ export default {
             );
         }
 
-        return {progress: progress};
     }
 
 };
\ No newline at end of file
diff --git a/src/chart/candlestick/helper.js b/src/chart/candlestick/helper.js
deleted file mode 100644
index a0b778e..0000000
--- a/src/chart/candlestick/helper.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import {parsePercent} from '../../util/number';
-import {retrieve2} from 'zrender/src/core/util';
-
-export function calculateCandleWidth(seriesModel, data) {
-    var baseAxis = seriesModel.getBaseAxis();
-    var extent;
-
-    var bandWidth = baseAxis.type === 'category'
-        ? baseAxis.getBandWidth()
-        : (
-            extent = baseAxis.getExtent(),
-            Math.abs(extent[1] - extent[0]) / data.count()
-        );
-
-    var barMaxWidth = parsePercent(
-        retrieve2(seriesModel.get('barMaxWidth'), bandWidth),
-        bandWidth
-    );
-    var barMinWidth = parsePercent(
-        retrieve2(seriesModel.get('barMinWidth'), 1),
-        bandWidth
-    );
-    var barWidth = seriesModel.get('barWidth');
-    return barWidth != null
-        ? parsePercent(barWidth, bandWidth)
-        // Put max outer to ensure bar visible in spite of overlap.
-        : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth);
-}
diff --git a/test/candlestick-large.html b/test/candlestick-large.html
index e5b797c..e6665f3 100644
--- a/test/candlestick-large.html
+++ b/test/candlestick-large.html
@@ -183,22 +183,28 @@
                         {
                             type: 'inside',
                             // xAxisIndex: [0, 1],
-                            // start: 10,
+                            // start: 99,
                             // end: 100
+                            // startValue: '2010-08-17',
+                            // endValue: '2012-08-06'
                         },
                         {
                             show: true,
                             // xAxisIndex: [0, 1],
                             type: 'slider',
                             bottom: 10,
-                            // start: 10,
+                            // start: 99,
                             // end: 100
+                            // startValue: '2010-08-17',
+                            // endValue: '2012-08-06'
                         }
                     ],
+                    animation: false,
                     series: [
                         {
                             name: 'Dow-Jones index',
                             type: 'candlestick',
+                            // large: false,
                             encode: {
                                 x: 0,
                                 y: [1, 2, 3, 4]
@@ -271,7 +277,7 @@
 
                 var panel = document.getElementById('panel0');
                 var chart = testHelper.create(echarts, 'main0', {
-                    title: 'Use dataset',
+                    title: 'Use dataset. Check item tooltip in large mode.',
                     option: option,
                     height: 550
                 });
diff --git a/test/candlestick-large.html b/test/candlestick-large2.html
similarity index 68%
copy from test/candlestick-large.html
copy to test/candlestick-large2.html
index e5b797c..ec99971 100644
--- a/test/candlestick-large.html
+++ b/test/candlestick-large2.html
@@ -3,7 +3,7 @@
     <head>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1" />
-        <script src="lib/esl.js"></script>
+        <script src="../dist/echarts.js"></script>
         <script src="lib/config.js"></script>
         <script src="lib/jquery.min.js"></script>
         <script src="lib/facePrint.js"></script>
@@ -28,81 +28,106 @@
 
         <script>
 
-            /**
-             * @see <https://en.wikipedia.org/wiki/Michelson%E2%80%93Morley_experiment>
-             * @see <http://bl.ocks.org/mbostock/4061502>
-             */
-            require([
-                'echarts',
-                'data/stock-DJI.json'
-            ], update)
-
-            function splitData(rawData) {
-                var categoryData = [];
-                var values = [];
-                var volumns = [];
-                for (var i = 0; i < rawData.length; i++) {
-                    categoryData.push(rawData[i].splice(0, 1)[0]);
-                    values.push(rawData[i])
-                    volumns.push(rawData[i][4]);
+            // The data count is from a real requirement.
+            var rawDataCount = 2e5;
+            var progressive = 4e3;
+
+            function run() {
+                var data = generateOHLC(rawDataCount);
+                var result = reorder(data);
+                init(result.data, result.categoryData);
+            }
+
+            function generateOHLC(count) {
+                var data = [];
+
+                var xValue = +new Date(2011, 0, 1);
+                var minute = 60 * 1000;
+                var baseValue = Math.random() * 12000;
+                var tmpVals = new Array(4);
+                var dayRange = 12;
+
+                for (var i = 0; i < count; i++) {
+                    baseValue = baseValue + Math.random() * 20 - 10;
+
+                    for (var j = 0; j < 4; j++) {
+                        tmpVals[j] = (Math.random() - 0.5) * dayRange + baseValue;
+                    }
+                    tmpVals.sort();
+
+                    var idxRandom = Math.random();
+                    var openIdx = Math.round(Math.random() * 3);
+                    var closeIdx = Math.round(Math.random() * 2);
+                    if (closeIdx === openIdx) {
+                        closeIdx++;
+                    }
+
+                    // ['open', 'close', 'lowest', 'highest']
+                    // [1, 4, 3, 2]
+                    data.push([
+                        echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', xValue += minute),
+                        +tmpVals[openIdx].toFixed(2), // open
+                        +tmpVals[3].toFixed(2), // highest
+                        +tmpVals[0].toFixed(2), // lowest
+                        +tmpVals[closeIdx].toFixed(2)  // close
+                    ]);
                 }
-                return {
-                    categoryData: categoryData,
-                    values: values,
-                    volumns: volumns
-                };
+
+                return data;
             }
 
-            function calculateMA(dayCount, rawData) {
+            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) {
                 var result = [];
-                for (var i = 0, len = rawData.length; i < len; i++) {
+                for (var i = 0, len = data.length; i < len; i++) {
                     if (i < dayCount) {
                         result.push('-');
                         continue;
                     }
                     var sum = 0;
                     for (var j = 0; j < dayCount; j++) {
-                        sum += rawData[i - j][2];
+                        sum += data[i - j][2];
                     }
                     result.push(+(sum / dayCount).toFixed(3));
                 }
                 return result;
             }
 
-            function update(echarts, rawData) {
-
-                // var data = splitData(rawData);
+            function init(rawData, categoryData) {
 
                 var option = {
                     dataset: {
                         source: rawData
                     },
                     backgroundColor: '#eee',
+                    // animation: false,
                     legend: {
-                        left: 0,
+                        left: 0
                     },
                     tooltip: {
                         trigger: 'axis',
                         axisPointer: {
-                            type: 'cross'
-                        },
-                        backgroundColor: 'rgba(245, 245, 245, 0.8)',
-                        borderWidth: 1,
-                        borderColor: '#ccc',
-                        padding: 10,
-                        textStyle: {
-                            color: '#000'
-                        },
-                        position: function (pos, params, el, elRect, size) {
-                            var obj = {top: 10};
-                            obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;
-                            return obj;
-                        }
-                    },
-                    axisPointer: {
-                        link: {xAxisIndex: 'all'},
-                        label: {
-                            backgroundColor: '#777'
+                            type: 'line'
                         }
                     },
                     toolbox: {
@@ -115,13 +140,13 @@
                             }
                         }
                     },
-                    brush: {
-                        xAxisIndex: 'all',
-                        brushLink: 'all',
-                        outOfBrush: {
-                            colorAlpha: 0.1
-                        }
-                    },
+                    // brush: {
+                    //     xAxisIndex: 'all',
+                    //     brushLink: 'all',
+                    //     outOfBrush: {
+                    //         colorAlpha: 0.1
+                    //     }
+                    // },
                     grid: [
                         {
                             left: '10%',
@@ -138,14 +163,14 @@
                     xAxis: [
                         {
                             type: 'category',
-                            // data: data.categoryData,
                             scale: true,
                             boundaryGap : false,
                             axisLine: {onZero: false},
                             splitLine: {show: false},
                             splitNumber: 20,
                             min: 'dataMin',
-                            max: 'dataMax'
+                            max: 'dataMax',
+                            data: categoryData
                         },
                         // {
                         //     type: 'category',
@@ -183,32 +208,28 @@
                         {
                             type: 'inside',
                             // xAxisIndex: [0, 1],
-                            // start: 10,
-                            // end: 100
+                            start: 10,
+                            end: 100
                         },
                         {
                             show: true,
                             // xAxisIndex: [0, 1],
                             type: 'slider',
                             bottom: 10,
-                            // start: 10,
-                            // end: 100
+                            start: 10,
+                            end: 100
                         }
                     ],
                     series: [
                         {
-                            name: 'Dow-Jones index',
+                            name: 'Fake index',
                             type: 'candlestick',
+                            // data: data,
                             encode: {
                                 x: 0,
-                                y: [1, 2, 3, 4]
-                            },
-                            itemStyle: {
-                                normal: {
-                                    borderColor: null,
-                                    borderColor0: null
-                                }
+                                y: [1, 4, 3, 2]
                             },
+                            progressive: progressive
                             // tooltip: {
                             //     formatter: function (param) {
                             //         var param = param[0];
@@ -221,12 +242,11 @@
                             //         ].join('')
                             //     }
                             // }
-                        },
+                        } //,
                         // {
                         //     name: 'MA5',
                         //     type: 'line',
-                        //     sampling: 'max',
-                        //     data: calculateMA(5, rawData),
+                        //     data: calculateMA(5, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -235,7 +255,7 @@
                         // {
                         //     name: 'MA10',
                         //     type: 'line',
-                        //     data: calculateMA(10, rawData),
+                        //     data: calculateMA(10, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -244,7 +264,7 @@
                         // {
                         //     name: 'MA20',
                         //     type: 'line',
-                        //     data: calculateMA(20, rawData),
+                        //     data: calculateMA(20, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -253,7 +273,7 @@
                         // {
                         //     name: 'MA30',
                         //     type: 'line',
-                        //     data: calculateMA(30, rawData),
+                        //     data: calculateMA(30, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -271,7 +291,7 @@
 
                 var panel = document.getElementById('panel0');
                 var chart = testHelper.create(echarts, 'main0', {
-                    title: 'Use dataset',
+                    title: 'Fake OHLC data',
                     option: option,
                     height: 550
                 });
@@ -312,8 +332,11 @@
                 //         }
                 //     ]
                 // });
+
             }
 
+            run();
+
         </script>
 
 
diff --git a/test/candlestick-large.html b/test/candlestick-large3.html
similarity index 73%
copy from test/candlestick-large.html
copy to test/candlestick-large3.html
index e5b797c..9d3f579 100644
--- a/test/candlestick-large.html
+++ b/test/candlestick-large3.html
@@ -3,11 +3,12 @@
     <head>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1" />
-        <script src="lib/esl.js"></script>
+        <script src="../dist/echarts.js"></script>
         <script src="lib/config.js"></script>
         <script src="lib/jquery.min.js"></script>
         <script src="lib/facePrint.js"></script>
         <script src="lib/testHelper.js"></script>
+        <script src="lib/frameInsight.js"></script>
         <link rel="stylesheet" href="lib/reset.css" />
     </head>
     <body>
@@ -21,6 +22,7 @@
 
         <div id="main0"></div>
         <div id="panel0"></div>
+        <div id="duration"></div>
 
 
 
@@ -28,81 +30,86 @@
 
         <script>
 
-            /**
-             * @see <https://en.wikipedia.org/wiki/Michelson%E2%80%93Morley_experiment>
-             * @see <http://bl.ocks.org/mbostock/4061502>
-             */
-            require([
-                'echarts',
-                'data/stock-DJI.json'
-            ], update)
-
-            function splitData(rawData) {
-                var categoryData = [];
-                var values = [];
-                var volumns = [];
-                for (var i = 0; i < rawData.length; i++) {
-                    categoryData.push(rawData[i].splice(0, 1)[0]);
-                    values.push(rawData[i])
-                    volumns.push(rawData[i][4]);
+            // The data count is from a real requirement.
+            var rawDataCount = 2e5;
+
+            function run() {
+                var data = generateOHLC(rawDataCount);
+                // var result = reorder(data);
+                init(data);
+            }
+
+            function generateOHLC(count) {
+                var data = [];
+
+                var xValue = +new Date(2011, 0, 1);
+                var minute = 60 * 1000;
+                var baseValue = Math.random() * 12000;
+                var tmpVals = new Array(4);
+                var dayRange = 12;
+
+                for (var i = 0; i < count; i++) {
+                    baseValue = baseValue + Math.random() * 20 - 10;
+
+                    for (var j = 0; j < 4; j++) {
+                        tmpVals[j] = (Math.random() - 0.5) * dayRange + baseValue;
+                    }
+                    tmpVals.sort();
+
+                    var idxRandom = Math.random();
+                    var openIdx = Math.round(Math.random() * 3);
+                    var closeIdx = Math.round(Math.random() * 2);
+                    if (closeIdx === openIdx) {
+                        closeIdx++;
+                    }
+
+                    // ['open', 'close', 'lowest', 'highest']
+                    // [1, 4, 3, 2]
+                    data.push([
+                        echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', xValue += minute),
+                        +tmpVals[openIdx].toFixed(2), // open
+                        +tmpVals[3].toFixed(2), // highest
+                        +tmpVals[0].toFixed(2), // lowest
+                        +tmpVals[closeIdx].toFixed(2)  // close
+                    ]);
                 }
-                return {
-                    categoryData: categoryData,
-                    values: values,
-                    volumns: volumns
-                };
+
+                return data;
             }
 
-            function calculateMA(dayCount, rawData) {
+            function calculateMA(dayCount, data) {
                 var result = [];
-                for (var i = 0, len = rawData.length; i < len; i++) {
+                for (var i = 0, len = data.length; i < len; i++) {
                     if (i < dayCount) {
                         result.push('-');
                         continue;
                     }
                     var sum = 0;
                     for (var j = 0; j < dayCount; j++) {
-                        sum += rawData[i - j][2];
+                        sum += data[i - j][2];
                     }
                     result.push(+(sum / dayCount).toFixed(3));
                 }
                 return result;
             }
 
-            function update(echarts, rawData) {
+            function init(rawData) {
 
-                // var data = splitData(rawData);
+                frameInsight.init(echarts, 'duration');
 
                 var option = {
                     dataset: {
                         source: rawData
                     },
                     backgroundColor: '#eee',
+                    // animation: false,
                     legend: {
-                        left: 0,
+                        left: 0
                     },
                     tooltip: {
                         trigger: 'axis',
                         axisPointer: {
-                            type: 'cross'
-                        },
-                        backgroundColor: 'rgba(245, 245, 245, 0.8)',
-                        borderWidth: 1,
-                        borderColor: '#ccc',
-                        padding: 10,
-                        textStyle: {
-                            color: '#000'
-                        },
-                        position: function (pos, params, el, elRect, size) {
-                            var obj = {top: 10};
-                            obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;
-                            return obj;
-                        }
-                    },
-                    axisPointer: {
-                        link: {xAxisIndex: 'all'},
-                        label: {
-                            backgroundColor: '#777'
+                            type: 'line'
                         }
                     },
                     toolbox: {
@@ -115,13 +122,13 @@
                             }
                         }
                     },
-                    brush: {
-                        xAxisIndex: 'all',
-                        brushLink: 'all',
-                        outOfBrush: {
-                            colorAlpha: 0.1
-                        }
-                    },
+                    // brush: {
+                    //     xAxisIndex: 'all',
+                    //     brushLink: 'all',
+                    //     outOfBrush: {
+                    //         colorAlpha: 0.1
+                    //     }
+                    // },
                     grid: [
                         {
                             left: '10%',
@@ -138,7 +145,6 @@
                     xAxis: [
                         {
                             type: 'category',
-                            // data: data.categoryData,
                             scale: true,
                             boundaryGap : false,
                             axisLine: {onZero: false},
@@ -183,32 +189,29 @@
                         {
                             type: 'inside',
                             // xAxisIndex: [0, 1],
-                            // start: 10,
-                            // end: 100
+                            start: 10,
+                            end: 100
                         },
                         {
                             show: true,
                             // xAxisIndex: [0, 1],
                             type: 'slider',
                             bottom: 10,
-                            // start: 10,
-                            // end: 100
+                            start: 10,
+                            end: 100
                         }
                     ],
                     series: [
                         {
-                            name: 'Dow-Jones index',
+                            name: 'Fake index',
                             type: 'candlestick',
+                            // data: data,
                             encode: {
                                 x: 0,
-                                y: [1, 2, 3, 4]
-                            },
-                            itemStyle: {
-                                normal: {
-                                    borderColor: null,
-                                    borderColor0: null
-                                }
+                                y: [1, 4, 3, 2]
                             },
+                            // progressive: false
+                            // progressive: progressive
                             // tooltip: {
                             //     formatter: function (param) {
                             //         var param = param[0];
@@ -221,12 +224,11 @@
                             //         ].join('')
                             //     }
                             // }
-                        },
+                        } //,
                         // {
                         //     name: 'MA5',
                         //     type: 'line',
-                        //     sampling: 'max',
-                        //     data: calculateMA(5, rawData),
+                        //     data: calculateMA(5, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -235,7 +237,7 @@
                         // {
                         //     name: 'MA10',
                         //     type: 'line',
-                        //     data: calculateMA(10, rawData),
+                        //     data: calculateMA(10, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -244,7 +246,7 @@
                         // {
                         //     name: 'MA20',
                         //     type: 'line',
-                        //     data: calculateMA(20, rawData),
+                        //     data: calculateMA(20, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -253,7 +255,7 @@
                         // {
                         //     name: 'MA30',
                         //     type: 'line',
-                        //     data: calculateMA(30, rawData),
+                        //     data: calculateMA(30, data),
                         //     smooth: true,
                         //     lineStyle: {
                         //         normal: {opacity: 0.5}
@@ -271,7 +273,7 @@
 
                 var panel = document.getElementById('panel0');
                 var chart = testHelper.create(echarts, 'main0', {
-                    title: 'Use dataset',
+                    title: 'Fake OHLC data',
                     option: option,
                     height: 550
                 });
@@ -312,8 +314,11 @@
                 //         }
                 //     ]
                 // });
+
             }
 
+            run();
+
         </script>
 
 
diff --git a/test/lib/frameInsight.js b/test/lib/frameInsight.js
index b19c6f3..1a397e9 100644
--- a/test/lib/frameInsight.js
+++ b/test/lib/frameInsight.js
@@ -139,9 +139,9 @@
         var domStyle = dom.style;
         // domStyle.border = '2px solid #333';
         domStyle.boxShadow = '0 0 3px #000';
-        domStyle.backgroundColor = '#fff';
+        domStyle.backgroundColor = '#eee';
         domStyle.padding = '0';
-        domStyle.height = '80px';
+        domStyle.height = '60px';
         domStyle.margin = '10px 20px';
 
         var domWidth = getSize(dom, 0);

-- 
To stop receiving notification emails like this one, please contact
sushuang@apache.org.

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org