| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 | 
/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**   http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*/var zrUtil = require("zrender/lib/core/util");var vector = require("zrender/lib/core/vector");var symbolUtil = require("../../util/symbol");var LinePath = require("./LinePath");var graphic = require("../../util/graphic");var _number = require("../../util/number");var round = _number.round;/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**   http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*//** * @module echarts/chart/helper/Line */var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];function makeSymbolTypeKey(symbolCategory) {  return '_' + symbolCategory + 'Type';}/** * @inner */function createSymbol(name, lineData, idx) {  var symbolType = lineData.getItemVisual(idx, name);  if (!symbolType || symbolType === 'none') {    return;  }  var color = lineData.getItemVisual(idx, 'color');  var symbolSize = lineData.getItemVisual(idx, name + 'Size');  var symbolRotate = lineData.getItemVisual(idx, name + 'Rotate');  if (!zrUtil.isArray(symbolSize)) {    symbolSize = [symbolSize, symbolSize];  }  var symbolPath = symbolUtil.createSymbol(symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2, symbolSize[0], symbolSize[1], color); // rotate by default if symbolRotate is not specified or NaN  symbolPath.__specifiedRotation = symbolRotate == null || isNaN(symbolRotate) ? void 0 : +symbolRotate * Math.PI / 180 || 0;  symbolPath.name = name;  return symbolPath;}function createLine(points) {  var line = new LinePath({    name: 'line',    subPixelOptimize: true  });  setLinePoints(line.shape, points);  return line;}function setLinePoints(targetShape, points) {  targetShape.x1 = points[0][0];  targetShape.y1 = points[0][1];  targetShape.x2 = points[1][0];  targetShape.y2 = points[1][1];  targetShape.percent = 1;  var cp1 = points[2];  if (cp1) {    targetShape.cpx1 = cp1[0];    targetShape.cpy1 = cp1[1];  } else {    targetShape.cpx1 = NaN;    targetShape.cpy1 = NaN;  }}function updateSymbolAndLabelBeforeLineUpdate() {  var lineGroup = this;  var symbolFrom = lineGroup.childOfName('fromSymbol');  var symbolTo = lineGroup.childOfName('toSymbol');  var label = lineGroup.childOfName('label'); // Quick reject  if (!symbolFrom && !symbolTo && label.ignore) {    return;  }  var invScale = 1;  var parentNode = this.parent;  while (parentNode) {    if (parentNode.scale) {      invScale /= parentNode.scale[0];    }    parentNode = parentNode.parent;  }  var line = lineGroup.childOfName('line'); // If line not changed  // FIXME Parent scale changed  if (!this.__dirty && !line.__dirty) {    return;  }  var percent = line.shape.percent;  var fromPos = line.pointAt(0);  var toPos = line.pointAt(percent);  var d = vector.sub([], toPos, fromPos);  vector.normalize(d, d);  if (symbolFrom) {    symbolFrom.attr('position', fromPos); // Fix #12388    // when symbol is set to be 'arrow' in markLine,    // symbolRotate value will be ignored, and compulsively use tangent angle.    // rotate by default if symbol rotation is not specified    var specifiedRotation = symbolFrom.__specifiedRotation;    if (specifiedRotation == null) {      var tangent = line.tangentAt(0);      symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));    } else {      symbolFrom.attr('rotation', specifiedRotation);    }    symbolFrom.attr('scale', [invScale * percent, invScale * percent]);  }  if (symbolTo) {    symbolTo.attr('position', toPos); // Fix #12388    // when symbol is set to be 'arrow' in markLine,    // symbolRotate value will be ignored, and compulsively use tangent angle.    // rotate by default if symbol rotation is not specified    var specifiedRotation = symbolTo.__specifiedRotation;    if (specifiedRotation == null) {      var tangent = line.tangentAt(1);      symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));    } else {      symbolTo.attr('rotation', specifiedRotation);    }    symbolTo.attr('scale', [invScale * percent, invScale * percent]);  }  if (!label.ignore) {    label.attr('position', toPos);    var textPosition;    var textAlign;    var textVerticalAlign;    var textOrigin;    var distance = label.__labelDistance;    var distanceX = distance[0] * invScale;    var distanceY = distance[1] * invScale;    var halfPercent = percent / 2;    var tangent = line.tangentAt(halfPercent);    var n = [tangent[1], -tangent[0]];    var cp = line.pointAt(halfPercent);    if (n[1] > 0) {      n[0] = -n[0];      n[1] = -n[1];    }    var dir = tangent[0] < 0 ? -1 : 1;    if (label.__position !== 'start' && label.__position !== 'end') {      var rotation = -Math.atan2(tangent[1], tangent[0]);      if (toPos[0] < fromPos[0]) {        rotation = Math.PI + rotation;      }      label.attr('rotation', rotation);    }    var dy;    switch (label.__position) {      case 'insideStartTop':      case 'insideMiddleTop':      case 'insideEndTop':      case 'middle':        dy = -distanceY;        textVerticalAlign = 'bottom';        break;      case 'insideStartBottom':      case 'insideMiddleBottom':      case 'insideEndBottom':        dy = distanceY;        textVerticalAlign = 'top';        break;      default:        dy = 0;        textVerticalAlign = 'middle';    }    switch (label.__position) {      case 'end':        textPosition = [d[0] * distanceX + toPos[0], d[1] * distanceY + toPos[1]];        textAlign = d[0] > 0.8 ? 'left' : d[0] < -0.8 ? 'right' : 'center';        textVerticalAlign = d[1] > 0.8 ? 'top' : d[1] < -0.8 ? 'bottom' : 'middle';        break;      case 'start':        textPosition = [-d[0] * distanceX + fromPos[0], -d[1] * distanceY + fromPos[1]];        textAlign = d[0] > 0.8 ? 'right' : d[0] < -0.8 ? 'left' : 'center';        textVerticalAlign = d[1] > 0.8 ? 'bottom' : d[1] < -0.8 ? 'top' : 'middle';        break;      case 'insideStartTop':      case 'insideStart':      case 'insideStartBottom':        textPosition = [distanceX * dir + fromPos[0], fromPos[1] + dy];        textAlign = tangent[0] < 0 ? 'right' : 'left';        textOrigin = [-distanceX * dir, -dy];        break;      case 'insideMiddleTop':      case 'insideMiddle':      case 'insideMiddleBottom':      case 'middle':        textPosition = [cp[0], cp[1] + dy];        textAlign = 'center';        textOrigin = [0, -dy];        break;      case 'insideEndTop':      case 'insideEnd':      case 'insideEndBottom':        textPosition = [-distanceX * dir + toPos[0], toPos[1] + dy];        textAlign = tangent[0] >= 0 ? 'right' : 'left';        textOrigin = [distanceX * dir, -dy];        break;    }    label.attr({      style: {        // Use the user specified text align and baseline first        textVerticalAlign: label.__verticalAlign || textVerticalAlign,        textAlign: label.__textAlign || textAlign      },      position: textPosition,      scale: [invScale, invScale],      origin: textOrigin    });  }}/** * @constructor * @extends {module:zrender/graphic/Group} * @alias {module:echarts/chart/helper/Line} */function Line(lineData, idx, seriesScope) {  graphic.Group.call(this);  this._createLine(lineData, idx, seriesScope);}var lineProto = Line.prototype; // Update symbol position and rotationlineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;lineProto._createLine = function (lineData, idx, seriesScope) {  var seriesModel = lineData.hostModel;  var linePoints = lineData.getItemLayout(idx);  var line = createLine(linePoints);  line.shape.percent = 0;  graphic.initProps(line, {    shape: {      percent: 1    }  }, seriesModel, idx);  this.add(line);  var label = new graphic.Text({    name: 'label',    // FIXME    // Temporary solution for `focusNodeAdjacency`.    // line label do not use the opacity of lineStyle.    lineLabelOriginalOpacity: 1  });  this.add(label);  zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {    var symbol = createSymbol(symbolCategory, lineData, idx); // 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    this.add(symbol);    this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);  }, this);  this._updateCommonStl(lineData, idx, seriesScope);};lineProto.updateData = function (lineData, idx, seriesScope) {  var seriesModel = lineData.hostModel;  var line = this.childOfName('line');  var linePoints = lineData.getItemLayout(idx);  var target = {    shape: {}  };  setLinePoints(target.shape, linePoints);  graphic.updateProps(line, target, seriesModel, idx);  zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {    var symbolType = lineData.getItemVisual(idx, symbolCategory);    var key = makeSymbolTypeKey(symbolCategory); // Symbol changed    if (this[key] !== symbolType) {      this.remove(this.childOfName(symbolCategory));      var symbol = createSymbol(symbolCategory, lineData, idx);      this.add(symbol);    }    this[key] = symbolType;  }, this);  this._updateCommonStl(lineData, idx, seriesScope);};lineProto._updateCommonStl = function (lineData, idx, seriesScope) {  var seriesModel = lineData.hostModel;  var line = this.childOfName('line');  var lineStyle = seriesScope && seriesScope.lineStyle;  var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;  var labelModel = seriesScope && seriesScope.labelModel;  var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; // Optimization for large dataset  if (!seriesScope || lineData.hasItemOption) {    var itemModel = lineData.getItemModel(idx);    lineStyle = itemModel.getModel('lineStyle').getLineStyle();    hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle();    labelModel = itemModel.getModel('label');    hoverLabelModel = itemModel.getModel('emphasis.label');  }  var visualColor = lineData.getItemVisual(idx, 'color');  var visualOpacity = zrUtil.retrieve3(lineData.getItemVisual(idx, 'opacity'), lineStyle.opacity, 1);  line.useStyle(zrUtil.defaults({    strokeNoScale: true,    fill: 'none',    stroke: visualColor,    opacity: visualOpacity  }, lineStyle));  line.hoverStyle = hoverLineStyle; // Update symbol  zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {    var symbol = this.childOfName(symbolCategory);    if (symbol) {      symbol.setColor(visualColor);      symbol.setStyle({        opacity: visualOpacity      });    }  }, this);  var showLabel = labelModel.getShallow('show');  var hoverShowLabel = hoverLabelModel.getShallow('show');  var label = this.childOfName('label');  var defaultLabelColor;  var baseText; // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`.  if (showLabel || hoverShowLabel) {    defaultLabelColor = visualColor || '#000';    baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType);    if (baseText == null) {      var rawVal = seriesModel.getRawValue(idx);      baseText = rawVal == null ? lineData.getName(idx) : isFinite(rawVal) ? round(rawVal) : rawVal;    }  }  var normalText = showLabel ? baseText : null;  var emphasisText = hoverShowLabel ? zrUtil.retrieve2(seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType), baseText) : null;  var labelStyle = label.style; // Always set `textStyle` even if `normalStyle.text` is null, because default  // values have to be set on `normalStyle`.  if (normalText != null || emphasisText != null) {    graphic.setTextStyle(label.style, labelModel, {      text: normalText    }, {      autoColor: defaultLabelColor    });    label.__textAlign = labelStyle.textAlign;    label.__verticalAlign = labelStyle.textVerticalAlign; // 'start', 'middle', 'end'    label.__position = labelModel.get('position') || 'middle';    var distance = labelModel.get('distance');    if (!zrUtil.isArray(distance)) {      distance = [distance, distance];    }    label.__labelDistance = distance;  }  if (emphasisText != null) {    // Only these properties supported in this emphasis style here.    label.hoverStyle = {      text: emphasisText,      textFill: hoverLabelModel.getTextColor(true),      // For merging hover style to normal style, do not use      // `hoverLabelModel.getFont()` here.      fontStyle: hoverLabelModel.getShallow('fontStyle'),      fontWeight: hoverLabelModel.getShallow('fontWeight'),      fontSize: hoverLabelModel.getShallow('fontSize'),      fontFamily: hoverLabelModel.getShallow('fontFamily')    };  } else {    label.hoverStyle = {      text: null    };  }  label.ignore = !showLabel && !hoverShowLabel;  graphic.setHoverStyle(this);};lineProto.highlight = function () {  this.trigger('emphasis');};lineProto.downplay = function () {  this.trigger('normal');};lineProto.updateLayout = function (lineData, idx) {  this.setLinePoints(lineData.getItemLayout(idx));};lineProto.setLinePoints = function (points) {  var linePath = this.childOfName('line');  setLinePoints(linePath.shape, points);  linePath.dirty();};zrUtil.inherits(Line, graphic.Group);var _default = Line;module.exports = _default;
 |