VChart icon indicating copy to clipboard operation
VChart copied to clipboard

[Bug] render result is inconsistent before and after datazoom moving

Open purpose233 opened this issue 6 months ago • 1 comments

Version

1.12.14

Link to Minimal Reproduction

null

Steps to Reproduce

function formatNumberToTenThousand(num, decimalPlaces = 2, unitStyle) {
  if (typeof num !== 'number') {
    return '-'; // 非数字类型返回 -
  }
  if (num < 0) {
    return `-${formatNumberToTenThousand(Math.abs(num), decimalPlaces, unitStyle)}`; // 处理负数
  }

  // 新增大单位判断逻辑
  const units = [
    { threshold: 100000000, divisor: 100000000, unit: '亿' }, // 1亿
    // { threshold: 10000000, divisor: 10000000, unit: '千万' }, // 1千万
    // { threshold: 1000000, divisor: 1000000, unit: '百万' }, // 1百万
    { threshold: 10000, divisor: 10000, unit: '万' }, // 1万
  ];

  // 查找匹配的单位
  const matchedUnit = units.find((unit) => num >= unit.threshold);

  if (matchedUnit) {
    const value = num / matchedUnit.divisor;
    const fixedValue = value.toFixed(decimalPlaces);

    // // 万单位格式化(增加千分位)
    // const valuePart = (num / 10000).toFixed(decimalPlaces).replace(/\B(?=(\d{3})+(?!\d))/g, ',');

    // 添加千分位并格式化
    const valuePart = fixedValue.includes('.')
      ? fixedValue.replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
      : Number(fixedValue).toLocaleString();

    if (unitStyle) {
      return `${valuePart}<span style="${unitStyle}">${matchedUnit.unit}</span>`;
    }
    return `${valuePart}${matchedUnit.unit}`;
  }

  // 千分位格式化(小于1万)
  return num.toLocaleString('en-US', {
    minimumFractionDigits: 0,
    maximumFractionDigits: decimalPlaces,
  });
}

function formatNumberToPercentage(num) {
  if (num === null || num === undefined) {
    return '-';
  }
  if (typeof num !== 'number') {
    return '-';
  }
  const percentage = (num * 100).toFixed(2);
  return `${percentage}%`;
}

const spec = {
  "type": "bar", 
   title: {
    visible: true,
    text: "地域分布",
    textStyle: {
      fontSize: 12,
      lineHeight: 18,
    },
  },
  "data": [
    {
      "id": "horizontalBarData",
      "values": [
        {
          "name": "广东",
          "value": 2152188,
          "percent": 0.09553189059340318
        },
        {
          "name": "江苏",
          "value": 1619691,
          "percent": 0.07189527281404776
        },
        {
          "name": "河南",
          "value": 1561747,
          "percent": 0.06932323920520682
        },
        {
          "name": "山东",
          "value": 1503274,
          "percent": 0.06672772420434812
        },
        {
          "name": "四川",
          "value": 1455915,
          "percent": 0.06462554037718572
        },
        {
          "name": "浙江",
          "value": 1392015,
          "percent": 0.06178913026388779
        },
        {
          "name": "河北",
          "value": 1040775,
          "percent": 0.04619819617633274
        },
        {
          "name": "湖北",
          "value": 1038831,
          "percent": 0.046111905389787336
        },
        {
          "name": "湖南",
          "value": 930692,
          "percent": 0.04131180283514061
        },
        {
          "name": "陕西",
          "value": 896006,
          "percent": 0.03977215148631663
        },
        {
          "name": "安徽",
          "value": 887653,
          "percent": 0.03940137631141244
        },
        {
          "name": "福建",
          "value": 748169,
          "percent": 0.03320992360025048
        },
        {
          "name": "江西",
          "value": 725247,
          "percent": 0.03219245579716729
        },
        {
          "name": "山西",
          "value": 685430,
          "percent": 0.030425048262250485
        },
        {
          "name": "辽宁",
          "value": 568686,
          "percent": 0.02524298468999924
        },
        {
          "name": "重庆",
          "value": 552976,
          "percent": 0.024545645051815976
        },
        {
          "name": "广西",
          "value": 521468,
          "percent": 0.023147059608157265
        },
        {
          "name": "贵州",
          "value": 489247,
          "percent": 0.021716825331778974
        },
        {
          "name": "云南",
          "value": 472549,
          "percent": 0.020975630088088067
        },
        {
          "name": "上海",
          "value": 472234,
          "percent": 0.020961647784712656
        },
        {
          "name": "北京",
          "value": 407114,
          "percent": 0.018071083988500422
        },
        {
          "name": "新疆",
          "value": 381173,
          "percent": 0.016919608014336698
        },
        {
          "name": "黑龙江",
          "value": 373560,
          "percent": 0.016581680155298557
        },
        {
          "name": "甘肃",
          "value": 327979,
          "percent": 0.014558418662744045
        },
        {
          "name": "吉林",
          "value": 309830,
          "percent": 0.013752816046996874
        },
        {
          "name": "内蒙古",
          "value": 296377,
          "percent": 0.013155660722205056
        },
        {
          "name": "天津",
          "value": 266422,
          "percent": 0.011826010253600366
        },
        {
          "name": "海南",
          "value": 143117,
          "percent": 0.006352715276758389
        },
        {
          "name": "宁夏",
          "value": 111102,
          "percent": 0.00493162498290497
        },
        {
          "name": "未知",
          "value": 85262,
          "percent": 0.0037846322234743163
        },
        {
          "name": "青海",
          "value": 49995,
          "percent": 0.002219191292869021
        },
        {
          "name": "西藏",
          "value": 14963,
          "percent": 0.0006641816044644296
        },
        {
          "name": "香港",
          "value": 6571,
          "percent": 0.00029167528723757045
        },
        {
          "name": "中区社区",
          "value": 1804,
          "percent": 0.00008007642948966323
        }
      ]
    }
  ],
  direction: 'horizontal',
  xField: 'value',
  yField: 'name',
  seriesField: 'name',
  height: 300,
  padding: { right: 140, left: 0 },
  legends: {
    visible: true,
    orient: 'top',
    position: 'middle',
  },
  axes: [
    {
      orient: 'bottom',
      visible: false,
      nice: false,
    },
    {
      orient: 'left',
      maxWidth: 120,
      label: {
        autoLimit: true,
      },
      domainLine: {
        visible: false,
      },
      tick: {
        visible: false,
      },
    },
  ],
  stackCornerRadius: 0,
  bar: {
    style: {
      height: 16,
    },
  },
  barBackground: {
    visible: true,
    style: {
      height: 16,
    },
    state: {
      hover: {
        stroke: '#D9D9D9',
        lineWidth: 1,
      },
    },
  },
  crosshair: {
    yField: {
      visible: false,
    },
  },
  extensionMark: [
    {
      type: 'text',
      dataId: 'barData',
      visible: true,
      style: {
        text: (datum) => `${formatNumberToTenThousand(datum?.value)}人 ${formatNumberToPercentage(datum?.percent)}`,
        fontSize: 12,
        x: (_datum, ctx) => {
          return ctx.getRegion().getLayoutRect().width + 16;
        },
        y: (datum, ctx) => {
          return ctx.valueToY([datum.name]) + ctx.yBandwidth() / 2;
        },
        textBaseline: 'middle',
        textAlign: 'left',
        fill: '#595959',
        size: 20,
      },
    },
  ],
  tooltip: {
    mark: {
      content: {
        value: (datum) => `${formatNumberToTenThousand(datum?.value)}`,
      },
    },
    dimension: {
      content: {
        value: (datum) => `${formatNumberToTenThousand(datum?.value)}`,
      },
    },
  },
  dataZoom: {
    visible: true,
    orient: 'right',
    valueField: 'name',
    field: 'name',
    showDetail: false,
    start: 0,
    end: 8 / 34,
    offsetX: 130,
    offsetY: 0,
    padding: 0,
    roamScroll: true,
  },
};

const vchart = new VChart(spec, { dom: CONTAINER_ID });

vchart.renderSync();

Current Behavior

before: Image after: Image

Expected Behavior

consistant render result

Environment

- OS:
- Browser:
- Framework:

Any additional comments?

No response

purpose233 avatar Jun 18 '25 08:06 purpose233

问题定位:

  1. datazoom 拖拽过程中,禁用了 update 动画,没有禁用 enter 动画;
  2. 快速滑动 datazoom 时,图元有从 enter 迅速变为 update 的过程。 导致:enter 有动画,先执行,动画过程中 update,被 enter 结果覆盖

xiaoluoHe avatar Jun 23 '25 08:06 xiaoluoHe