VChart icon indicating copy to clipboard operation
VChart copied to clipboard

[Bug] normal动画重复执行

Open skie1997 opened this issue 4 months ago • 1 comments

Version

2.0.1

Link to Minimal Reproduction

vscreen

Steps to Reproduce

本地case

  const spec = {
    type: 'bar',
    theme: {},
    xField: ['HqlYadRdHJ5c', '30001'],
    yField: ['10002'],
    direction: 'vertical',
    sortDataByAxis: true,
    seriesField: '30001',
    padding: {
      left: 6,
      right: 6,
      top: 6,
      bottom: 6
    },
    labelLayout: 'region',
    data: [
      {
        id: 'data',
        values: [
          {
            '10001': 'Sales',
            '10002': 30,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2023',
            HqlYadRdHJ5c: 'Labels',
            Tn97A7q0XVDq: 30,
            '7ti8XuX4kcY1': '2023'
          },
          {
            '10001': 'Sales',
            '10002': 24,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2022',
            HqlYadRdHJ5c: 'Labels',
            Tn97A7q0XVDq: 24,
            '7ti8XuX4kcY1': '2022'
          },
          {
            '10001': 'Sales',
            '10002': 20,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2023',
            HqlYadRdHJ5c: 'Tables',
            Tn97A7q0XVDq: 20,
            '7ti8XuX4kcY1': '2023'
          },
          {
            '10001': 'Sales',
            '10002': 10,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2022',
            HqlYadRdHJ5c: 'Tables',
            Tn97A7q0XVDq: 10,
            '7ti8XuX4kcY1': '2022'
          },
          {
            '10001': 'Sales',
            '10002': 40,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2023',
            HqlYadRdHJ5c: 'Storage',
            Tn97A7q0XVDq: 40,
            '7ti8XuX4kcY1': '2023'
          },
          {
            '10001': 'Sales',
            '10002': 20,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2022',
            HqlYadRdHJ5c: 'Storage',
            Tn97A7q0XVDq: 20,
            '7ti8XuX4kcY1': '2022'
          },
          {
            '10001': 'Sales',
            '10002': 10,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2023',
            HqlYadRdHJ5c: 'Furn',
            Tn97A7q0XVDq: 10,
            '7ti8XuX4kcY1': '2023'
          },
          {
            '10001': 'Sales',
            '10002': 10,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2022',
            HqlYadRdHJ5c: 'Furn',
            Tn97A7q0XVDq: 10,
            '7ti8XuX4kcY1': '2022'
          },
          {
            '10001': 'Sales',
            '10002': 50,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2023',
            HqlYadRdHJ5c: 'Art',
            Tn97A7q0XVDq: 50,
            '7ti8XuX4kcY1': '2023'
          },
          {
            '10001': 'Sales',
            '10002': 30,
            '10003': 'Tn97A7q0XVDq',
            '30001': '2022',
            HqlYadRdHJ5c: 'Art',
            Tn97A7q0XVDq: 30,
            '7ti8XuX4kcY1': '2022'
          }
        ],
        fields: {
          '10001': {
            alias: '指标名称'
          },
          '10002': {
            alias: '指标值'
          },
          '30001': {
            alias: '图例项',
            domain: null,
            sortIndex: null,
            lockStatisticsByDomain: true
          },
          HqlYadRdHJ5c: {
            alias: 'Product',
            domain: ['Art', 'Furn', 'Labels', 'Storage', 'Tables'],
            sortIndex: 0,
            lockStatisticsByDomain: true
          },
          '7ti8XuX4kcY1': {
            alias: 'Year'
          },
          Tn97A7q0XVDq: {
            alias: 'Sales'
          }
        }
      }
    ],
    stackInverse: true,
    axes: [
      {
        type: 'band',
        tick: {
          style: {
            strokeOpacity: 0.2
          },
          visible: false
        },
        grid: {
          visible: false,
          style: {
            zIndex: 150,
            stroke: '#FFFFFF',
            lineWidth: 1,
            lineDash: []
          }
        },
        orient: 'bottom',
        visible: true,
        domainLine: {
          visible: false,
          style: {
            lineWidth: 1,
            stroke: '#d5d7e2'
          }
        },
        title: {
          visible: false,
          space: 5,
          text: '',
          style: {
            fontSize: 12,
            fill: 'rgba(255,255,255,0.5)',
            fontFamily: 'D-DIN',
            fontWeight: 'normal'
          }
        },
        maxHeight: null,
        autoIndent: false,
        sampling: false,
        zIndex: 200,
        label: {
          visible: true,
          space: 4,
          style: {
            fontSize: 12,
            fill: 'rgba(255,255,255,0.7)',
            angle: 0,
            fontFamily: 'D-DIN',
            fontWeight: 'normal',
            direction: 'horizontal',
            maxLineWidth: 174
          },
          autoHide: true,
          autoHideMethod: 'greedy',
          flush: true,
          lastVisible: true,
          autoHideSeparation: 4
        },
        hover: true,
        background: {
          visible: true,
          state: {
            hover: {
              fillOpacity: 0.08,
              fill: '#141414'
            },
            hover_reverse: {
              fillOpacity: 0.08,
              fill: '#141414'
            }
          }
        },
        paddingInner: 0.18,
        paddingOuter: 0.18,
        ticks: true
      },
      {
        type: 'linear',
        tick: {
          size: 4,
          visible: true,
          tickMode: 'd3'
        },
        niceType: 'rough',
        zIndex: 200,
        grid: {
          visible: true
        },
        orient: 'left',
        visible: true,
        domainLine: {
          visible: false,
          style: {
            lineWidth: 1,
            stroke: '#d5d7e2'
          }
        },
        title: {
          visible: false,
          text: '',
          space: 8,
          style: {
            fontSize: 12,
            fill: 'rgba(255,255,255,0.5)',
            fontFamily: 'D-DIN',
            fontWeight: 'normal'
          }
        },
        autoIndent: false,
        sampling: false,
        label: {
          visible: true,
          space: 6,
          flush: true,
          padding: 0,
          style: {
            fontSize: 12,
            maxLineWidth: 174,
            fill: 'rgba(255,255,255,0.7)',
            angle: 0,
            fontFamily: 'D-DIN',
            fontWeight: 'normal',
            dy: 0,
            direction: 'horizontal'
          },
          autoHide: true,
          autoHideMethod: 'greedy',
          autoHideSeparation: 4,
          rotateAngle: [null],
          labelOverlap: 'custom',
          tighten: false
        },
        background: {
          visible: true,
          state: {
            hover: {
              fillOpacity: 0.08,
              fill: '#141414'
            },
            hover_reverse: {
              fillOpacity: 0.08,
              fill: '#141414'
            }
          }
        },
        innerOffset: {
          top: 0
        },
        zero: true,
        nice: true,
        paddingInner: 0.18,
        paddingOuter: 0.18,
        maxWidth: 180,
        ticks: true
      },
      {
        type: 'linear',
        tick: {
          size: 4,
          visible: true,
          tickMode: 'd3'
        },
        niceType: 'rough',
        zIndex: 200,
        grid: {
          visible: false
        },
        orient: 'right',
        visible: true,
        domainLine: {
          visible: false
        },
        title: {
          visible: false
        },
        autoIndent: false,
        sampling: false,
        label: {
          visible: true,
          space: 6,
          flush: true,
          padding: 0,
          style: {
            visible: false,
            fontSize: 12,
            maxLineWidth: 174,
            fill: 'rgba(255,255,255,0.7)',
            angle: 0,
            fontFamily: 'D-DIN',
            fontWeight: 'normal',
            dy: 0,
            direction: 'horizontal'
          },
          autoHide: true,
          autoHideMethod: 'greedy',
          autoHideSeparation: 4,
          rotateAngle: [null],
          labelOverlap: 'custom',
          tighten: false
        },
        background: {
          visible: true,
          state: {
            hover: {
              fillOpacity: 0.08,
              fill: '#141414'
            },
            hover_reverse: {
              fillOpacity: 0.08,
              fill: '#141414'
            }
          }
        },
        innerOffset: {
          top: 0
        },
        zero: true,
        nice: true,
        paddingInner: 0.18,
        paddingOuter: 0.18,
        maxWidth: 180,
        ticks: true
      }
    ],
    color: {
      field: '30001',
      type: 'ordinal',
      range: ['rgb(0,110,255)', 'rgb(0,229,229)'],
      specified: {},
      domain: ['2023', '2022']
    },
    colorGradient: {
      type: 'linear',
      x0: {
        field: '30001',
        type: 'ordinal',
        range: [0, 0]
      },
      y0: {
        field: '30001',
        type: 'ordinal',
        range: [1, 1]
      },
      x1: {
        field: '30001',
        type: 'ordinal',
        range: [0.00001, 0.00001]
      },
      y1: {
        field: '30001',
        type: 'ordinal',
        range: [0, 0]
      },
      stops: [
        {
          offset: 0,
          color: {
            field: '30001',
            type: 'ordinal',
            range: ['rgba(0,110,255,0.2)', 'rgba(0,229,229,0.2)']
          }
        },
        {
          offset: 1,
          color: {
            field: '30001',
            type: 'ordinal',
            range: ['rgb(0,110,255)', 'rgb(0,229,229)']
          }
        }
      ]
    },
    legends: [
      {
        type: 'discrete',
        visible: true,
        id: 'legend-discrete',
        orient: 'top',
        position: 'end',
        layoutType: 'normal',
        maxRow: 1,
        title: {
          textStyle: {
            fontSize: 12,
            fill: 'rgba(255,255,255,0.7)'
          }
        },
        layoutLevel: 70,
        item: {
          focus: false,
          focusIconStyle: {
            size: 14
          },
          maxWidth: 400,
          spaceRow: 0,
          spaceCol: 0,
          padding: {
            left: 10,
            right: 0,
            top: 0,
            bottom: 5
          },
          background: {
            visible: false,
            style: {
              fillOpacity: 0.001
            }
          },
          label: {
            style: {
              fontSize: 12,
              fill: 'rgba(255,255,255,0.7)',
              fontFamily: 'D-DIN',
              fontWeight: 'normal'
            },
            state: {
              unSelected: {
                fillOpacity: 0.2
              }
            }
          },
          shape: {
            style: {
              lineWidth: 0,
              symbolType: 'square',
              size: 12,
              fillOpacity: 1,
              width: 12,
              height: 7.416
            }
          }
        },
        pager: {
          layout: 'horizontal',
          padding: 0,
          textStyle: {
            fill: 'rgba(255,255,255,0.7)'
          },
          space: 0,
          handler: {
            preShape: 'triangleLeft',
            nextShape: 'triangleRight',
            style: {
              fill: 'rgba(255,255,255,0.7)',
              cursor: 'pointer',
              fillOpaity: 1
            },
            state: {
              disable: {
                fill: 'rgba(255,255,255,0.7)',
                cursor: 'not-allowed',
                fillOpacity: 0.45
              }
            }
          }
        },
        alignSelf: 'end',
        padding: {
          left: 10,
          right: 0,
          top: 0,
          bottom: 12
        }
      }
    ],
    label: {
      visible: false,
      offset: 3,
      overlap: {
        hideOnHit: true,
        avoidBaseMark: false,
        strategy: [
          {
            type: 'position',
            position: []
          }
        ],
        clampForce: true
      },
      style: {
        fontSize: 10,
        fontFamily: 'D-DIN',
        fontWeight: 'normal',
        zIndex: 400,
        lineHeight: '100%',
        boundsPadding: [1, 0, 0, 0],
        fill: 'rgba(255,255,255,1)',
        strokeOpacity: 0
      },
      position: 'outside',
      smartInvert: false,
      fontWeight: 'normal',
      animation: false,
      avoidBaseMark: true
    },
    tooltip: {
      visible: true,
      renderMode: 'canvas',
      mark: {
        visible: true
      },
      style: {
        panel: {
          padding: {
            top: 5,
            bottom: 10,
            left: 10,
            right: 10
          },
          backgroundColor: 'rgba(8, 28, 48, 0.95)',
          border: {
            color: '#CFCFCF',
            width: 0,
            radius: 2
          },
          shadow: {
            x: 0,
            y: 4,
            blur: 12,
            spread: 0,
            color: 'rgba(0, 0, 0, 0.2)'
          }
        },
        titleLabel: {
          fontSize: 14,
          fontColor: '#FFF',
          fontWeight: 'bold',
          fontFamily: 'D-DIN',
          align: 'left',
          lineHeight: 18
        },
        keyLabel: {
          fontSize: 12,
          fontColor: 'rgba(255,255,255,0.65)',
          fontWeight: 'normal',
          fontFamily: 'SourceHanSansCN-Normal',
          align: 'left',
          lineHeight: 18
        },
        valueLabel: {
          fontSize: 12,
          fontColor: '#FFF',
          fontWeight: 'normal',
          fontFamily: 'D-DIN',
          align: 'right',
          lineHeight: 18
        },
        shape: {
          size: 10,
          spacing: 10,
          shapeLineWidth: 0
        },
        spaceRow: 10
      },
      dimension: {
        visible: true
      }
    },
    hover: {
      enable: true
    },
    select: {
      enable: true
    },
    bar: {
      state: {
        hover: {
          cursor: 'pointer',
          fillOpacity: 0.8,
          stroke: '#58595B',
          lineWidth: 1,
          zIndex: 500
        },
        selected: {
          cursor: 'pointer',
          fillOpacity: 1,
          stroke: '#58595B',
          lineWidth: 1
        },
        selected_reverse: {
          fillOpacity: 0.3,
          lineWidth: 0.3
        }
      },
      style: {
        cornerRadius: 0,
        fill: {
          gradient: 'linear',
          stops: [
            {
              offset: 0
            },
            {
              offset: 1
            }
          ]
        },
        lineWidth: 2,
        stroke: {
          gradient: 'linear',
          stops: [
            {
              offset: 0
            },
            {
              offset: 1
            }
          ]
        }
      }
    },
    region: [
      {
        clip: true
      }
    ],
    background: 'rgba(0, 0, 0, 1)',
    markLine: [],
    animation: true,
    crosshair: {
      xField: {
        visible: true,
        line: {
          type: 'rect',
          style: {
            fillOpacity: 1,
            fill: 'rgba(80,156,255,0.1)'
          }
        }
      },
      gridZIndex: 100,
      yField: {
        line: {
          style: {
            fillOpacity: 1,
            fill: 'rgba(80,156,255,0.1)'
          }
        },
        visible: false
      }
    },
    morph: {
      enable: false
    },
    fillOpacity: {
      fillOpacity: 1
    },
    axesPadding: true,
    plotLayout: {
      clip: false
    },
    scales: [
      {
        id: 'gradientFillStop0_data',
        type: 'ordinal',
        range: [
          'rgba(0,110,255,0.2)',
          'rgba(0,229,229,0.2)',
          'rgba(46,85,234,0.2)',
          'rgba(184,231,254,0.2)',
          'rgba(0,214,137,0.2)',
          'rgba(183,249,245,0.2)',
          'rgba(251,204,113,0.2)',
          'rgba(244,110,80,0.2)'
        ],
        domain: ['2023', '2022']
      },
      {
        id: 'gradientFillStop1_data',
        type: 'ordinal',
        range: [
          'rgb(0,110,255)',
          'rgb(0,229,229)',
          'rgb(46,85,234)',
          'rgb(184,231,254)',
          'rgb(0,214,137)',
          'rgb(183,249,245)',
          'rgb(251,204,113)',
          'rgb(244,110,80)'
        ],
        domain: ['2023', '2022']
      },
      {
        id: 'gradientStrokeStop0_data',
        type: 'ordinal',
        range: [
          'rgba(51, 139, 255, 0.2)',
          'rgba(25, 255, 255, 0.2)',
          'rgba(92, 123, 239, 0.2)',
          'rgba(234, 248, 255, 0.2)',
          'rgba(10, 255, 167, 0.2)',
          'rgba(230, 253, 252, 0.2)',
          'rgba(252, 222, 163, 0.2)',
          'rgba(247, 150, 128, 0.2)'
        ],
        domain: ['2023', '2022']
      },
      {
        id: 'gradientStrokeStop1_data',
        type: 'ordinal',
        range: [
          'rgba(51, 139, 255, 1)',
          'rgba(25, 255, 255, 1)',
          'rgba(92, 123, 239, 1)',
          'rgba(234, 248, 255, 1)',
          'rgba(10, 255, 167, 1)',
          'rgba(230, 253, 252, 1)',
          'rgba(252, 222, 163, 1)',
          'rgba(247, 150, 128, 1)'
        ],
        domain: ['2023', '2022']
      },
      {
        id: 'gradientX1_data',
        type: 'ordinal',
        range: [
          6.123233995736766e-17, 6.123233995736766e-17, 6.123233995736766e-17, 6.123233995736766e-17,
          6.123233995736766e-17, 6.123233995736766e-17, 6.123233995736766e-17, 6.123233995736766e-17
        ],
        domain: ['2023', '2022']
      },
      {
        id: 'gradientY1_data',
        type: 'ordinal',
        range: [1, 1, 1, 1, 1, 1, 1, 1],
        domain: ['2023', '2022']
      }
    ],
    barWidth: '60%',
    barBackground: {
      fieldLevel: 0,
      visible: false,
      interactive: false,
      style: {
        cornerRadius: 0,
        fill: 'rgba(255,255,255,1)',
        fillOpacity: 0.1
      }
    },
    animationAppear: {
      bar: {
        type: 'growHeightIn',
        oneByOne: false,
        controlOptions: {
          immediatelyApply: true
        },
        easing: 'circInOut',
        duration: 1000
      }
    },
    animationNormal: {
      bar: [
        {
          type: 'growHeightIn',
          oneByOne: false,
          controlOptions: {
            immediatelyApply: false
          },
          options: (...args) => {
            return {
              orient: 'negative'
              // overall: chartInstance.getChart().getComponentsByType('cartesianAxis-linear')[0]._scale.range()[0], // 解决堆叠图分开grow的问题
            };
          },
          startTime: 5000,
          easing: 'circInOut',
          duration: 1000,
          delayAfter: 6000,
          loop: true
        },
        {
          loop: true,
          startTime: 5000,
          delay: 1000,
          delayAfter: 5000,
          duration: 1000,
          easing: 'circInOut',
          custom: StreamLight,
          customParameters: {
            isHorizontal: false,
            attribute: {
              fill: {
                gradient: 'linear',
                x0: 0,
                x1: 1,
                y1: 0,
                y0: 0,
                stops: [
                  {
                    offset: 0,
                    color: 'rgba(255,255,255,0)'
                  },
                  {
                    offset: 1,
                    color: 'rgba(255,255,255,0.3)'
                  }
                ]
              },
              blur: 0,
              shadowColor: 'rgba(0,0,0,0)'
            }
          }
        }
      ]
    },
    animationEnter: {
      bar: {
        easing: 'circInOut',
        duration: 1000
      }
    },
    animationUpdate: {
      bar: {
        easing: 'circInOut',
        duration: 1000
      }
    },
    animationExit: {},
    hash: 'e46ddf53dc5b4c292b8647ec7d667b0a',
    line: {
      style: {
        stroke: {
          gradient: 'linear',
          stops: [
            {
              offset: 0
            },
            {
              offset: 1
            }
          ]
        }
      }
    },
    width: 400,
    height: 225
  };

  const emptySpec = {
    type: 'pie',
    data: [{}],
    background: 'rgba(255,255,255,0)',
    theme: {}
  };

  const cs = new VChart(emptySpec, {
    dataSet,
    dom: document.getElementById('chart'),
    mode: isMobile ? 'mobile-browser' : 'desktop-browser'
  });
  console.time('renderTime');
  cs.renderAsync().then(() => {
    console.timeEnd('renderTime');
  });

  // 字体更新
  setTimeout(() => {
    cs.updateSpec(spec);
    cs.reRunNormalAnimation();
  }, 100);

  // 第一次绘制
  setTimeout(() => {
    cs.updateSpec(spec);
    cs.reRunNormalAnimation();
  }, 200);

Current Behavior

Image

调试后发现是reRunNormalAnimation引起的

Expected Behavior

不会重复跑normal动画

Environment

- OS:
- Browser:
- Framework:

Any additional comments?

No response

skie1997 avatar Aug 14 '25 07:08 skie1997

fixed in https://github.com/VisActor/VRender/pull/1917

neuqzxy avatar Aug 15 '25 06:08 neuqzxy