【问题标题】:Poor performance on SVG mouseoverSVG 鼠标悬停时性能不佳
【发布时间】:2020-01-16 07:51:20
【问题描述】:

我的代码比我正在复制的示例滞后得多,我不知道为什么。

我正在尝试将原版 JS + d3 的页面重做为 React + VX。 VX 是一个包装 d3 的库,以使其适合反应编程模式。还有其他类似的,比如Recharts

当我添加一个在鼠标光标的 x 位置绘制一条垂直线的鼠标悬停回调时,我的问题就出现了。

我在关注this example,很流畅。您可以看到,当您将光标移到图表上时,线条流畅地跟随。

尽我所能,我做了同样的事情,但在我的图表上,光标后面的线非常滞后。

这不仅仅是画线。我在 DOM 中输出 X 位置作为健全性检查,并且似乎以相同的滞后间隔更新。

UI 更新是滞后的,尽管我可以看出鼠标移动时鼠标悬停回调被连续快速地调用,因为我可以看到 console.log 输出更快。

这是我的代码。我想知道:为什么我的会比我复制的示例慢这么多?

import React from 'react';
import rawData from 'lib/data.js';
import ChartData from 'lib/chart-data.js';
import { extent } from 'd3-array';
import { AxisLeft, AxisBottom } from '@vx/axis';
import { Group } from '@vx/group';
import { scaleTime, scaleLinear } from '@vx/scale';
import { Line, LinePath, Bar } from '@vx/shape';
import { localPoint } from '@vx/event';
import styles from './BitcoinPrice.scss';

const chartDimensions = (() => {
  const margin = { top: 20, right: 20, bottom: 35, left: 75 };
  const width = 800;
  const height = 400;
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  return { margin, width, height, innerWidth, innerHeight };
})();

class BitcoinPrice extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      lineX: 0,
      lineVisible: false,
    };

    this.chartData = new ChartData(rawData);
  }

  onMouseOver() {
    this.setState({ lineVisible: true });
  }

  onMouseOut() {
    this.setState({ lineVisible: false });
  }

  onMouseMove(e, data, xScale) {
    const { margin } = chartDimensions;
    const point = localPoint(e);
    const x = point.x - margin.left;

    console.log(`x`, x);

    this.setState({ lineX: x });
  }

  render() {
    const { data } = this.chartData;
    const { margin, width, height, innerWidth, innerHeight } = chartDimensions;

    const xScale = scaleTime({
      range: [0, innerWidth],
      domain: extent(data, (d) => d.date)
    });

    const yScale = scaleLinear({
      range: [innerHeight, 0],
      domain: extent(data, (d) => d.price)
    });

    const lineX = this.state.lineX;
    const lineVisibility = this.state.lineVisible ? 'visible' : 'hidden';

    return (
      <div>
        <h1>Example chart: Bitcion price over time</h1>
        <p>Mouse X position: {lineX}</p>

        <svg width={800} height={400} viewBox={`0 0 ${width} ${height}`}>
          <Group top={margin.top} left={margin.left}>
            {/* The bitcoin price line */}
            <LinePath
              data={data}
              x={(d) => xScale(d.date)}
              y={(d) => yScale(d.price)}
              className={`${styles['path-line']} ${styles['path-price']}`}
            />

            {/* The vertical line that follows the cursor when hovering */}
            <Line
              visibility={lineVisibility}
              x1={0}
              y1={0}
              x2={0}
              y2={innerHeight}
              transform={`translate(${lineX}, 0)`}
              className={styles['mouse-line']}
            />

            {/* Left axis */}
            <AxisLeft
              scale={yScale}
            />

            {/* Bottom axis */}
            <AxisBottom
              scale={xScale}
              top={innerHeight}
            />

            {/* Hover detection area */}
            <Bar
              width={innerWidth}
              height={innerHeight}
              className={styles['mouse-overlay']}
              onMouseOver={() => this.onMouseOver()}
              onMouseOut={() => this.onMouseOut()}
              onMouseMove={(e) => this.onMouseMove(e, data, xScale)}
            />
          </Group>
        </svg>
      </div>
    )
  }
}

export default BitcoinPrice;

【问题讨论】:

  • 我认为您的性能问题与mouseMove 中的 setState 相关,如果您真的不需要在其中设置 setState,请忽略它并改用 refs 来更新 UI 和值检索。
  • @mamounothman 成功了——谢谢!我发布了更新的代码作为答案。
  • Glade 成功了,接受您的回答,以便将此问题标记为已回答。
  • 我会在 1 天后做。

标签: reactjs


【解决方案1】:

答案是在 mouseMove 回调中使用 refs 而不是 setState。似乎 setState 的性能不足以每几毫秒调用一次,并且希望 UI 更新没有延迟。

注意一个 VX 特定的更改:我必须将 Line 组件更改为常规 XML line 标记,因为在 React 中,refs are not supported on function components

这是更新后的代码:

import React from 'react';
import rawData from 'lib/data.js';
import ChartData from 'lib/chart-data.js';
import { extent } from 'd3-array';
import { AxisLeft, AxisBottom } from '@vx/axis';
import { Group } from '@vx/group';
import { scaleTime, scaleLinear } from '@vx/scale';
import { Line, LinePath, Bar } from '@vx/shape';
import { localPoint } from '@vx/event';
import styles from './NeverLookBack.scss';

const chartDimensions = (() => {
  const margin = { top: 20, right: 20, bottom: 35, left: 75 };
  const width = 800;
  const height = 400;
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  return { margin, width, height, innerWidth, innerHeight };
})();

class BitcoinPrice extends React.Component {
  constructor(props) {
    super(props);

    this.lineRef = React.createRef();
    this.chartData = new ChartData(rawData);
  }

  onMouseOver() {
    this.lineRef.current.setAttribute('visibility', 'visible')
  }

  onMouseOut() {
    this.lineRef.current.setAttribute('visibility', 'hidden')
  }

  onMouseMove(e, data, xScale) {
    const { margin } = chartDimensions;
    const point = localPoint(e);
    const x = point.x - margin.left;

    console.log(`x`, x);

    this.lineRef.current.setAttribute('transform', `translate(${x}, 0)`)
  }

  render() {
    const { data } = this.chartData;
    const { margin, width, height, innerWidth, innerHeight } = chartDimensions;

    const xScale = scaleTime({
      range: [0, innerWidth],
      domain: extent(data, (d) => d.date)
    });

    const yScale = scaleLinear({
      range: [innerHeight, 0],
      domain: extent(data, (d) => d.price)
    });

    return (
      <div>
        <h1>Example chart: Bitcion price over time</h1>

        <svg width={800} height={400} viewBox={`0 0 ${width} ${height}`}>
          <Group top={margin.top} left={margin.left}>
            {/* The bitcoin price line */}
            <LinePath
              data={data}
              x={(d) => xScale(d.date)}
              y={(d) => yScale(d.price)}
              className={`${styles['path-line']} ${styles['path-price']}`}
            />

            {/* The vertical line that follows the cursor when hovering */}
            <line
              ref={this.lineRef}
              x1={0}
              y1={0}
              x2={0}
              y2={innerHeight}
              className={styles['mouse-line']}
            />

            {/* Left axis */}
            <AxisLeft
              scale={yScale}
            />

            {/* Bottom axis */}
            <AxisBottom
              scale={xScale}
              top={innerHeight}
            />

            {/* Hover detection area */}
            <Bar
              width={innerWidth}
              height={innerHeight}
              className={styles['mouse-overlay']}
              onMouseOver={() => this.onMouseOver()}
              onMouseOut={() => this.onMouseOut()}
              onMouseMove={(e) => this.onMouseMove(e, data, xScale)}
            />
          </Group>
        </svg>
      </div>
    )
  }
}

export default BitcoinPrice;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 2015-01-04
    • 2021-11-13
    • 1970-01-01
    • 2020-08-04
    • 2015-05-11
    相关资源
    最近更新 更多