Почему два отдельных компонента диаграммы втиснулись в одну и ту же коробку? - PullRequest
0 голосов
/ 26 мая 2020

Я делаю приборную панель, используя React.js и d3.js. Я столкнулся с этой интригующей проблемой, которая сбивала меня с толку на несколько часов.

Вот Scatterplot.js, написанное на d3.js:

import React, { Component } from "react"
import * as d3 from "d3"
import { ceil, floor } from "lodash"
import "./scatterplot.css"

export default class Scatterplot extends Component {

  constructor(props) {
    super(props)
    this.state = {
      name: "Level 1",
      category: "Status",
      yAxisAttribute: "YoY Growth Rate",
      xAxisAttribute: "Social Popularity",
      width: 700,
      height: 500,
    }
    this.chartRef = React.createRef()
    this.drawChart = this.drawChart.bind(this)
  }

  componentDidUpdate() {
    this.drawChart()
  }

  drawChart() {

    // Helper functions

    const calcMean = arr => {
      const reducer = (total, currentValue) => total + currentValue
      const sum = arr.reduce(reducer)
      return sum / arr.length
    }

    // Regarding data

    const { data } = this.props

    const xMin = floor(
      data.reduce(
        (a, b) => a["Social Popularity"] < b["Social Popularity"] ? a : b
      )["Social Popularity"],
      1
    )
    const xMax = ceil(
      data.reduce(
        (a, b) => a["Social Popularity"] > b["Social Popularity"] ? a : b
      )["Social Popularity"],
      1
    )

    const xAvg = calcMean(data.map(d => d["Social Popularity"]))

    const yMin = floor(
      data.reduce(
        (a, b) => a["YoY Growth Rate"] < b["YoY Growth Rate"] ? a : b
      )["YoY Growth Rate"],
      1
    )

    const yMax = ceil(
      data.reduce(
        (a, b) => a["YoY Growth Rate"] > b["YoY Growth Rate"] ? a : b
      )["YoY Growth Rate"],
      1
    )

    const yAvg = calcMean(data.map(d => d["YoY Growth Rate"]))

    // Config Chart

    const margin = {
      top: 50,
      right: 50,
      bottom: 50,
      left: 50
    }

    const width = this.state.width - margin.left - margin.right
    const height = this.state.height - margin.top - margin.bottom
    const offset = 10

    const svg = d3.select(".Chart")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr(
        "transform",
        `translate(${margin.left}, ${margin.top})`
      )

    // Add background image

    svg.append("svg:defs")
      .append("svg:pattern")
      .attr("id", "background")
      .attr("width", "100%")
      .attr("height", "100%")
      .append("svg:image")
      .attr("xlink:href", "https://live.staticflickr.com/65535/49921469713_111b7c41da_o.png")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .attr("x", 0)
      .attr("y", 0)

    svg.append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", width)
      .attr("height", height)
      .style("fill", "url(#background)")
      .style("stroke", "black")
      .style("stroke-dasharray", "1000")

    // Add tags

    const topLeftTag = svg.append("g")
      .attr("transform", "translate(0, 0)")

    topLeftTag.append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", 120)
      .attr("height", 20)
      .attr("class", "rect-tag")

    topLeftTag.append("text")
      .attr("dx", 6)
      .attr("dy", 14)
      .text("Small and Emerging")
      .attr("class", "rect-tag-text")

    const topRightTag = svg.append("g")
      .attr("transform", `translate(470, 0)`)

    topRightTag.append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", 130)
      .attr("height", 20)
      .attr("class", "rect-tag")

    topRightTag.append("text")
      .attr("dx", 6)
      .attr("dy", 14)
      .text("Popular and Growing")
      .attr("class", "rect-tag-text")

    const bottomLeftTag = svg.append("g")
      .attr("transform", `translate(0, 380)`)

    bottomLeftTag.append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", 100)
      .attr("height", 20)
      .attr("class", "rect-tag")

    bottomLeftTag.append("text")
      .attr("dx", 5)
      .attr("dy", 14)
      .text("Small and Stable")
      .attr("class", "rect-tag-text")

    const bottomRightTag = svg.append("g")
      .attr("transform", `translate(485, 380)`)

    bottomRightTag.append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", 115)
      .attr("height", 20)
      .attr("class", "rect-tag")

    bottomRightTag.append("text")
      .attr("dx", 5)
      .attr("dy", 14)
      .text("Popular and Stable")
      .attr("class", "rect-tag-text")

    // Add X axis
    const x = d3.scaleLinear()
      .domain([xMin, xMax])
      .range([0, width])

    svg.append("g")
      .attr("transform", `translate(0,${height})`)
      .attr("class", "axis x")
      .call(d3.axisBottom(x).tickSize(0).tickFormat(d => `${d * 100}%`))
      .selectAll("text")
      .style("text-anchor", "middle")

    // X axis label

    svg.append("text")
      .attr("text-anchor", "end")
      .attr("x", width / 2 + offset * 2)
      .attr("y", height + margin.bottom / 2 + offset)
      .text("Social Popularity")

    // Add Y axis

    const y = d3.scaleLinear()
      .domain([yMin, yMax * 1.1])
      .range([height, 0])

    svg.append("g")
      .attr("class", "axis-y")
      .call(d3.axisLeft(y).tickSize(0).tickFormat(d => `${d * 100}%`))
      .selectAll("text")
      .style("text-anchor", "end")

    // Y axis label
    svg.append("text")
      .attr("text-anchor", "end")
      .attr("transform", "rotate(-90)")
      .attr("x", margin.top - height / 2)
      .attr("y", -margin.left + offset * 1.5)
      .text("YoY Growth Rate")

    // Add horizontal line

    svg.append("line")
      .attr("class", "avg-line")
      .attr("x1", 0)
      .attr("y1", y(yAvg))
      .attr("x2", width)
      .attr("y2", y(yAvg))

    // Add vertical line
    svg.append("line")
      .attr("class", "avg-line")
      .attr("x1", x(xAvg))
      .attr("y1", 0)
      .attr("x2", x(xAvg))
      .attr("y2", height)

    // Color scale

    const colors = d3.scaleOrdinal()
      .domain(["Growing", "Declining", "Stable", "Seasonal", "Hot Topic"])
      .range(["#4ca457", "#b12318", "#7f7f7f", "#9b6b23", "#334a74"])

    const points = svg.selectAll("g.node")
      .data(data, d => d["Level 1"])
      .enter()
      .append("g")
      .attr("class", "node")
      .attr("transform", d => `translate(${x(d["Social Popularity"])}, ${y(d["YoY Growth Rate"])})`)

    points.append("circle")
      .attr("r", 5)
      .attr("class", "dot")
      .style("fill", d => colors(d["Status"]))

    points.append("text")
      .attr("class", "annotation")
      .attr("dy", -10)
      .text(d => d["Level 1"])


  }

  render() {
    return (
      <div className="Chart" ref={this.chartRef}>
      </div>
    )
  }
}

Код выше dr aws диаграмма, которая выглядит что это:

enter image description here

Интригующая проблема возникла, когда я попытался передать тот же компонент диаграммы (т.е. Scatterplot.js) в App.js в React.js. Вот App.js:

import React, { Component, Fragment } from "react"
import "react-bulma-components/dist/react-bulma-components.min.css";
import { Section, Container, Heading, Columns, Box } from "react-bulma-components"
import Scatterplot from "./charts/Scatterplot.js"
import "./App.sass"
import "./App.css"

export default class App extends Component {

    state = {
        passionPoint1: null,
        passionPoint2: null,
        passionPoint3: null,
        isLoading: true
    }

    componentDidMount() {
        fetch("/dashboard")
            .then(response => response.json())
            .then(data => this.setState({
                author: data.author,
                passionPoints1: JSON.parse(data.level1_data),
                passionPoints2: JSON.parse(data.level2_data),
                passionPoints3: JSON.parse(data.level3_data),
            }))
        this.setState({ isLoading: false })
    }

    render() {
        const { isLoading, passionPoints1, passionPoints2, passionPoints3 } = this.state
        if (isLoading) {
            return null
        } else {
            return (
                <Fragment>
                    <Section size="small">
                        <Container>
                            <Heading>The chart takes up Box 1, which is what I expected</Heading>
                            <Heading subtitle>
                                By Author
                            </Heading>
                        </Container>
                    </Section>
                    <Section size="small">
                        <Columns>
                            <Columns.Column size={7}>
                                <Box>
                                    <Scatterplot data={passionPoints1} />
                                </Box>
                            </Columns.Column>
                            <Columns.Column size={5}>
                                <Box>
                                    <p>Box 2</p>
                                </Box>
                            </Columns.Column>
                            <Columns.Column size={7}>
                                <Box>
                                    <p>Box 3</p>
                                </Box>
                            </Columns.Column>
                            <Columns.Column size={5}>
                                <Box>
                                    <p>Box 4</p>
                                </Box>
                            </Columns.Column>
                        </Columns>
                    </Section>
                </Fragment>
            )
        }
    }
}

Панель управления теперь выглядит так, как я и ожидал.

enter image description here

Однако , в App.js, если я заменю

                        <Box>
                            <p>Box 3</p>
                        </Box>

на

                        <Box>
                            <Scatterplot data={passionPoints1} />
                        </Box>

Новая диаграмма не занимала место в ячейке 3, вместо этого она слилась с диаграммой в ячейке 1. Это выглядит так:

enter image description here

Меня это очень раздражает. Мне нужна новая диаграмма, занимающая пространство блока 3 вместо слияния с блоком 1.

Я пробовал другие интерфейсные фреймворки, такие как material-ui и ant-design, и возникла та же проблема, что и хорошо. Здесь bulma - только один из тех случаев, которые я пробовал. Я также пробовал использовать другой источник данных, но проблема не исчезла. Я пробовал назначать различные идентификаторы блоку диаграммы, но это не помогло. Может ли кто-нибудь объяснить, почему это произошло, и есть ли какое-либо решение для получения желаемого результата?

Ответы [ 2 ]

1 голос
/ 26 мая 2020

d3.select(".Chart") выберет элемент с классом «Диаграмма». Он находит ваш первый div с этим классом, ничто в d3 не привязано к вашему компоненту. Вам нужно найти другой способ получить элемент. Вероятно, вы захотите использовать ref :

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }

  drawChart() {
    ...
    const svg = d3.select(this.myRef.current)
      .append("svg")
    ...
  }
}
0 голосов
/ 26 мая 2020
const svg = d3.select(".Chart")

всегда выбирает первый элемент с классом Chart в dom (который является первым блоком). попробуйте использовать более подробный селектор c для контейнера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...