/ *
* 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 .
* /
/ * *
* AUTO - GENERATED FILE . DO NOT MODIFY .
* /
/ *
* 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 .
* /
import { __ extends } from "tslib" ; // FIXME step not support polar
import * as zrUtil from 'zrender/lib/core/util.js' ;
import SymbolDraw from '../helper/SymbolDraw.js' ;
import SymbolClz from '../helper/Symbol.js' ;
import lineAnimationDiff from './lineAnimationDiff.js' ;
import * as graphic from '../../util/graphic.js' ;
import * as modelUtil from '../../util/model.js' ;
import { ECPolyline , ECPolygon } from './poly.js' ;
import ChartView from '../../view/Chart.js' ;
import { prepareDataCoordInfo , getStackedOnPoint } from './helper.js' ;
import { createGridClipPath , createPolarClipPath } from '../helper/createClipPathFromCoordSys.js' ;
import { isCoordinateSystemType } from '../../coord/CoordinateSystem.js' ;
import { setStatesStylesFromModel , setStatesFlag , toggleHoverEmphasis , SPECIAL_STATES } from '../../util/states.js' ;
import { setLabelStyle , getLabelStatesModels , labelInner } from '../../label/labelStyle.js' ;
import { getDefaultLabel , getDefaultInterpolatedLabel } from '../helper/labelHelper.js' ;
import { getECData } from '../../util/innerStore.js' ;
import { createFloat32Array } from '../../util/vendor.js' ;
import { convertToColorString } from '../../util/format.js' ;
import { lerp } from 'zrender/lib/tool/color.js' ;
function isPointsSame ( points1 , points2 ) {
if ( points1 . length !== points2 . length ) {
return ;
}
for ( var i = 0 ; i < points1 . length ; i ++ ) {
if ( points1 [ i ] !== points2 [ i ] ) {
return ;
}
}
return true ;
}
function bboxFromPoints ( points ) {
var minX = Infinity ;
var minY = Infinity ;
var maxX = - Infinity ;
var maxY = - Infinity ;
for ( var i = 0 ; i < points . length ; ) {
var x = points [ i ++ ] ;
var y = points [ i ++ ] ;
if ( ! isNaN ( x ) ) {
minX = Math . min ( x , minX ) ;
maxX = Math . max ( x , maxX ) ;
}
if ( ! isNaN ( y ) ) {
minY = Math . min ( y , minY ) ;
maxY = Math . max ( y , maxY ) ;
}
}
return [ [ minX , minY ] , [ maxX , maxY ] ] ;
}
function getBoundingDiff ( points1 , points2 ) {
var _ a = bboxFromPoints ( points1 ) ,
min1 = _ a [ 0 ] ,
max1 = _ a [ 1 ] ;
var _ b = bboxFromPoints ( points2 ) ,
min2 = _ b [ 0 ] ,
max2 = _ b [ 1 ] ; // Get a max value from each corner of two boundings.
return Math . max ( Math . abs ( min1 [ 0 ] - min2 [ 0 ] ) , Math . abs ( min1 [ 1 ] - min2 [ 1 ] ) , Math . abs ( max1 [ 0 ] - max2 [ 0 ] ) , Math . abs ( max1 [ 1 ] - max2 [ 1 ] ) ) ;
}
function getSmooth ( smooth ) {
return zrUtil . isNumber ( smooth ) ? smooth : smooth ? 0.5 : 0 ;
}
function getStackedOnPoints ( coordSys , data , dataCoordInfo ) {
if ( ! dataCoordInfo . valueDim ) {
return [ ] ;
}
var len = data . count ( ) ;
var points = createFloat32Array ( len * 2 ) ;
for ( var idx = 0 ; idx < len ; idx ++ ) {
var pt = getStackedOnPoint ( dataCoordInfo , coordSys , data , idx ) ;
points [ idx * 2 ] = pt [ 0 ] ;
points [ idx * 2 + 1 ] = pt [ 1 ] ;
}
return points ;
}
function turnPointsIntoStep ( points , coordSys , stepTurnAt , connectNulls ) {
var baseAxis = coordSys . getBaseAxis ( ) ;
var baseIndex = baseAxis . dim === 'x' || baseAxis . dim === 'radius' ? 0 : 1 ;
var stepPoints = [ ] ;
var i = 0 ;
var stepPt = [ ] ;
var pt = [ ] ;
var nextPt = [ ] ;
var filteredPoints = [ ] ;
if ( connectNulls ) {
for ( i = 0 ; i < points . length ; i += 2 ) {
if ( ! isNaN ( points [ i ] ) && ! isNaN ( points [ i + 1 ] ) ) {
filteredPoints . push ( points [ i ] , points [ i + 1 ] ) ;
}
}
points = filteredPoints ;
}
for ( i = 0 ; i < points . length - 2 ; i += 2 ) {
nextPt [ 0 ] = points [ i + 2 ] ;
nextPt [ 1 ] = points [ i + 3 ] ;
pt [ 0 ] = points [ i ] ;
pt [ 1 ] = points [ i + 1 ] ;
stepPoints . push ( pt [ 0 ] , pt [ 1 ] ) ;
switch ( stepTurnAt ) {
case 'end' :
stepPt [ baseIndex ] = nextPt [ baseIndex ] ;
stepPt [ 1 - baseIndex ] = pt [ 1 - baseIndex ] ;
stepPoints . push ( stepPt [ 0 ] , stepPt [ 1 ] ) ;
break ;
case 'middle' :
var middle = ( pt [ baseIndex ] + nextPt [ baseIndex ] ) / 2 ;
var stepPt2 = [ ] ;
stepPt [ baseIndex ] = stepPt2 [ baseIndex ] = middle ;
stepPt [ 1 - baseIndex ] = pt [ 1 - baseIndex ] ;
stepPt2 [ 1 - baseIndex ] = nextPt [ 1 - baseIndex ] ;
stepPoints . push ( stepPt [ 0 ] , stepPt [ 1 ] ) ;
stepPoints . push ( stepPt2 [ 0 ] , stepPt2 [ 1 ] ) ;
break ;
default :
// default is start
stepPt [ baseIndex ] = pt [ baseIndex ] ;
stepPt [ 1 - baseIndex ] = nextPt [ 1 - baseIndex ] ;
stepPoints . push ( stepPt [ 0 ] , stepPt [ 1 ] ) ;
}
} // Last points
stepPoints . push ( points [ i ++ ] , points [ i ++ ] ) ;
return stepPoints ;
}
/ * *
* Clip color stops to edge . Avoid creating too large gradients .
* Which may lead to blurry when GPU acceleration is enabled . See # 15680
*
* The stops has been sorted from small to large .
* /
function clipColorStops ( colorStops , maxSize ) {
var newColorStops = [ ] ;
var len = colorStops . length ; // coord will always < 0 in prevOutOfRangeColorStop.
var prevOutOfRangeColorStop ;
var prevInRangeColorStop ;
function lerpStop ( stop0 , stop1 , clippedCoord ) {
var coord0 = stop0 . coord ;
var p = ( clippedCoord - coord0 ) / ( stop1 . coord - coord0 ) ;
var color = lerp ( p , [ stop0 . color , stop1 . color ] ) ;
return {
coord : clippedCoord ,
color : color
} ;
}
for ( var i = 0 ; i < len ; i ++ ) {
var stop_1 = colorStops [ i ] ;
var coord = stop_1 . coord ;
if ( coord < 0 ) {
prevOutOfRangeColorStop = stop_1 ;
} else if ( coord > maxSize ) {
if ( prevInRangeColorStop ) {
newColorStops . push ( lerpStop ( prevInRangeColorStop , stop_1 , maxSize ) ) ;
} else if ( prevOutOfRangeColorStop ) {
// If there are two stops and coord range is between these two stops
newColorStops . push ( lerpStop ( prevOutOfRangeColorStop , stop_1 , 0 ) , lerpStop ( prevOutOfRangeColorStop , stop_1 , maxSize ) ) ;
} // All following stop will be out of range. So just ignore them.
break ;
} else {
if ( prevOutOfRangeColorStop ) {
newColorStops . push ( lerpStop ( prevOutOfRangeColorStop , stop_1 , 0 ) ) ; // Reset
prevOutOfRangeColorStop = null ;
}
newColorStops . push ( stop_1 ) ;
prevInRangeColorStop = stop_1 ;
}
}
return newColorStops ;
}
function getVisualGradient ( data , coordSys , api ) {
var visualMetaList = data . getVisual ( 'visualMeta' ) ;
if ( ! visualMetaList || ! visualMetaList . length || ! data . count ( ) ) {
// When data.count() is 0, gradient range can not be calculated.
return ;
}
if ( coordSys . type !== 'cartesian2d' ) {
if ( process . env . NODE_ENV !== 'production' ) {
console . warn ( 'Visual map on line style is only supported on cartesian2d.' ) ;
}
return ;
}
var coordDim ;
var visualMeta ;
for ( var i = visualMetaList . length - 1 ; i >= 0 ; i -- ) {
var dimInfo = data . getDimensionInfo ( visualMetaList [ i ] . dimension ) ;
coordDim = dimInfo && dimInfo . coordDim ; // Can only be x or y
if ( coordDim === 'x' || coordDim === 'y' ) {
visualMeta = visualMetaList [ i ] ;
break ;
}
}
if ( ! visualMeta ) {
if ( process . env . NODE_ENV !== 'production' ) {
console . warn ( 'Visual map on line style only support x or y dimension.' ) ;
}
return ;
} // If the area to be rendered is bigger than area defined by LinearGradient,
// the canvas spec prescribes that the color of the first stop and the last
// stop should be used. But if two stops are added at offset 0, in effect
// browsers use the color of the second stop to render area outside
// LinearGradient. So we can only infinitesimally extend area defined in
// LinearGradient to render `outerColors`.
var axis = coordSys . getAxis ( coordDim ) ; // dataToCoord mapping may not be linear, but must be monotonic.
var colorStops = zrUtil . map ( visualMeta . stops , function ( stop ) {
// offset will be calculated later.
return {
coord : axis . toGlobalCoord ( axis . dataToCoord ( stop . value ) ) ,
color : stop . color
} ;
} ) ;
var stopLen = colorStops . length ;
var outerColors = visualMeta . outerColors . slice ( ) ;
if ( stopLen && colorStops [ 0 ] . coord > colorStops [ stopLen - 1 ] . coord ) {
colorStops . reverse ( ) ;
outerColors . reverse ( ) ;
}
var colorStopsInRange = clipColorStops ( colorStops , coordDim === 'x' ? api . getWidth ( ) : api . getHeight ( ) ) ;
var inRangeStopLen = colorStopsInRange . length ;
if ( ! inRangeStopLen && stopLen ) {
// All stops are out of range. All will be the same color.
return colorStops [ 0 ] . coord < 0 ? outerColors [ 1 ] ? outerColors [ 1 ] : colorStops [ stopLen - 1 ] . color : outerColors [ 0 ] ? outerColors [ 0 ] : colorStops [ 0 ] . color ;
}
var tinyExtent = 10 ; // Arbitrary value: 10px
var minCoord = colorStopsInRange [ 0 ] . coord - tinyExtent ;
var maxCoord = colorStopsInRange [ inRangeStopLen - 1 ] . coord + tinyExtent ;
var coordSpan = maxCoord - minCoord ;
if ( coordSpan < 1e-3 ) {
return 'transparent' ;
}
zrUtil . each ( colorStopsInRange , function ( stop ) {
stop . offset = ( stop . coord - minCoord ) / coordSpan ;
} ) ;
colorStopsInRange . push ( {
// NOTE: inRangeStopLen may still be 0 if stoplen is zero.
offset : inRangeStopLen ? colorStopsInRange [ inRangeStopLen - 1 ] . offset : 0.5 ,
color : outerColors [ 1 ] || 'transparent'
} ) ;
colorStopsInRange . unshift ( {
offset : inRangeStopLen ? colorStopsInRange [ 0 ] . offset : 0.5 ,
color : outerColors [ 0 ] || 'transparent'
} ) ;
var gradient = new graphic . LinearGradient ( 0 , 0 , 0 , 0 , colorStopsInRange , true ) ;
gradient [ coordDim ] = minCoord ;
gradient [ coordDim + '2' ] = maxCoord ;
return gradient ;
}
function getIsIgnoreFunc ( seriesModel , data , coordSys ) {
var showAllSymbol = seriesModel . get ( 'showAllSymbol' ) ;
var isAuto = showAllSymbol === 'auto' ;
if ( showAllSymbol && ! isAuto ) {
return ;
}
var categoryAxis = coordSys . getAxesByScale ( 'ordinal' ) [ 0 ] ;
if ( ! categoryAxis ) {
return ;
} // Note that category label interval strategy might bring some weird effect
// in some scenario: users may wonder why some of the symbols are not
// displayed. So we show all symbols as possible as we can.
if ( isAuto // Simplify the logic, do not determine label overlap here.
&& canShowAllSymbolForCategory ( categoryAxis , data ) ) {
return ;
} // Otherwise follow the label interval strategy on category axis.
var categoryDataDim = data . mapDimension ( categoryAxis . dim ) ;
var labelMap = { } ;
zrUtil . each ( categoryAxis . getViewLabels ( ) , function ( labelItem ) {
var ordinalNumber = categoryAxis . scale . getRawOrdinalNumber ( labelItem . tickValue ) ;
labelMap [ ordinalNumber ] = 1 ;
} ) ;
return function ( dataIndex ) {
return ! labelMap . hasOwnProperty ( data . get ( categoryDataDim , dataIndex ) ) ;
} ;
}
function canShowAllSymbolForCategory ( categoryAxis , data ) {
// In most cases, line is monotonous on category axis, and the label size
// is close with each other. So we check the symbol size and some of the
// label size alone with the category axis to estimate whether all symbol
// can be shown without overlap.
var axisExtent = categoryAxis . getExtent ( ) ;
var availSize = Math . abs ( axisExtent [ 1 ] - axisExtent [ 0 ] ) / categoryAxis . scale . count ( ) ;
isNaN ( availSize ) && ( availSize = 0 ) ; // 0/0 is NaN.
// Sampling some points, max 5.
var dataLen = data . count ( ) ;
var step = Math . max ( 1 , Math . round ( dataLen / 5 ) ) ;
for ( var dataIndex = 0 ; dataIndex < dataLen ; dataIndex += step ) {
if ( SymbolClz . getSymbolSize ( data , dataIndex // Only for cartesian, where `isHorizontal` exists.
) [ categoryAxis . isHorizontal ( ) ? 1 : 0 ] // Empirical number
* 1.5 > availSize ) {
return false ;
}
}
return true ;
}
function isPointNull ( x , y ) {
return isNaN ( x ) || isNaN ( y ) ;
}
function getLastIndexNotNull ( points ) {
var len = points . length / 2 ;
for ( ; len > 0 ; len -- ) {
if ( ! isPointNull ( points [ len * 2 - 2 ] , points [ len * 2 - 1 ] ) ) {
break ;
}
}
return len - 1 ;
}
function getPointAtIndex ( points , idx ) {
return [ points [ idx * 2 ] , points [ idx * 2 + 1 ] ] ;
}
function getIndexRange ( points , xOrY , dim ) {
var len = points . length / 2 ;
var dimIdx = dim === 'x' ? 0 : 1 ;
var a ;
var b ;
var prevIndex = 0 ;
var nextIndex = - 1 ;
for ( var i = 0 ; i < len ; i ++ ) {
b = points [ i * 2 + dimIdx ] ;
if ( isNaN ( b ) || isNaN ( points [ i * 2 + 1 - dimIdx ] ) ) {
continue ;
}
if ( i === 0 ) {
a = b ;
continue ;
}
if ( a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY ) {
nextIndex = i ;
break ;
}
prevIndex = i ;
a = b ;
}
return {
range : [ prevIndex , nextIndex ] ,
t : ( xOrY - a ) / ( b - a )
} ;
}
function anyStateShowEndLabel ( seriesModel ) {
if ( seriesModel . get ( [ 'endLabel' , 'show' ] ) ) {
return true ;
}
for ( var i = 0 ; i < SPECIAL_STATES . length ; i ++ ) {
if ( seriesModel . get ( [ SPECIAL_STATES [ i ] , 'endLabel' , 'show' ] ) ) {
return true ;
}
}
return false ;
}
function createLineClipPath ( lineView , coordSys , hasAnimation , seriesModel ) {
if ( isCoordinateSystemType ( coordSys , 'cartesian2d' ) ) {
var endLabelModel_1 = seriesModel . getModel ( 'endLabel' ) ;
var valueAnimation_1 = endLabelModel_1 . get ( 'valueAnimation' ) ;
var data_1 = seriesModel . getData ( ) ;
var labelAnimationRecord_1 = {
lastFrameIndex : 0
} ;
var during = anyStateShowEndLabel ( seriesModel ) ? function ( percent , clipRect ) {
lineView . _ endLabelOnDuring ( percent , clipRect , data_1 , labelAnimationRecord_1 , valueAnimation_1 , endLabelModel_1 , coordSys ) ;
} : null ;
var isHorizontal = coordSys . getBaseAxis ( ) . isHorizontal ( ) ;
var clipPath = createGridClipPath ( coordSys , hasAnimation , seriesModel , function ( ) {
var endLabel = lineView . _ endLabel ;
if ( endLabel && hasAnimation ) {
if ( labelAnimationRecord_1 . originalX != null ) {
endLabel . attr ( {
x : labelAnimationRecord_1 . originalX ,
y : labelAnimationRecord_1 . originalY
} ) ;
}
}
} , during ) ; // Expand clip shape to avoid clipping when line value exceeds axis
if ( ! seriesModel . get ( 'clip' , true ) ) {
var rectShape = clipPath . shape ;
var expandSize = Math . max ( rectShape . width , rectShape . height ) ;
if ( isHorizontal ) {
rectShape . y -= expandSize ;
rectShape . height += expandSize * 2 ;
} else {
rectShape . x -= expandSize ;
rectShape . width += expandSize * 2 ;
}
} // Set to the final frame. To make sure label layout is right.
if ( during ) {
during ( 1 , clipPath ) ;
}
return clipPath ;
} else {
if ( process . env . NODE_ENV !== 'production' ) {
if ( seriesModel . get ( [ 'endLabel' , 'show' ] ) ) {
console . warn ( 'endLabel is not supported for lines in polar systems.' ) ;
}
}
return createPolarClipPath ( coordSys , hasAnimation , seriesModel ) ;
}
}
function getEndLabelStateSpecified ( endLabelModel , coordSys ) {
var baseAxis = coordSys . getBaseAxis ( ) ;
var isHorizontal = baseAxis . isHorizontal ( ) ;
var isBaseInversed = baseAxis . inverse ;
var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center' ;
var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom' ;
return {
normal : {
align : endLabelModel . get ( 'align' ) || align ,
verticalAlign : endLabelModel . get ( 'verticalAlign' ) || verticalAlign
}
} ;
}
var LineView =
/** @class */
function ( _ super ) {
__ extends ( LineView , _ super ) ;
function LineView ( ) {
return _ super !== null && _ super . apply ( this , arguments ) || this ;
}
LineView . prototype . init = function ( ) {
var lineGroup = new graphic . Group ( ) ;
var symbolDraw = new SymbolDraw ( ) ;
this . group . add ( symbolDraw . group ) ;
this . _ symbolDraw = symbolDraw ;
this . _ lineGroup = lineGroup ;
} ;
LineView . prototype . render = function ( seriesModel , ecModel , api ) {
var _ this = this ;
var coordSys = seriesModel . coordinateSystem ;
var group = this . group ;
var data = seriesModel . getData ( ) ;
var lineStyleModel = seriesModel . getModel ( 'lineStyle' ) ;
var areaStyleModel = seriesModel . getModel ( 'areaStyle' ) ;
var points = data . getLayout ( 'points' ) || [ ] ;
var isCoordSysPolar = coordSys . type === 'polar' ;
var prevCoordSys = this . _ coordSys ;
var symbolDraw = this . _ symbolDraw ;
var polyline = this . _ polyline ;
var polygon = this . _ polygon ;
var lineGroup = this . _ lineGroup ;
var hasAnimation = ! ecModel . ssr && seriesModel . isAnimationEnabled ( ) ;
var isAreaChart = ! areaStyleModel . isEmpty ( ) ;
var valueOrigin = areaStyleModel . get ( 'origin' ) ;
var dataCoordInfo = prepareDataCoordInfo ( coordSys , data , valueOrigin ) ;
var stackedOnPoints = isAreaChart && getStackedOnPoints ( coordSys , data , dataCoordInfo ) ;
var showSymbol = seriesModel . get ( 'showSymbol' ) ;
var connectNulls = seriesModel . get ( 'connectNulls' ) ;
var isIgnoreFunc = showSymbol && ! isCoordSysPolar && getIsIgnoreFunc ( seriesModel , data , coordSys ) ; // Remove temporary symbols
var oldData = this . _ data ;
oldData && oldData . eachItemGraphicEl ( function ( el , idx ) {
if ( el . __ temp ) {
group . remove ( el ) ;
oldData . setItemGraphicEl ( idx , null ) ;
}
} ) ; // Remove previous created symbols if showSymbol changed to false
if ( ! showSymbol ) {
symbolDraw . remove ( ) ;
}
group . add ( lineGroup ) ; // FIXME step not support polar
var step = ! isCoordSysPolar ? seriesModel . get ( 'step' ) : false ;
var clipShapeForSymbol ;
if ( coordSys && coordSys . getArea && seriesModel . get ( 'clip' , true ) ) {
clipShapeForSymbol = coordSys . getArea ( ) ; // Avoid float number rounding error for symbol on the edge of axis extent.
// See #7913 and `test/dataZoom-clip.html`.
if ( clipShapeForSymbol . width != null ) {
clipShapeForSymbol . x -= 0.1 ;
clipShapeForSymbol . y -= 0.1 ;
clipShapeForSymbol . width += 0.2 ;
clipShapeForSymbol . height += 0.2 ;
} else if ( clipShapeForSymbol . r0 ) {
clipShapeForSymbol . r0 -= 0.5 ;
clipShapeForSymbol . r += 0.5 ;
}
}
this . _ clipShapeForSymbol = clipShapeForSymbol ;
var visualColor = getVisualGradient ( data , coordSys , api ) || data . getVisual ( 'style' ) [ data . getVisual ( 'drawType' ) ] ; // Initialization animation or coordinate system changed
if ( ! ( polyline && prevCoordSys . type === coordSys . type && step === this . _ step ) ) {
showSymbol && symbolDraw . updateData ( data , {
isIgnore : isIgnoreFunc ,
clipShape : clipShapeForSymbol ,
disableAnimation : true ,
getSymbolPoint : function ( idx ) {
return [ points [ idx * 2 ] , points [ idx * 2 + 1 ] ] ;
}
} ) ;
hasAnimation && this . _ initSymbolLabelAnimation ( data , coordSys , clipShapeForSymbol ) ;
if ( step ) {
// TODO If stacked series is not step
points = turnPointsIntoStep ( points , coordSys , step , connectNulls ) ;
if ( stackedOnPoints ) {
stackedOnPoints = turnPointsIntoStep ( stackedOnPoints , coordSys , step , connectNulls ) ;
}
}
polyline = this . _ newPolyline ( points ) ;
if ( isAreaChart ) {
polygon = this . _ newPolygon ( points , stackedOnPoints ) ;
} // If areaStyle is removed
else if ( polygon ) {
lineGroup . remove ( polygon ) ;
polygon = this . _ polygon = null ;
} // NOTE: Must update _endLabel before setClipPath.
if ( ! isCoordSysPolar ) {
this . _ initOrUpdateEndLabel ( seriesModel , coordSys , convertToColorString ( visualColor ) ) ;
}
lineGroup . setClipPath ( createLineClipPath ( this , coordSys , true , seriesModel ) ) ;
} else {
if ( isAreaChart && ! polygon ) {
// If areaStyle is added
polygon = this . _ newPolygon ( points , stackedOnPoints ) ;
} else if ( polygon && ! isAreaChart ) {
// If areaStyle is removed
lineGroup . remove ( polygon ) ;
polygon = this . _ polygon = null ;
} // NOTE: Must update _endLabel before setClipPath.
if ( ! isCoordSysPolar ) {
this . _ initOrUpdateEndLabel ( seriesModel , coordSys , convertToColorString ( visualColor ) ) ;
} // Update clipPath
var oldClipPath = lineGroup . getClipPath ( ) ;
if ( oldClipPath ) {
var newClipPath = createLineClipPath ( this , coordSys , false , seriesModel ) ;
graphic . initProps ( oldClipPath , {
shape : newClipPath . shape
} , seriesModel ) ;
} else {
lineGroup . setClipPath ( createLineClipPath ( this , coordSys , true , seriesModel ) ) ;
} // Always update, or it is wrong in the case turning on legend
// because points are not changed.
showSymbol && symbolDraw . updateData ( data , {
isIgnore : isIgnoreFunc ,
clipShape : clipShapeForSymbol ,
disableAnimation : true ,
getSymbolPoint : function ( idx ) {
return [ points [ idx * 2 ] , points [ idx * 2 + 1 ] ] ;
}
} ) ; // In the case data zoom triggered refreshing frequently
// Data may not change if line has a category axis. So it should animate nothing.
if ( ! isPointsSame ( this . _ stackedOnPoints , stackedOnPoints ) || ! isPointsSame ( this . _ points , points ) ) {
if ( hasAnimation ) {
this . _ doUpdateAnimation ( data , stackedOnPoints , coordSys , api , step , valueOrigin , connectNulls ) ;
} else {
// Not do it in update with animation
if ( step ) {
// TODO If stacked series is not step
points = turnPointsIntoStep ( points , coordSys , step , connectNulls ) ;
if ( stackedOnPoints ) {
stackedOnPoints = turnPointsIntoStep ( stackedOnPoints , coordSys , step , connectNulls ) ;
}
}
polyline . setShape ( {
points : points
} ) ;
polygon && polygon . setShape ( {
points : points ,
stackedOnPoints : stackedOnPoints
} ) ;
}
}
}
var emphasisModel = seriesModel . getModel ( 'emphasis' ) ;
var focus = emphasisModel . get ( 'focus' ) ;
var blurScope = emphasisModel . get ( 'blurScope' ) ;
var emphasisDisabled = emphasisModel . get ( 'disabled' ) ;
polyline . useStyle ( zrUtil . defaults ( // Use color in lineStyle first
lineStyleModel . getLineStyle ( ) , {
fill : 'none' ,
stroke : visualColor ,
lineJoin : 'bevel'
} ) ) ;
setStatesStylesFromModel ( polyline , seriesModel , 'lineStyle' ) ;
if ( polyline . style . lineWidth > 0 && seriesModel . get ( [ 'emphasis' , 'lineStyle' , 'width' ] ) === 'bolder' ) {
var emphasisLineStyle = polyline . getState ( 'emphasis' ) . style ;
emphasisLineStyle . lineWidth = + polyline . style . lineWidth + 1 ;
} // Needs seriesIndex for focus
getECData ( polyline ) . seriesIndex = seriesModel . seriesIndex ;
toggleHoverEmphasis ( polyline , focus , blurScope , emphasisDisabled ) ;
var smooth = getSmooth ( seriesModel . get ( 'smooth' ) ) ;
var smoothMonotone = seriesModel . get ( 'smoothMonotone' ) ;
polyline . setShape ( {
smooth : smooth ,
smoothMonotone : smoothMonotone ,
connectNulls : connectNulls
} ) ;
if ( polygon ) {
var stackedOnSeries = data . getCalculationInfo ( 'stackedOnSeries' ) ;
var stackedOnSmooth = 0 ;
polygon . useStyle ( zrUtil . defaults ( areaStyleModel . getAreaStyle ( ) , {
fill : visualColor ,
opacity : 0.7 ,
lineJoin : 'bevel' ,
decal : data . getVisual ( 'style' ) . decal
} ) ) ;
if ( stackedOnSeries ) {
stackedOnSmooth = getSmooth ( stackedOnSeries . get ( 'smooth' ) ) ;
}
polygon . setShape ( {
smooth : smooth ,
stackedOnSmooth : stackedOnSmooth ,
smoothMonotone : smoothMonotone ,
connectNulls : connectNulls
} ) ;
setStatesStylesFromModel ( polygon , seriesModel , 'areaStyle' ) ; // Needs seriesIndex for focus
getECData ( polygon ) . seriesIndex = seriesModel . seriesIndex ;
toggleHoverEmphasis ( polygon , focus , blurScope , emphasisDisabled ) ;
}
var changePolyState = function ( toState ) {
_ this . _ changePolyState ( toState ) ;
} ;
data . eachItemGraphicEl ( function ( el ) {
// Switch polyline / polygon state if element changed its state.
el && ( el . onHoverStateChange = changePolyState ) ;
} ) ;
this . _ polyline . onHoverStateChange = changePolyState ;
this . _ data = data ; // Save the coordinate system for transition animation when data changed
this . _ coordSys = coordSys ;
this . _ stackedOnPoints = stackedOnPoints ;
this . _ points = points ;
this . _ step = step ;
this . _ valueOrigin = valueOrigin ;
if ( seriesModel . get ( 'triggerLineEvent' ) ) {
this . packEventData ( seriesModel , polyline ) ;
polygon && this . packEventData ( seriesModel , polygon ) ;
}
} ;
LineView . prototype . packEventData = function ( seriesModel , el ) {
getECData ( el ) . eventData = {
componentType : 'series' ,
componentSubType : 'line' ,
componentIndex : seriesModel . componentIndex ,
seriesIndex : seriesModel . seriesIndex ,
seriesName : seriesModel . name ,
seriesType : 'line'
} ;
} ;
LineView . prototype . highlight = function ( seriesModel , ecModel , api , payload ) {
var data = seriesModel . getData ( ) ;
var dataIndex = modelUtil . queryDataIndex ( data , payload ) ;
this . _ changePolyState ( 'emphasis' ) ;
if ( ! ( dataIndex instanceof Array ) && dataIndex != null && dataIndex >= 0 ) {
var points = data . getLayout ( 'points' ) ;
var symbol = data . getItemGraphicEl ( dataIndex ) ;
if ( ! symbol ) {
// Create a temporary symbol if it is not exists
var x = points [ dataIndex * 2 ] ;
var y = points [ dataIndex * 2 + 1 ] ;
if ( isNaN ( x ) || isNaN ( y ) ) {
// Null data
return ;
} // fix #11360: shouldn't draw symbol outside clipShapeForSymbol
if ( this . _ clipShapeForSymbol && ! this . _ clipShapeForSymbol . contain ( x , y ) ) {
return ;
}
var zlevel = seriesModel . get ( 'zlevel' ) || 0 ;
var z = seriesModel . get ( 'z' ) || 0 ;
symbol = new SymbolClz ( data , dataIndex ) ;
symbol . x = x ;
symbol . y = y ;
symbol . setZ ( zlevel , z ) ; // ensure label text of the temporary symbol is in front of line and area polygon
var symbolLabel = symbol . getSymbolPath ( ) . getTextContent ( ) ;
if ( symbolLabel ) {
symbolLabel . zlevel = zlevel ;
symbolLabel . z = z ;
symbolLabel . z2 = this . _ polyline . z2 + 1 ;
}
symbol . __ temp = true ;
data . setItemGraphicEl ( dataIndex , symbol ) ; // Stop scale animation
symbol . stopSymbolAnimation ( true ) ;
this . group . add ( symbol ) ;
}
symbol . highlight ( ) ;
} else {
// Highlight whole series
ChartView . prototype . highlight . call ( this , seriesModel , ecModel , api , payload ) ;
}
} ;
LineView . prototype . downplay = function ( seriesModel , ecModel , api , payload ) {
var data = seriesModel . getData ( ) ;
var dataIndex = modelUtil . queryDataIndex ( data , payload ) ;
this . _ changePolyState ( 'normal' ) ;
if ( dataIndex != null && dataIndex >= 0 ) {
var symbol = data . getItemGraphicEl ( dataIndex ) ;
if ( symbol ) {
if ( symbol . __ temp ) {
data . setItemGraphicEl ( dataIndex , null ) ;
this . group . remove ( symbol ) ;
} else {
symbol . downplay ( ) ;
}
}
} else {
// FIXME
// can not downplay completely.
// Downplay whole series
ChartView . prototype . downplay . call ( this , seriesModel , ecModel , api , payload ) ;
}
} ;
LineView . prototype . _ changePolyState = function ( toState ) {
var polygon = this . _ polygon ;
setStatesFlag ( this . _ polyline , toState ) ;
polygon && setStatesFlag ( polygon , toState ) ;
} ;
LineView . prototype . _ newPolyline = function ( points ) {
var polyline = this . _ polyline ; // Remove previous created polyline
if ( polyline ) {
this . _ lineGroup . remove ( polyline ) ;
}
polyline = new ECPolyline ( {
shape : {
points : points
} ,
segmentIgnoreThreshold : 2 ,
z2 : 10
} ) ;
this . _ lineGroup . add ( polyline ) ;
this . _ polyline = polyline ;
return polyline ;
} ;
LineView . prototype . _ newPolygon = function ( points , stackedOnPoints ) {
var polygon = this . _ polygon ; // Remove previous created polygon
if ( polygon ) {
this . _ lineGroup . remove ( polygon ) ;
}
polygon = new ECPolygon ( {
shape : {
points : points ,
stackedOnPoints : stackedOnPoints
} ,
segmentIgnoreThreshold : 2
} ) ;
this . _ lineGroup . add ( polygon ) ;
this . _ polygon = polygon ;
return polygon ;
} ;
LineView . prototype . _ initSymbolLabelAnimation = function ( data , coordSys , clipShape ) {
var isHorizontalOrRadial ;
var isCoordSysPolar ;
var baseAxis = coordSys . getBaseAxis ( ) ;
var isAxisInverse = baseAxis . inverse ;
if ( coordSys . type === 'cartesian2d' ) {
isHorizontalOrRadial = baseAxis . isHorizontal ( ) ;
isCoordSysPolar = false ;
} else if ( coordSys . type === 'polar' ) {
isHorizontalOrRadial = baseAxis . dim === 'angle' ;
isCoordSysPolar = true ;
}
var seriesModel = data . hostModel ;
var seriesDuration = seriesModel . get ( 'animationDuration' ) ;
if ( zrUtil . isFunction ( seriesDuration ) ) {
seriesDuration = seriesDuration ( null ) ;
}
var seriesDelay = seriesModel . get ( 'animationDelay' ) || 0 ;
var seriesDelayValue = zrUtil . isFunction ( seriesDelay ) ? seriesDelay ( null ) : seriesDelay ;
data . eachItemGraphicEl ( function ( symbol , idx ) {
var el = symbol ;
if ( el ) {
var point = [ symbol . x , symbol . y ] ;
var start = void 0 ;
var end = void 0 ;
var current = void 0 ;
if ( clipShape ) {
if ( isCoordSysPolar ) {
var polarClip = clipShape ;
var coord = coordSys . pointToCoord ( point ) ;
if ( isHorizontalOrRadial ) {
start = polarClip . startAngle ;
end = polarClip . endAngle ;
current = - coord [ 1 ] / 180 * Math . PI ;
} else {
start = polarClip . r0 ;
end = polarClip . r ;
current = coord [ 0 ] ;
}
} else {
var gridClip = clipShape ;
if ( isHorizontalOrRadial ) {
start = gridClip . x ;
end = gridClip . x + gridClip . width ;
current = symbol . x ;
} else {
start = gridClip . y + gridClip . height ;
end = gridClip . y ;
current = symbol . y ;
}
}
}
var ratio = end === start ? 0 : ( current - start ) / ( end - start ) ;
if ( isAxisInverse ) {
ratio = 1 - ratio ;
}
var delay = zrUtil . isFunction ( seriesDelay ) ? seriesDelay ( idx ) : seriesDuration * ratio + seriesDelayValue ;
var symbolPath = el . getSymbolPath ( ) ;
var text = symbolPath . getTextContent ( ) ;
el . attr ( {
scaleX : 0 ,
scaleY : 0
} ) ;
el . animateTo ( {
scaleX : 1 ,
scaleY : 1
} , {
duration : 200 ,
setToFinal : true ,
delay : delay
} ) ;
if ( text ) {
text . animateFrom ( {
style : {
opacity : 0
}
} , {
duration : 300 ,
delay : delay
} ) ;
}
symbolPath . disableLabelAnimation = true ;
}
} ) ;
} ;
LineView . prototype . _ initOrUpdateEndLabel = function ( seriesModel , coordSys , inheritColor ) {
var endLabelModel = seriesModel . getModel ( 'endLabel' ) ;
if ( anyStateShowEndLabel ( seriesModel ) ) {
var data_2 = seriesModel . getData ( ) ;
var polyline = this . _ polyline ; // series may be filtered.
var points = data_2 . getLayout ( 'points' ) ;
if ( ! points ) {
polyline . removeTextContent ( ) ;
this . _ endLabel = null ;
return ;
}
var endLabel = this . _ endLabel ;
if ( ! endLabel ) {
endLabel = this . _ endLabel = new graphic . Text ( {
z2 : 200 // should be higher than item symbol
} ) ;
endLabel . ignoreClip = true ;
polyline . setTextContent ( this . _ endLabel ) ;
polyline . disableLabelAnimation = true ;
} // Find last non-NaN data to display data
var dataIndex = getLastIndexNotNull ( points ) ;
if ( dataIndex >= 0 ) {
setLabelStyle ( polyline , getLabelStatesModels ( seriesModel , 'endLabel' ) , {
inheritColor : inheritColor ,
labelFetcher : seriesModel ,
labelDataIndex : dataIndex ,
defaultText : function ( dataIndex , opt , interpolatedValue ) {
return interpolatedValue != null ? getDefaultInterpolatedLabel ( data_2 , interpolatedValue ) : getDefaultLabel ( data_2 , dataIndex ) ;
} ,
enableTextSetter : true
} , getEndLabelStateSpecified ( endLabelModel , coordSys ) ) ;
polyline . textConfig . position = null ;
}
} else if ( this . _ endLabel ) {
this . _ polyline . removeTextContent ( ) ;
this . _ endLabel = null ;
}
} ;
LineView . prototype . _ endLabelOnDuring = function ( percent , clipRect , data , animationRecord , valueAnimation , endLabelModel , coordSys ) {
var endLabel = this . _ endLabel ;
var polyline = this . _ polyline ;
if ( endLabel ) {
// NOTE: Don't remove percent < 1. percent === 1 means the first frame during render.
// The label is not prepared at this time.
if ( percent < 1 && animationRecord . originalX == null ) {
animationRecord . originalX = endLabel . x ;
animationRecord . originalY = endLabel . y ;
}
var points = data . getLayout ( 'points' ) ;
var seriesModel = data . hostModel ;
var connectNulls = seriesModel . get ( 'connectNulls' ) ;
var precision = endLabelModel . get ( 'precision' ) ;
var distance = endLabelModel . get ( 'distance' ) || 0 ;
var baseAxis = coordSys . getBaseAxis ( ) ;
var isHorizontal = baseAxis . isHorizontal ( ) ;
var isBaseInversed = baseAxis . inverse ;
var clipShape = clipRect . shape ;
var xOrY = isBaseInversed ? isHorizontal ? clipShape . x : clipShape . y + clipShape . height : isHorizontal ? clipShape . x + clipShape . width : clipShape . y ;
var distanceX = ( isHorizontal ? distance : 0 ) * ( isBaseInversed ? - 1 : 1 ) ;
var distanceY = ( isHorizontal ? 0 : - distance ) * ( isBaseInversed ? - 1 : 1 ) ;
var dim = isHorizontal ? 'x' : 'y' ;
var dataIndexRange = getIndexRange ( points , xOrY , dim ) ;
var indices = dataIndexRange . range ;
var diff = indices [ 1 ] - indices [ 0 ] ;
var value = void 0 ;
if ( diff >= 1 ) {
// diff > 1 && connectNulls, which is on the null data.
if ( diff > 1 && ! connectNulls ) {
var pt = getPointAtIndex ( points , indices [ 0 ] ) ;
endLabel . attr ( {
x : pt [ 0 ] + distanceX ,
y : pt [ 1 ] + distanceY
} ) ;
valueAnimation && ( value = seriesModel . getRawValue ( indices [ 0 ] ) ) ;
} else {
var pt = polyline . getPointOn ( xOrY , dim ) ;
pt && endLabel . attr ( {
x : pt [ 0 ] + distanceX ,
y : pt [ 1 ] + distanceY
} ) ;
var startValue = seriesModel . getRawValue ( indices [ 0 ] ) ;
var endValue = seriesModel . getRawValue ( indices [ 1 ] ) ;
valueAnimation && ( value = modelUtil . interpolateRawValues ( data , precision , startValue , endValue , dataIndexRange . t ) ) ;
}
animationRecord . lastFrameIndex = indices [ 0 ] ;
} else {
// If diff <= 0, which is the range is not found(Include NaN)
// Choose the first point or last point.
var idx = percent === 1 || animationRecord . lastFrameIndex > 0 ? indices [ 0 ] : 0 ;
var pt = getPointAtIndex ( points , idx ) ;
valueAnimation && ( value = seriesModel . getRawValue ( idx ) ) ;
endLabel . attr ( {
x : pt [ 0 ] + distanceX ,
y : pt [ 1 ] + distanceY
} ) ;
}
if ( valueAnimation ) {
var inner = labelInner ( endLabel ) ;
if ( typeof inner . setLabelText === 'function' ) {
inner . setLabelText ( value ) ;
}
}
}
} ;
/ * *
* @ private
* /
// FIXME Two value axis
LineView . prototype . _ doUpdateAnimation = function ( data , stackedOnPoints , coordSys , api , step , valueOrigin , connectNulls ) {
var polyline = this . _ polyline ;
var polygon = this . _ polygon ;
var seriesModel = data . hostModel ;
var diff = lineAnimationDiff ( this . _ data , data , this . _ stackedOnPoints , stackedOnPoints , this . _ coordSys , coordSys , this . _ valueOrigin , valueOrigin ) ;
var current = diff . current ;
var stackedOnCurrent = diff . stackedOnCurrent ;
var next = diff . next ;
var stackedOnNext = diff . stackedOnNext ;
if ( step ) {
// TODO If stacked series is not step
current = turnPointsIntoStep ( diff . current , coordSys , step , connectNulls ) ;
stackedOnCurrent = turnPointsIntoStep ( diff . stackedOnCurrent , coordSys , step , connectNulls ) ;
next = turnPointsIntoStep ( diff . next , coordSys , step , connectNulls ) ;
stackedOnNext = turnPointsIntoStep ( diff . stackedOnNext , coordSys , step , connectNulls ) ;
} // Don't apply animation if diff is large.
// For better result and avoid memory explosion problems like
// https://github.com/apache/incubator-echarts/issues/12229
if ( getBoundingDiff ( current , next ) > 3000 || polygon && getBoundingDiff ( stackedOnCurrent , stackedOnNext ) > 3000 ) {
polyline . stopAnimation ( ) ;
polyline . setShape ( {
points : next
} ) ;
if ( polygon ) {
polygon . stopAnimation ( ) ;
polygon . setShape ( {
points : next ,
stackedOnPoints : stackedOnNext
} ) ;
}
return ;
}
polyline . shape . __ points = diff . current ;
polyline . shape . points = current ;
var target = {
shape : {
points : next
}
} ; // Also animate the original points.
// If points reference is changed when turning into step line.
if ( diff . current !== current ) {
target . shape . __ points = diff . next ;
} // Stop previous animation.
polyline . stopAnimation ( ) ;
graphic . updateProps ( polyline , target , seriesModel ) ;
if ( polygon ) {
polygon . setShape ( {
// Reuse the points with polyline.
points : current ,
stackedOnPoints : stackedOnCurrent
} ) ;
polygon . stopAnimation ( ) ;
graphic . updateProps ( polygon , {
shape : {
stackedOnPoints : stackedOnNext
}
} , seriesModel ) ; // If use attr directly in updateProps.
if ( polyline . shape . points !== polygon . shape . points ) {
polygon . shape . points = polyline . shape . points ;
}
}
var updatedDataInfo = [ ] ;
var diffStatus = diff . status ;
for ( var i = 0 ; i < diffStatus . length ; i ++ ) {
var cmd = diffStatus [ i ] . cmd ;
if ( cmd === '=' ) {
var el = data . getItemGraphicEl ( diffStatus [ i ] . idx1 ) ;
if ( el ) {
updatedDataInfo . push ( {
el : el ,
ptIdx : i // Index of points
} ) ;
}
}
}
if ( polyline . animators && polyline . animators . length ) {
polyline . animators [ 0 ] . during ( function ( ) {
polygon && polygon . dirtyShape ( ) ;
var points = polyline . shape . __ points ;
for ( var i = 0 ; i < updatedDataInfo . length ; i ++ ) {
var el = updatedDataInfo [ i ] . el ;
var offset = updatedDataInfo [ i ] . ptIdx * 2 ;
el . x = points [ offset ] ;
el . y = points [ offset + 1 ] ;
el . markRedraw ( ) ;
}
} ) ;
}
} ;
LineView . prototype . remove = function ( ecModel ) {
var group = this . group ;
var oldData = this . _ data ;
this . _ lineGroup . removeAll ( ) ;
this . _ symbolDraw . remove ( true ) ; // Remove temporary created elements when highlighting
oldData && oldData . eachItemGraphicEl ( function ( el , idx ) {
if ( el . __ temp ) {
group . remove ( el ) ;
oldData . setItemGraphicEl ( idx , null ) ;
}
} ) ;
this . _ polyline = this . _ polygon = this . _ coordSys = this . _ points = this . _ stackedOnPoints = this . _ endLabel = this . _ data = null ;
} ;
LineView . type = 'line' ;
return LineView ;
} ( ChartView ) ;
export default LineView ;