> > But I prefer not to distinguish "whether series type changed" when > describing the requirements. For examples, case "DIVISION_ASSEMBLY_C" in > the first email, it is essentially `one-one` data mapping within each > `one-many` series mapping. Whether the series type changes or not ("bar" to > "bar" or "bar" to other), the "old series" and the "new series" should be > different series (because of the `one-many` series mapping).
Yeah, it's not precise to use the term "series type changed" in the previous email. Actually I mean the view and model of series have been changed. So the "DIVISION_ASSEMBLY_C" should be in case 4. On Fri, Apr 10, 2020 at 2:07 AM SHUANG SU <sushuang0...@gmail.com> wrote: > Hi Yi, > > Trill-down and roll-up is a great and important case in this topic. > > > I am thinking we should probably better finally come up with a "*simple > concept*" from user perspective so that users can understand it easily. > And the concept should also be correspondingly "powerful"/"flexible" so > that users can manipulate it creatively for their various cases. > > For example, wrapping up the cases we already have in the previous posts, > could the "simple but powerful concept" about "transition" be one sentence: > > *"If you want to make transition, you only need to specify some keys to > define the data mappings from the old series to new series."* > > > > And then the details of the concept can be expanded: > > **[ What is "old series" and "new series"? ]** > When you calling `setOption`, the series existed before the calling are > "old series", whilst the series existing after that calling are "new > series". > If a series is not deleted after `setOption` called, it belongs to both > "old series" and "new series". > > **[ What if we do not provide the keys? ]** > If an "old series" and a "new series" is the same one, the key would be by > default datum name or datum id if possible. > Otherwise, no transition. > That is what we already have in echarts4. > > **[ What is the keys and what is data mappings? ]** > Key is what echarts depends on to make mappings. > There are two levels of mappings: `series mapping` and `data mapping`. > (1). *<SERIES MAPPING>*: > The mapping from "old series" to "new series". > It can be done explicitly or implicitly. > If a series do not be deleted after `setOption` called, it by default have > that mapping from the old self to the new self. > Otherwise, if a series is deleted by `setOption` but a new one created with > the same `seriesId` or `seriesName`, the mapping should also be built > automatically, no matter whether they have the same `seriesType` or not. In > this case I prefer not to make auto mapping by `seriesIndex`, being afraid > of unexpected mapping. > Note that if auto-mapped by `seriesName`, it is possible that the mapping > is `many-series-to-one-series` or a `one-series-to-many-series` (say > `one-many` for short), which should be allowed and useful in some cases, > like transit from a bar series to several pie series, or vise versa. > Finally, users should be allowed to specify series mapping explicitly in > API, where one-many mapping are also available. > (2). *<DATA MAPPING>*: > The mappings from "old data" to "new data" within a given "series mapping". > Having a series mapping established, echarts get a scope to build data > mappings. The old datum and the new datum will be mapped if they have the > same value on key. The key can be "datum name" or "datum id" or the values > in a certain dimension on the data source. > Notice the a datum can be mapped to any datum within the scope defined by > the series mapping, no matter whether crosses series. > And data mapping can also be `one-one` or `one-many`. > > **[ How to specify the keys? ]** > That is an issue in practice level, not totally sure yet. But at least we > already have: > Keys can be specified in the second parameter of `setOption` explicitly. > If users use aggregation, the key could be the aggregation key. > > That all about the "concept" from user perspective. > > > -------------------------------------------------------- > > > Next, let's check the "concept" in the cases the last two email mentioned: > > > *1. The series type is not changed. Only data changes.* > > If explain it by the "concept", that is the "old series" and the "new > series" are the same one, and the keys are datum name / datum name, one-one > data mapping. > > > *2. The series type is changed.* Transition between two different types > of series is more difficult because the view and model have been changed. > ... > > If explain it by the "concept", that is the "old series" and the "new > series" are not the same one but auto mapped by `seriesId` or `seriesName`. > The original intention of this case might only cover `one-one` data > mappings within each `one-one` series mapping. > > > 3. The series type is not changed. But new data is a subset or superset. > * The very common scenario is drill-down and roll-up. Data is usually being > aggregated from one single source. > > If explain it by the "concept", that is the "old series" and the "new > series" are the same one, and `one-many` data mappings exists within each > `one-one` series mapping. `one-many` data mappings requires "split" and > "merge" transition. > > > 4. *Series type is changed when drill-down, roll-up.* For example, a > rectangle in the bar series is animated to multiple sectors in another pie > series. In this case, we need to combine the strategy used in both 2 and 3. > > If explain it by the "concept", that is the "old series" and the "new > series" are not the same one, and `one-one` data mappings exists within > each `one-many` series mapping. > > But I prefer not to distinguish "whether series type changed" when > describing the requirements. For examples, case "DIVISION_ASSEMBLY_C" in > the first email, it is essentially `one-one` data mapping within each > `one-many` series mapping. Whether the series type changes or not ("bar" to > "bar" or "bar" to other), the "old series" and the "new series" should be > different series (because of the `one-many` series mapping). > > > > Thanks, > ------------------------------ > Su Shuang (100pah) > ------------------------------ > > > > On Thu, 9 Apr 2020 at 13:51, Yi Shen <shenyi....@gmail.com> wrote: > > > Hi Shuang, It's a very detailed explanation for the scenarios. > > > > I like the idea of specifying mapping rules from the old option to the > new > > option in the second parameter. > > It gives developers a lot of flexibility, things like which series should > > be deleted can be easily achieved. > > I think it's a general solution of model preparing, should not > > be restricted in the transition domain. > > Perhaps we can discuss the specification of this mapping rule in a > separate > > thread. > > > > About the strategy. Here are 4 scenarios I concluded. > > > > *1. The series type is not changed. Only data changes.* > > Currently, we only handle this situation by mapping data with name / id / > > index as keys. > > > > *2. The series type is changed.* > > Transition between two different types of series is more difficult > because > > the view and model have been changed. > > In this case, we need to keep the removed series and diff the data with > the > > new series. The mapping key can be seriesId / seriesName / seriesIndex. > > And usually, we need to morph between two different shapes when series > type > > is changed. > > > > *3. The series type is not changed. But new data is a subset or > superset. * > > The very common scenario is drill-down and drill-up. Data is usually > being > > aggregated from one single source. > > In this case, we need to find a key used in the aggregation algorithm. > > For example, the `sex` field used in `groupBy` method can also be used to > > map from one source element to multiple target elements and apply the > > animation. > > > > If we apply the aggregation algorithm in the dataset transform. As I > > mentioned in [1]. It's easy to get the key. > > But if developers do the aggregation outside and only give the data. They > > should specify the key explicitly. > > > > 4. *Series type is changed when drill-down, drill-up.* > > For example, a rectangle in the bar series is animated to multiple > sectors > > in another pie series. In this case, we need to combine the strategy used > > in both 2 and 3. > > > > In these four scenarios, I think it's not necessary to add > > prepareCrossTransition > > stage*.* The mapping comes from the information collected during model > > preparation. > > > > About the CrossTransitionManager you mentioned. I'm thinking perhaps we > > need a structure to manage the mapping of data and graphic elements. > > Instead of storing the graphic element in the data directly. > > > > [1] > > > > > https://lists.apache.org/thread.html/r0cc1ead9c131031fc18fc10ff7a7a7ffa0718c5bd5b226d70e9ee192%40%3Cdev.echarts.apache.org%3E > > > > On Thu, Apr 9, 2020 at 2:31 AM SHUANG SU <sushuang0...@gmail.com> wrote: > > > > > Transition > > > > > > Transition animation will enhance the customization power and would be > > one > > > of the infrastructures for storytelling capability. > > > > > > > > > > > > ------------------------------ > > > Framework of transition related API > > > > > > Via setOption: > > > > > > // Forward > > > chart.setOption(optionA); > > > chart.setOption(optionB); > > > chart.setOption(optionC); > > > chart.setOption(optionD); > > > // Backward > > > chart.setOption(optionD); > > > chart.setOption(optionC); > > > chart.setOption(optionB); > > > chart.setOption(optionA); > > > // Note that this sentences above are just illustrative.// in practice > > > those sentences should commonly be// triggered by user behaviors > > > rather than called// synchronously. > > > > > > Note that if intending to keep some of the component in merge mode, the > > > capacity of deleting part of the components (or hide components) should > > be > > > implemented: Probably: > > > > > > chart.setOption({ > > > // Full option > > > }); > > > chart.setOption({ > > > series: [ > > > {id: 'ser0'}, > > > {id: 'ser1'}, > > > ] > > > // Only replace series > > > }, {replace: 'series'}); > > > chart.setOption({ > > > dataset: { ... }, > > > series: [ > > > {id: 'ser0'}, > > > {id: 'ser1'}, > > > ] > > > // Only replace series and dataset > > > }, {replace: ['series', 'dataset']}); > > > > > > > > > > > > > > > ------------------------------ > > > Transition casesTERMS > > > > > > I am not sure whether these terms below are appropriate. Before better > > one > > > are promoted, we use them in this thread. > > > > > > - Transform/Transformation: means transit a data item to another > data > > > item one by one. > > > - Division/Divide: means split a data item to some of data items, > > which > > > might belong to different new series. > > > - Assembly/Assemble: the opposite of division. > > > > > > Case OTHERS > > > > > > These cases below are correspondingly clear so we do not need to make > > > further discussion. > > > > > > - Use dispatchAction to trigger "dataZoom" or "geoRoam" with > > animation. > > > - Dynamic data as we already have. > > > > > > Case TRANSFORMATION_A > > > > > > var optionA = { > > > dataset: {...}, > > > xAxis: {id: 'A'}, > > > yAxis: {id: 'A'}, > > > series: [{ > > > id: 'serA', > > > xAxisId: 'A', > > > yAxisId: 'A', > > > type: 'scatter' > > > }] > > > };var optionB = { > > > xAxis: {id: 'B'}, > > > yAxis: {id: 'B'}, > > > series: [{ > > > id: 'serB', > > > xAxisId: 'B', > > > yAxisId: 'B', > > > type: 'bar', > > > }] > > > };var optionC = { > > > xAxis: {id: 'C'}, > > > yAxis: {id: 'C'}, > > > series: [{ > > > id: 'serC', > > > xAxisId: 'C', > > > yAxisId: 'C', > > > type: 'line' > > > }] > > > }; > > > > > > If we want the transition to be: Use the same data, but transit the > > > elements to another coordinate system. From scatter to bar, can be > morph. > > > From bar to line, can be fade out/in. Also consider, the case: a series > > > change its coordinate system from cartesian to polar, where morph may > > need > > > to be applied. > > > Case TRANSFORMATION_B > > > > > > var optionA = { > > > dataset: { > > > source: [ > > > // dim1 dim2 dim3 > > > ['2020-03-01', 32, 4413, 0.17 ], > > > ['2020-03-02', 42, 1423, 0.47 ], > > > ['2020-03-03', 62, 5467, 0.87 ], > > > ['2020-03-04', 12, 1498, 0.17 ], > > > ['2020-03-05', 52, 2435, 0.57 ], > > > ] > > > }, > > > xAxis: {}, > > > yAxis: {}, > > > series: [{ > > > type: 'scatter', > > > coordinateSystem: 'cartesian', > > > encode: {x: 0, y: 1} > > > }] > > > };var optionB = { > > > series: [{ > > > // Update the series: encode.y is modified to `2`. > > > encode: {x: 0, y: 2} > > > }] > > > }; > > > > > > If we want the transition to be: The the same coordinate system. But > map > > a > > > different dimension to yAxis. The scatter points should be transformed > to > > > the new location in the same coordinate system and the yAxis should be > > > transformed properly. > > > Case DIVISION_ASSEMBLY_A > > > > > > var optionA = { > > > dataset: { > > > id: 'dsA', > > > source: [ > > > // Date Month HP Category > > > ['2020-03-29', 3, 32, 'Rice' ], // item0 > > > ['2020-03-30', 3, 42, 'Pizza' ], // item1 > > > ['2020-03-31', 3, 62, 'Noodles' ], // item2 > > > ['2020-04-01', 4, 18, 'Rice' ], // item3 > > > ['2020-04-02', 4, 52, 'Pizza' ], // item4 > > > ] > > > }, > > > series: [ > > > {id: 'ser0', type: 'bar', datasetId: 'dsA', encode: {x: 0, y: > 2}} > > > ] > > > };var optionB = { > > > dataset: { > > > id: 'dsB', > > > source: [ > > > // HP Category > > > [ 50, 'Rice' ], // dsA.item0 + dsA.item3 > > > [ 94, 'Pizza' ], // dsA.item1 + dsA.item4 > > > [ 62, 'Noodles' ], // dsA.item2 > > > ] > > > }, > > > series: [ > > > {id: 'ser1', type: 'bar', datasetId: 'dsB', encode: {x: 1, y: > 0}} > > > ] > > > }; > > > > > > If we want the transition to be: Bars of "ser0" are assembled by > > "Category" > > > and transit to "ser1". Or event, consider there is: > > > > > > var optionC = { > > > dataset: { > > > id: 'dsB', > > > source: [ > > > // Month HP > > > [ 3, 136, ], // dsA.item0 + dsA.item1 + dsA.item2 > > > [ 4, 70, ], // dsA.item3 + dsA.item4 > > > ] > > > }, > > > series: [ > > > {id: 'ser2', type: 'bar', datasetId: 'dsC', encode: {x: 1, y: > 0}} > > > ] > > > }; > > > > > > Can we make transition from optionB to optionC, where both division and > > > assembly happen? > > > Case DIVISION_ASSEMBLY_B > > > > > > var optionA = { > > > dataset: { > > > source: [ > > > // X Y1=Y2+Y3 Y2 Y3 > > > ['2020-03-01', 32, 12, 20 ], > > > ['2020-03-02', 42, 22, 20 ], > > > ['2020-03-03', 62, 30, 32 ], > > > ['2020-03-04', 18, 2, 16 ], > > > ['2020-03-05', 52, 12, 40 ], > > > ] > > > }, > > > series: [ > > > {id: 'ser0', type: 'bar', encode: {x: 0, y: 1}} > > > ] > > > };var optionB = { > > > series: [ > > > {id: 'ser1', type: 'bar', encode: {x: 0, y: 2}}, > > > {id: 'ser2', type: 'bar', encode: {x: 0, y: 3}} > > > ] > > > }; > > > > > > If we want the transition to be: Each bar of "ser0" is divided to a bar > > of > > > "ser1" and a bar of "ser2". > > > Case DIVISION_ASSEMBLY_C > > > > > > var optionA = { > > > dataset: [{ > > > id: 'dsA', > > > source: [ > > > // X Y_ser0 Y_ser1 Y_ser2 > > > ['2020-03-01', 32, 44, 17 ], > > > ['2020-03-02', 42, 14, 47 ], > > > ['2020-03-03', 62, 54, 87 ], > > > ['2020-03-04', 12, 14, 17 ], > > > ['2020-03-05', 52, 24, 57 ], > > > ] > > > }, { > > > id: 'dsB', > > > source: [ > > > ['Breakfast', 200], // sum of "dsA" dimension 1. > > > ['Lunch', 150], // sum of "dsA" dimension 2. > > > ['Supper', 225] // sum of "dsA" dimension 3. > > > ] > > > }], > > > series: [{ > > > // Also consider the type can be 'line' with `areaStyle: {}`, > > > // where there is only one polygon for a series. > > > id: 'ser0', type: 'bar', stack: 's', datasetId: 'dsA', > > > name: 'Breakfast', encode: {x: 0, y: 1} > > > }, { > > > id: 'ser1', type: 'bar', stack: 's', datasetId: 'dsA', > > > name: 'Lunch', encode: {x: 0, y: 2} > > > }, { > > > id: 'ser2', type: 'bar', stack: 's', datasetId: 'dsA', > > > name: 'Supper', encode: {x: 0, y: 3} > > > }] > > > };var optionB = { > > > series: [{ > > > id: 'ser3', type: 'bar', name: 'sum', datasetId: 'dsB', > > > encode: {x: 0, y: 1} > > > }] > > > }; > > > > > > If we want the transition to be: bars from "ser0" are assembled to > "ser3" > > > bar0, bars from "ser1" are assembled to "ser3" bar1, bars from "ser2" > are > > > assembled to "ser3" bar2, or vice versa (division). That is, the entire > > > series of "ser0"/"ser1"/"ser2" are mapped to an datum of "ser3". > > > Proposed transition API > > > > > > How to describe this mapping relationship in option? > > > > > > The key point is how to map the old graphic element to the new one. But > > > follow the conventional principle, we should better firstly consider to > > > describe the mapping in data rather than expose the concept "graphic > > > element" to users. > > > > > > Moreover, since the description of transition might be volatile rather > > than > > > persistent (only works while the setOption being called), probably we > > > should not put the "transition description" in optoin. Instead, put it > as > > > one extra optional param of setOption, which has the same life-cycle as > > an > > > Payload object. (But I am not totally sure about that, whether put the > > > "transition" setting in option or in setOption param?) > > > > > > A proposed transition API to cover the cases above would be: > > > > > > chart.setOption( > > > newOption, > > > { > > > // Transition mapping rules. Can be > > > // a single rule (object) or rules (Array<object>). > > > // For a single series, only one rule (from top to bottom) > > > // can be accepted. > > > transition: [ > > > { > > > from: {seriesId: 'ser0', dimension: 1}, > > > to: [ > > > {seriesId: 'ser1', dimension: 1}, > > > {seriesId: 'ser2', dimension: 2}, > > > {seriesId: 'ser3', dimension: 3} > > > ] > > > }, > > > ... > > > ], > > > // If `true`, transit from "to" to "from". > > > transitionBackward: true > > > } > > > ); > > > > > > Take the cases above as examples: > > > > > > // Case TRANSFORMATION_A > > > chart.setOption( > > > newOption, > > > { > > > replace: ['series', 'xAxis', 'yAxis', 'grid'], > > > transition: { > > > // By default, mapping by index. > > > from: {seriesId: 'serA'}, > > > to: {seriesId: 'serB'} > > > } > > > } > > > );// Case DIVISION_ASSEMBLY_A > > > chart.setOption( > > > newOption, > > > { > > > replace: 'series', > > > transition: { > > > // Use the category dimension as the mapping key. > > > from: {seriesId: 'ser0', mapOnDimension: 3}, > > > to: {seriesId: 'ser1', mapOnDimension: 1} > > > } > > > } > > > );// Case DIVISION_ASSEMBLY_B > > > chart.setOption( > > > newOption, > > > { > > > replace: 'series', > > > transition: { > > > // Both use dimension 0 (date string) as the mapping key. > > > from: {seriesId: 'ser0', mapOnDimension: 0}, > > > to: [ > > > {seriesId: 'ser1', mapOnDimension: 0}, > > > {seriesId: 'ser2', mapOnDimension: 0} > > > ] > > > } > > > } > > > );// Case DIVISION_ASSEMBLY_C// Theoretically DIVISION_ASSEMBLY_C is > > > the same as DIVISION_ASSEMBLY_A// We can add three extra dimensions to > > > dataset with all the values being series names:var optionA = { > > > dataset: [{ > > > id: 'dsA', > > > source: [ > > > // X SeriesName0 Y_ser0 SeriesName1 Y_ser1 > > > SeriesName2 Y_ser2 > > > ['2020-03-01', 'Breakfast', 32, 'Lunch', 44, > > > 'Supper', 17 ], > > > ['2020-03-02', 'Breakfast', 42, 'Lunch', 14, > > > 'Supper', 47 ], > > > ['2020-03-03', 'Breakfast', 62, 'Lunch', 54, > > > 'Supper', 87 ], > > > ['2020-03-04', 'Breakfast', 12, 'Lunch', 14, > > > 'Supper', 17 ], > > > ['2020-03-05', 'Breakfast', 52, 'Lunch', 24, > > > 'Supper', 57 ], > > > ] > > > }, { > > > id: 'dsB', > > > source: [ > > > ['Breakfast', 200], // sum of "dsA" dimension 2. > > > ['Lunch', 150], // sum of "dsA" dimension 4. > > > ['Supper', 225] // sum of "dsA" dimension 6. > > > ] > > > }], > > > ... > > > };// Add specify transition rules: > > > chart.setOption( > > > optionB, > > > { > > > replace: ['series', 'xAxis', 'yAxis', 'grid'], > > > transition: { > > > from: [ > > > {seriesId: 'ser0', mapOnDimension: 1}, > > > {seriesId: 'ser1', mapOnDimension: 3}, > > > {seriesId: 'ser2', mapOnDimension: 5} > > > ], > > > // If `mapOnDimension` provided, use the values of that > > > // dimension as the key to make transition mapping. > > > // In this case, the value of dimension 0 in "ser3" > > > // are "Breakfast", "Lunch", "Supper". They are mapped > > > // to the series name of "ser0"/"ser1"/"ser2". > > > to: {seriesId: 'ser3', mapOnDimension: 0} > > > } > > > } > > > ); > > > > > > Implementation of "Internal-Series-Transition" > > > > > > Transition inside a single series has been implemented via data.diff > and > > > graphic.initProps/graphic.updateProps. But consider the cases > > > TRANSFORMATION_A and TRANSFORMATION_B, the original updateProps should > > > better be enhanced to support morph. > > > Implementation of "Cross-Series-Transition" > > > > > > The implementation of "Cross-Series-Transition", which is probably not > > only > > > necessary for "Division"/"Assembly" but also useful in other transform > > > requirements above, might be more complicated. I am not sure about the > > > detail of this implementation. > > > > > > Before we make further discussion, we assume that these design is > > > appropriate: "Cross-Series-Transition" can only happen on "create new > > > series". The "transition target" is a new series, and the "transition > > > source" is an existing series, or an series having just been deleted > (the > > > latter one might be more common). > > > > > > First of all, consider "delete series", we need to introduce an unified > > > mechanism to temporarily store the "previous series model" (just > deleted) > > > or, more specifically, the "previous data with graphic elements". > > Currently > > > these "previous data" are stored on each series view, which is not able > > to > > > "cross series". > > > > > > Secondly, we may need to introduce one extra stage before the render > > stage, > > > say prepareCrossTransition. > > > > > > The stage prepareCrossTransition travels and recognizes the "transition > > > rules" given in the params of setOption calling, finds a proper rule > for > > > each series, and detects mappings for each data item based on the key > get > > > from mapOnDimension, similar as what DataDiffer.ts did. After these > > > mappings established, we can known how many pieces a single graphic > > element > > > will be "divided to" or "assembled from". Then for each old graphic > > element > > > related to any mapping: (A) If it will be "divided", we use a algorithm > > > (say, splitAlgorithm) to calculate the polygons it can be split, and > get > > > sourcePolygons. (B) If it will not be "divided", we trade itself as the > > > sourcePolygons. The result of the mappings and sourcePolygons are > finally > > > stored in a data structure, say crossTransitionManager. > > > > > > In the chartView.render, it receives new parameters indicating whether > > this > > > series needs to "Crose-Series-Transition". If not, do things as > usually. > > If > > > so, for each element to be created, instead of calling > graphic.initProps, > > > we initialize a morph transition animation for the creating procedure. > > > Firstly we retrieve the sourcePolygons from crossTransitionManager. And > > > then, (A) If the element should be "assembled", we use splitAlgorithm > to > > > generate targetPolygons for each sourcePolygon, and start morph > > transition > > > animations for each sourcePolygon-targetPolygon tuple. (B) If the > element > > > should not be "assembled", we trade the element itself as the > > > targetPolygon, > > > and start a morph transition animation for the > > sourcePolygon-targetPolygon > > > tuple. > > > > > > > > > > > > ------------------------------ > > > Transition on custom series > > > > > > Having the transition framework established, the transition description > > for > > > "custom series" is similar. And we can customize more in graphic > element > > > definitions, since "custom series" has exposed the graphic element > > > definitions to users. > > > > > > custom animation should be at least applied on > > > > > > - properties in "shape": e.g., shape.x, shape.height, shape.points, > > ... > > > - "transform" propeties: x, y, scaleX, scaleY, originX, originY, > > > rotation > > > . > > > - numeric "style" properties: style.x, style.y, style.opacity. > > > - text, which we will discussed in the next section. > > > > > > Entering transition and leaving transition of custom series > > > > > > For example, if we want to implement the "enter animation" as: "the > > radius > > > increases from 0 and the opacity increase from 0", the proposed option > > can > > > be as follows: > > > > > > renderItem: function () { > > > return { > > > type: 'group', > > > children: [{ > > > type: 'circle', > > > shape: { > > > cx: 100, > > > cy: 100, > > > // Means that the final radius is 50 > > > r: 50 > > > }, > > > style: { > > > // Means that the final opacity is 1 > > > opacity: 1, > > > fill: 'red' > > > }, > > > // Set all "enter transition related props" here. > > > // Notice that only needed props needs to set here. > > > // Then the enter animation will be performed from > > > // these props. > > > // The arguments of the enter animation will follow the > > > // settings like `animationEasing`, `animationDuration` > > > // on seriesModel, as other series did. > > > enterTransition: { > > > // The initial radius is 0 > > > shape: { > > > r: 0 > > > }, > > > // The initial opacify is 0 > > > style: { > > > opacity: 0 > > > } > > > }, > > > // Set all of the "out props" here. It works when the > element > > > // will be disappeared. > > > // In most case, simply set opacity: 0 as follows is > enough. > > > leaveTransition: { > > > style: { > > > opacity: 0 > > > } > > > } > > > }] > > > }; > > > } > > > > > > [[PENDING]] Do we need to support setting style.opcity on a group, > which > > > can be adopted on every descendants in this group. This probably > enhance > > > the animation performance for custom series. > > > Updating transition of custom series > > > > > > As we already have (what scatter series did), data item mappings can be > > > made automatically by dataItem.name or index. But we should better > > > automatically make transition animation when updating, because echarts > > does > > > not known which properties changed. This work should better be left to > > > users. > > > > > > chart.setOtion({ > > > dataset: { > > > source: [ > > > ['2020-02-02', 12, 553], > > > ['2020-02-03', 55, 172], > > > ['2020-02-04', 16, 812], > > > ['2020-02-05', 94, 756], > > > ] > > > }, > > > series: { > > > type: 'custom', > > > renderItem: function (params, api) { > > > var pos = api.coord([api.value(0), api.value(1)]); > > > return { > > > type: 'group', > > > children: [{ > > > type: 'rect', > > > shape: {x: -20, y: -20, width: 40, height: 40}, > > > // Instead of setting `x`/`y` on the root level of > > > this `rect`, > > > // we set them in `updateTransition`, which means > if > > > update > > > // transition occurs, auto make transition > > > animation (additive > > > // animation) targeting to these `x`/`y`, otherwise > > > set this > > > // `x`/`y` directly. The behavior is like what > > > `graphic.updateProps` > > > // did. > > > updateTransition: { > > > x: pos[0], > > > y: pos[1], > > > } > > > }] > > > }; > > > }, > > > // Mapping can be established by itemName. > > > // If itemName not specified, mapping can be established by > > index, > > > // which makes sense when dataZoom exists. > > > encode: {x: 1, y: 2, itemName: 0} > > > } > > > });// Now update > > > chart.setOption({ > > > dataset: { > > > source: [ > > > // ['2020-02-02', 12, 553], // remove one > > > ['2020-02-03', 55, 172], > > > ['2020-02-04', 16, 812], > > > ['2020-02-05', 94, 756], > > > ['2020-02-06', 71, 318], // add one > > > ] > > > } > > > }); > > > > > > Cross-Series-Transition of custom series > > > > > > This kind of API will be discuss later, after the final morph API > > > (division/assembly) decided. > > > > > > Thanks, > > > ------------------------------ > > > Su Shuang (100pah) > > > ------------------------------ > > > > > > > > > -- > > Yi Shen > > Apache ECharts(incubating) PPMC > > > -- Yi Shen Apache ECharts(incubating) PPMC