From d42d48c0aceb7a9fdd00a19e91fbf00fcc49a44d Mon Sep 17 00:00:00 2001 From: lang Date: Fri, 27 Nov 2015 16:16:59 +0800 Subject: [PATCH] Improve markLine and some other tweak --- src/chart/helper/LineDraw.js | 266 ++++++++++++++++++++---- src/component/marker/MarkLineView.js | 65 ++++-- src/component/tooltip/TooltipContent.js | 2 +- src/component/tooltip/TooltipModel.js | 8 +- src/coord/axisDefault.js | 2 +- src/model/Series.js | 4 +- 6 files changed, 272 insertions(+), 75 deletions(-) diff --git a/src/chart/helper/LineDraw.js b/src/chart/helper/LineDraw.js index 0387a1ed6..47a84baf3 100644 --- a/src/chart/helper/LineDraw.js +++ b/src/chart/helper/LineDraw.js @@ -1,7 +1,104 @@ +/** + * @module echarts/component/marker/LineDraw + */ define(function (require) { var graphic = require('../../util/graphic'); + var numberUtil = require('../../util/number'); + var zrUtil = require('zrender/core/util'); + var symbolUtil = require('../../util/symbol'); + var vector = require('zrender/core/vector'); + function tangentRotation(p1, p2) { + return -Math.PI / 2 - Math.atan2( + p2[1] - p1[1], p2[0] - p1[0] + ); + } + /** + * @inner + */ + function createSymbol(data, idx) { + var color = data.getItemVisual(idx, 'color'); + var symbolType = data.getItemVisual(idx, 'symbol'); + var symbolSize = data.getItemVisual(idx, 'symbolSize'); + + if (symbolType === 'none') { + return; + } + + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [symbolSize, symbolSize]; + } + var symbolPath = symbolUtil.createSymbol( + symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2, + symbolSize[0], symbolSize[1], color + ); + + return symbolPath; + } + + function isSymbolArrow(symbol) { + return symbol.type === 'symbol' && symbol.shape.symbolType === 'arrow'; + } + + function updateSymbolBeforeLineUpdate () { + var lineGroup = this; + var line = lineGroup.childOfName('line'); + var symbolFrom = lineGroup.childOfName('fromSymbol'); + var symbolTo = lineGroup.childOfName('toSymbol'); + var label = lineGroup.childOfName('label'); + var lineShape = line.shape; + var fromPos = [lineShape.x1, lineShape.y1]; + var toPos = [lineShape.x2, lineShape.y2]; + + var d = vector.sub([], toPos, fromPos); + vector.normalize(d, d); + + if (symbolFrom) { + symbolFrom.attr('position', fromPos); + // Rotate the arrow + // FIXME Hard coded ? + if (isSymbolArrow(symbolTo)) { + symbolTo.attr('rotation', tangentRotation(fromPos, toPos)); + } + } + if (symbolTo) { + symbolTo.attr('position', toPos); + if (isSymbolArrow(symbolFrom)) { + symbolFrom.attr('rotation', tangentRotation(toPos, fromPos)); + } + } + + label.attr('position', toPos); + + var textPosition; + var textAlign; + var textBaseline; + // End + if (label.__position === 'end') { + textPosition = [d[0] * 5 + toPos[0], d[1] * 5 + toPos[1]]; + textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center'); + textBaseline = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle'); + } + // Start + else { + textPosition = [-d[0] * 5 + fromPos[0], -d[1] * 5 + fromPos[1]]; + textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center'); + textBaseline = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle'); + } + label.attr({ + style: { + textBaseline: textBaseline, + textAlign: textAlign + }, + position: textPosition + }); + } + + /** + * @alias module:echarts/component/marker/LineDraw + * @constructor + */ function LineDraw() { this.group = new graphic.Group(); } @@ -9,78 +106,155 @@ define(function (require) { var lineDrawProto = LineDraw.prototype; /** - * @param {module:echarts/data/List} data + * @param {module:echarts/data/List} lineData + * @param {module:echarts/data/List} [fromData] + * @param {module:echarts/data/List} [toData] + * @param {module:echarts/model/Model} seriesModel * @param {module:echarts/ExtensionAPI} api - * @param {Function} [isIgnore] */ - lineDrawProto.updateData = function (data, api, isIgnore) { + lineDrawProto.update = function ( + lineData, fromData, toData, seriesModel, api + ) { + + var oldFromData = this._fromData; + var oldToData = this._toData; + var oldLineData = this._lineData; var group = this.group; - var oldData = this._data; - // var seriesModel = data.hostModel; - data.diff(oldData) + lineData.diff(oldLineData) .add(function (idx) { - var shape = data.getItemLayout(idx); - var line = new graphic[shape.cpx1 != null ? 'BezierCurve' : 'Line']({ - shape: shape + var linePoints = lineData.getItemLayout(idx); + var p1 = linePoints[0]; + var p2 = linePoints[1]; + + var itemModel = lineData.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var textStyleModel = labelModel.getModel('textStyle'); + + var lineGroup = new graphic.Group(); + + var line = new graphic.Line({ + name: 'line', + shape: { + x1: p1[0], y1: p1[1], + x2: p1[0], y2: p1[1] + } + }); + api.initGraphicEl(line, { + shape: { + x2: p2[0], y2: p2[1] + } + }); + + lineGroup.add(line); + + var label = new graphic.Text({ + name: 'label', + style: { + text: seriesModel.getFormattedLabel(idx, 'normal') + || numberUtil.round(seriesModel.getData().getRawValue(idx)), + textFont: textStyleModel.getFont(), + fill: textStyleModel.get('color') || lineData.getItemVisual(idx, 'color') + } }); + lineGroup.add(label); + label.__position = labelModel.get('position'); - data.setItemGraphicEl(idx, line); - group.add(line); + if (fromData) { + var symbolFrom = createSymbol(fromData, idx); + symbolFrom.name = 'fromSymbol'; + // symbols must added after line to make sure + // it will be updated after line#update. + // Or symbol position and rotation update in line#beforeUpdate will be one frame slow + lineGroup.add(symbolFrom); + } + if (toData) { + var symbolTo = createSymbol(toData, idx); + symbolTo.name = 'toSymbol'; + lineGroup.add(symbolTo); + } + + // Update symbol position and rotation + lineGroup.beforeUpdate = updateSymbolBeforeLineUpdate; + lineData.setItemGraphicEl(idx, lineGroup); + + group.add(lineGroup); }) .update(function (newIdx, oldIdx) { - var line = oldData.getItemGraphicEl(oldIdx); - var shape = data.getItemLayout(newIdx); - if (shape.cpx1 != null && line.type === 'line') { - var oldShape = line.shape; - line = new graphic.BezierCurve({ - shape: oldShape - }); - line.setShape({ - cpx1: (oldShape.x1 + oldShape.x2) / 2, - cpy1: (oldShape.y1 + oldShape.y2) / 2 - }); - } + var lineGroup = oldLineData.getItemGraphicEl(oldIdx); + var line = lineGroup.childOfName('line'); + + var linePoints = lineData.getItemLayout(newIdx); + var p1 = linePoints[0]; + var p2 = linePoints[1]; + api.updateGraphicEl(line, { - shape: shape + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + } }); - data.setItemGraphicEl(newIdx, line); - group.add(line); + // Symbol changed + if (fromData) { + var fromSymbolType = fromData.getItemVisual(newIdx, 'symbol'); + if (!oldFromData || fromSymbolType !== oldFromData.getItemVisual(oldIdx, 'symbol')) { + var symbolFrom = createSymbol(fromData, newIdx); + symbolFrom.name = 'fromSymbol'; + lineGroup.remove(line.childOfName('fromSymbol')); + lineGroup.add(symbolFrom); + } + } + if (toData) { + var toSymbolType = toData.getItemVisual(newIdx, 'symbol'); + // Symbol changed + if (!oldToData || toSymbolType !== oldToData.getItemVisual(oldIdx, 'symbol')) { + var symbolTo = createSymbol(toData, newIdx); + symbolTo.name = 'toSymbol'; + lineGroup.remove(line.childOfName('toSymbol')); + lineGroup.add(symbolTo); + } + } + + lineData.setItemGraphicEl(newIdx, lineGroup); + + group.add(lineGroup); }) .remove(function (idx) { - group.remove(oldData.getItemGraphicEl(idx)); + group.remove(lineData.getItemGraphicEl(idx)); }) .execute(); - this._data = data; + lineData.eachItemGraphicEl(function (lineGroup, idx) { + var line = lineGroup.childOfName('line'); + var itemModel = lineData.getItemModel(idx); - this.updateVisual(); - }; + line.setStyle(zrUtil.defaults( + { + stroke: lineData.getItemVisual(idx, 'color') + }, + itemModel.getModel('lineStyle.normal').getLineStyle() + )); - lineDrawProto.updateVisual = function () { - var data = this._data; - data.eachItemGraphicEl(function (el, idx) { - var itemModel = data.getItemModel(idx); - el.setStyle(itemModel.getModel('lineStyle.normal').getLineStyle()); + graphic.setHoverStyle( + line, + itemModel.getModel('lineStyle.emphasis').getLineStyle() + ); }); + + this._lineData = lineData; + this._fromData = fromData; + this._toData = toData; }; lineDrawProto.updateLayout = function () { - var data = this._data; - data.eachItemGraphicEl(function (el, idx) { - var points = data.getItemLayout(idx); - data.setShape({ - x1: points[0][0], - y1: points[0][1], - x2: points[1][0], - y2: points[1][1] - }); - }); + }; lineDrawProto.remove = function () { - + this.group.removeAll(); }; return LineDraw; diff --git a/src/component/marker/MarkLineView.js b/src/component/marker/MarkLineView.js index e974659bc..18ad99f3e 100644 --- a/src/component/marker/MarkLineView.js +++ b/src/component/marker/MarkLineView.js @@ -10,7 +10,7 @@ define(function (require) { var markerHelper = require('./markerHelper'); - var SeriesMarkLine = require('./SeriesMarkLine'); + var LineDraw = require('../../chart/helper/LineDraw'); var markLineTransform = function (data, coordSys, baseAxis, valueAxis, item) { // Special type markLine like 'min', 'max', 'average' @@ -31,7 +31,7 @@ define(function (require) { var extent = data.getDataExtent(valueAxis.dim, true); - delete mlFrom.type; // Remove type + mlFrom.type = null; // FIXME Polar should use circle mlFrom[baseAxisKey] = baseScaleExtent[0]; @@ -47,15 +47,20 @@ define(function (require) { mlFrom[valueAxisKey] = mlTo[valueAxisKey] = value; item = [mlFrom, mlTo, { // Extra option for tooltip and label - __rawValue: value, - name: item.name + __rawValue: value }]; } - return [ + item = [ markerHelper.dataTransform(data, coordSys, item[0]), markerHelper.dataTransform(data, coordSys, item[1]), - item[2] + {} ]; + + // Merge from option and to option into line option + zrUtil.merge(item[2], item[0]); + zrUtil.merge(item[2], item[1]); + + return item; }; function markLineFilter(coordSys, dimensionInverse, item) { @@ -102,9 +107,9 @@ define(function (require) { }, render: function (markLineModel, ecModel, api) { - var seriesMarkLineMap = this._markLineMap; - for (var name in seriesMarkLineMap) { - seriesMarkLineMap[name].__keep = false; + var lineDrawMap = this._markLineMap; + for (var name in lineDrawMap) { + lineDrawMap[name].__keep = false; } ecModel.eachSeries(function (seriesModel) { @@ -112,9 +117,9 @@ define(function (require) { mlModel && this._renderSeriesML(seriesModel, mlModel, ecModel, api); }, this); - for (var name in seriesMarkLineMap) { - if (!seriesMarkLineMap[name].__keep) { - this.group.remove(seriesMarkLineMap[name].group); + for (var name in lineDrawMap) { + if (!lineDrawMap[name].__keep) { + this.group.remove(lineDrawMap[name].group); } } }, @@ -124,18 +129,19 @@ define(function (require) { var seriesName = seriesModel.name; var seriesData = seriesModel.getData(); - var seriesMarkLineMap = this._markLineMap; - var seriesMarkLine = seriesMarkLineMap[seriesName]; - if (!seriesMarkLine) { - seriesMarkLine = seriesMarkLineMap[seriesName] = new SeriesMarkLine(); + var lineDrawMap = this._markLineMap; + var lineDraw = lineDrawMap[seriesName]; + if (!lineDraw) { + lineDraw = lineDrawMap[seriesName] = new LineDraw(); } - this.group.add(seriesMarkLine.group); + this.group.add(lineDraw.group); var mlData = createList(coordSys, seriesData, mlModel); var dims = coordSys.dimensions; var fromData = mlData.from; var toData = mlData.to; + var lineData = mlData.line; // Line data for tooltip and formatter var lineData = mlData.line; @@ -155,19 +161,32 @@ define(function (require) { symbolSize = [symbolSize, symbolSize]; } + // Update visual and layout of from symbol and to symbol mlData.from.each(function (idx) { updateDataVisualAndLayout(fromData, idx, true); updateDataVisualAndLayout(toData, idx); }); - seriesMarkLine.update( - fromData, toData, mlModel, api, ecModel.get('animation') - ); + // Update visual and layout of line + lineData.each(function (idx) { + var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color'); + lineData.setItemVisual(idx, { + color: lineColor || fromData.getItemVisual(idx, 'color') + }); + lineData.setItemLayout(idx, [ + fromData.getItemLayout(idx), + toData.getItemLayout(idx) + ]); + }); + + lineDraw.update(lineData, fromData, toData, mlModel, api); // Set host model for tooltip // FIXME - mlData.from.eachItemGraphicEl(function (el, idx) { - el.hostModel = mlModel; + mlData.line.eachItemGraphicEl(function (el, idx) { + el.eachChild(function (child) { + child.hostModel = mlModel; + }); }); function updateDataVisualAndLayout(data, idx, isFrom) { @@ -197,7 +216,7 @@ define(function (require) { }); } - seriesMarkLine.__keep = true; + lineDraw.__keep = true; } }); diff --git a/src/component/tooltip/TooltipContent.js b/src/component/tooltip/TooltipContent.js index b2ff8d79e..35099f336 100644 --- a/src/component/tooltip/TooltipContent.js +++ b/src/component/tooltip/TooltipContent.js @@ -201,8 +201,8 @@ define(function (require) { hideLater: function (time) { if (this._show && !(this._inContent && this.enterable)) { if (time) { - // Set show false to avoid invoke hideLater mutiple times this._hideDelay = time; + // Set show false to avoid invoke hideLater mutiple times this._show = false; this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time); } diff --git a/src/component/tooltip/TooltipModel.js b/src/component/tooltip/TooltipModel.js index d02cec575..1ef3e6dc0 100644 --- a/src/component/tooltip/TooltipModel.js +++ b/src/component/tooltip/TooltipModel.js @@ -19,9 +19,11 @@ define(function (require) { // 触发类型,默认数据触发,见下图,可选为:'item' ¦ 'axis' trigger: 'item', - // If the tooltip follow the mouse position - // TODO - // followMouse: true, + // 触发条件,支持 'click' | 'mousemove' + triggerOn: 'mousemove', + + // 是否永远显示 content + // alwaysShowContent: false, // 位置 {Array} | {Function} // position: null diff --git a/src/coord/axisDefault.js b/src/coord/axisDefault.js index d6eb2b54f..899f591db 100644 --- a/src/coord/axisDefault.js +++ b/src/coord/axisDefault.js @@ -23,7 +23,7 @@ define(function (require) { onZero: true, // 属性lineStyle控制线条样式 lineStyle: { - color: '#000', + color: '#333', width: 1, type: 'solid' } diff --git a/src/model/Series.js b/src/model/Series.js index a7e5bde6e..8a5c8e692 100644 --- a/src/model/Series.js +++ b/src/model/Series.js @@ -70,7 +70,9 @@ define(function(require) { zrUtil.merge(option, this.getDefaultOption()); // Default label emphasis `position` and `show` - modelUtil.defaultEmphasis(option.label); + modelUtil.defaultEmphasis( + option.label, ['position', 'show', 'textStyle', 'distance', 'formatter'] + ); }, mergeOption: function (newSeriesOption, ecModel) { -- GitLab