VChart icon indicating copy to clipboard operation
VChart copied to clipboard

[Bug] react-vchart tooltip 如果出现在图表外,在接入方动态更改 size 后意外隐藏

Open zamhown opened this issue 1 year ago • 0 comments

vchart 的 tooltip 一共有两种自动消失的逻辑:

  • 鼠标从 canvas 移出,自动隐藏当前图表 tooltip;
  • 在 enterable 情况下,鼠标可以挪到 tooltip 上,如果 tooltip 一半在图表内一半在外,那么鼠标从 tooltip 直接移到图表外也会自动隐藏 tooltip。

这个问题是,如果:

  • tooltip 一半在图表内一半在外
  • 鼠标刚好在图表外的那半截 tooltip 上交互(如点击)
  • 在对 tooltip 进行交互时 tooltip 大小有抖动

可能会导致导致鼠标在一瞬间出现在了 tooltip 外,会误触发第二个规则。

具体代码在 packages/vchart/src/plugin/components/tooltip-handler/dom/dom-tooltip-handler.ts问题根因是 dom 尺寸改变时,浏览器会意外触发 pointerleave 事件

img_v3_02d3_f8bc848d-7fda-4a2a-b717-0b05b5c3d85g

复现 demo:

const spec = {
  type: 'line',
  data: {
    values: [
      { type: '指甲油 Nail polish', country: 'Africa', value: 4229 },
      { type: '指甲油 Nail polish', country: 'EU', value: 4376 },
      { type: '指甲油 Nail polish', country: 'China', value: 3054 },
      { type: '指甲油 Nail polish', country: 'USA', value: 12814 },
      { type: '眉笔 Eyebrow pencil', country: 'Africa', value: 3932 },
      { type: '眉笔 Eyebrow pencil', country: 'EU', value: 3987 },
      { type: '眉笔 Eyebrow pencil', country: 'China', value: 5067 },
      { type: '眉笔 Eyebrow pencil', country: 'USA', value: 13012 },
      { type: '胭脂 Rouge', country: 'Africa', value: 5221 },
      { type: '胭脂 Rouge', country: 'EU', value: 3574 },
      { type: '胭脂 Rouge', country: 'China', value: 7004 },
      { type: '胭脂 Rouge', country: 'USA', value: 11624 },
      { type: '口红 Lipstick', country: 'Africa', value: 9256 },
      { type: '口红 Lipstick', country: 'EU', value: 4376 },
      { type: '口红 Lipstick', country: 'China', value: 9054 },
      { type: '口红 Lipstick', country: 'USA', value: 8814 },
      { type: '眼影 Eyeshadows', country: 'Africa', value: 3308 },
      { type: '眼影 Eyeshadows', country: 'EU', value: 4572 },
      { type: '眼影 Eyeshadows', country: 'China', value: 12043 },
      { type: '眼影 Eyeshadows', country: 'USA', value: 12998 },
      { type: '眼线笔 Eyeliner', country: 'Africa', value: 5432 },
      { type: '眼线笔 Eyeliner', country: 'EU', value: 3417 },
      { type: '眼线笔 Eyeliner', country: 'China', value: 15067 },
      { type: '眼线笔 Eyeliner', country: 'USA', value: 12321 },
      { type: '粉底 Foundation', country: 'Africa', value: 13701 },
      { type: '粉底 Foundation', country: 'EU', value: 5231 },
      { type: '粉底 Foundation', country: 'China', value: 10119 },
      { type: '粉底 Foundation', country: 'USA', value: 10342 },
      { type: '唇彩 Lip gloss', country: 'Africa', value: 4008 },
      { type: '唇彩 Lip gloss', country: 'EU', value: 4572 },
      { type: '唇彩 Lip gloss', country: 'China', value: 12043 },
      { type: '唇彩 Lip gloss', country: 'USA', value: 22998 },
      { type: '睫毛膏 Mascara', country: 'Africa', value: 18712 },
      { type: '睫毛膏 Mascara', country: 'EU', value: 6134 },
      { type: '睫毛膏 Mascara', country: 'China', value: 10419 },
      { type: '睫毛膏 Mascara', country: 'USA', value: 11261 }
    ]
  },
  xField: 'type',
  yField: 'value',
  seriesField: 'country',
  legends: { visible: true },
  axes: [
    {
      orient: 'bottom',
      type: 'band',
      sampling: false,
      label: {
        style: {
          direction: 'vertical'
        }
      }
    },
    { orient: 'left', type: 'linear' }
  ],
  tooltip: {
    enterable: true,
    updateElement: (el, actualTooltip, params) => {
      if (actualTooltip.activeType === 'dimension') {
        const { changePositionOnly, dimensionInfo } = params;
        if (changePositionOnly) {
          return;
        }
        el.style.width = 'auto';
        el.style.height = 'auto';
        el.style.minHeight = 'auto';
        el.getElementsByClassName('value-box')[0].style.flex = '1';
        for (const valueLabel of el.getElementsByClassName('value')) {
          valueLabel.style.maxWidth = 'none';
        }
        if (el.lastElementChild?.id === 'button-container') {
          el.lastElementChild.remove();
        }
        const div = document.createElement('div');
        div.id = 'button-container';
        div.style.margin = '10px -10px 0px';
        div.style.padding = '10px 0px 0px';
        div.style.borderTop = '1px solid #cccccc';
        div.style.textAlign = 'center';
        div.innerHTML = `<a
          href="https://www.google.com/search?q=${dimensionInfo[0]?.value}"
          style="text-decoration: none"
          target="_blank"
        >Search with <b>Google</b></a>`;
        el.appendChild(div);
        el.addEventListener('click', e => {
          el.style.overflow = 'hidden';
          el.style.height = '0px';
          el.style.height = '10px';
        });
      } else {
        if (el.lastElementChild?.id === 'button-container') {
          el.lastElementChild.remove();
        }
      }
    }
  }
};
const vchart = new VChart(spec, { dom: CONTAINER_ID });
vchart.renderSync();
// Just for the convenience of console debugging, DO NOT COPY!
window['vchart'] = vchart;

zamhown avatar Jul 25 '24 05:07 zamhown