Переходите и круги и текст (в пределах g элемента) в единый общий шаблон обновления - PullRequest
0 голосов
/ 25 сентября 2018

Рефакторинг вопроса, который я отправил ~ 30 минут назад, чтобы сделать его лучше.У меня есть следующий код, который использует общий шаблон обновления для перемещения кругов и текста по экрану.

class App extends React.Component {
  constructor(props) { 
    super(props);
    
    this.state = {
      mybutton: "A"
    }
  } 
    
  handleButtonChange = (event) => {
    this.setState({ mybutton: event.target.value });
  };
      
  drawPoints() {
    const {mybutton} = this.state;
    
    const myData = [
      {x1:30, x2: 140, y1: 50, y2: 60, letter:"A"},
      {x1:50, x2: 150, y1: 60, y2: 120, letter:"B"},
      {x1:70, x2: 120, y1: 70, y2: 110, letter:"C"}
    ];
    
    const pointsLayer = d3.select('#my-svg').select('g.points')
    const xShift = function(d) {
      if(mybutton === "A") {
        return d.x1
      } else {
        return d.x2
      }
    }
    const yShift = function(d) {
      if(mybutton === "A") {
        return d.y1
      } else {
        return d.y2
      }
    }
    const textChange = function(d) {
      if(mybutton === "A") {
        return "white"
      } else {
        return "black"
      }
    }
    const circleColorChange = function(d) {
      if(mybutton === "A") {
        return "#FF0000"
      } else {
        return "#FFAAAA"
      }
    }


		pointsLayer
			.selectAll("circle")
			.data(myData)
			.exit()
			.transition()
				.duration(100)
				.attr('r', 0)
			.remove();

		pointsLayer
			.selectAll("circle")
			.data(myData)
			.enter()
			.append("circle")
				.attr("cx", d => xShift(d))
				.attr("cy", d => yShift(d))
        .attr("r", 30)
        .attr("fill", d => circleColorChange(d))
//        .on("mouseover", ...)
//        .on("mouseout", ...)

    pointsLayer
			.selectAll("circle")
			.data(myData)
			.transition()
			.duration(1000)
			.delay((d, i) => i * 0.5)
				.attr("cx", d => xShift(d))
				.attr("cy", d => yShift(d))
        .attr("fill", d => circleColorChange(d))

    pointsLayer.selectAll("text").remove('*')
    pointsLayer
        .selectAll("text")
        .data(myData)
        .enter()
        .append('text')
            .attr('x', d => xShift(d))
            .attr('y', d => yShift(d))
            .text(d => d.letter)
  } 
      
  componentDidMount() {
    d3.select('#my-svg')
			.attr('width', '100%')
			.attr('height', '100%')
			.attr('viewBox', "0 0 " + (800) + " " + 600)  
			.attr('preserveAspectRatio', "xMaxYMax")  
  
    this.drawPoints();
  }
  
  componentDidUpdate() {
    this.drawPoints()
  }
  
  render() {
  
    const {mybutton} = this.state;
    return(
      <div>
        <form>
            <div>
              <label>
                  <input
                      type={"radio"}
                      value={"A"}
                      checked={mybutton === "A"}
                      onChange={this.handleButtonChange}
                  />
                  <span>{"A"}</span>
              </label>
            </div>
            <div>
              <label>
                  <input
                      type={"radio"}
                      value={"B"}
                      checked={mybutton === "B"}
                      onChange={this.handleButtonChange}
                  />
                  <span>{"B"}</span>
              </label>
            </div>
        </form>
      
        <svg id="my-svg">
          <g className="points" />
        </svg>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>


<div id='root'>
  Come On Work!
</div>

В настоящее время я использую общий шаблон обновления для кругов.Для текста я просто удаляю отображаемый текст и перерисовываю его, однако я намерен использовать общий шаблон обновления для скольжения текста.

Однако , мое приложение имеет определенные эффекты .on () mouseover и mouseout для кругов, которые перепутались, когда мой курсор находится над текстом в круге, так как my .on ()функции размещены только на элементах моего круга.

Я хотел бы включить оба круги и текст каждый в свой собственный элемент, так что структура:

<g> 
  <circle>
  <text>
</g>

... и в идеале я хотел бы применить общий шаблон обновления по одному разу к элементам g, а не по одному разу для кругов и для текста.Тогда, я полагаю, функции .on () могут быть размещены и на данных элементах.

В этом примере пропущены другие события, которые происходят при переходе, в том числе изменение цвета круга, радиуса, обводки и т. Д.использование других вспомогательных функций (аналогично xShfit, yShift, но для этих стилей).Кроме того, цвет текста также будет меняться при переходах.

Согласно ответу на первоначальный вопрос по этому вопросу, я планирую прочитать документы D3 позже сегодня и завтра в деталях, чтобы улучшить общий шаблон обновления.связанные с D3, но сейчас я все еще ищу помощи в этой проблеме и надеюсь, что повторная публикация привлечет больше внимания.

Любая помощь в рефакторинге этого кода, так что потребуется только один общий шаблон обновления дляОбновление кругов и текста вместе, из элементов, будет очень полезно !!

Спасибо!

РЕДАКТИРОВАТЬ: редактируется с функцией изменения цвета круга и другой (неиспользованной) функцией, которая будетгипотетически использоваться для изменения цвета текста.

EDIT2: также не уверен, что общий шаблон обновления, который я сейчас использую, является хорошим / правильным в целом.Я использую выход и вход и обновление, но не объединяю нигде (не уверен, если это необходимо) ...

1 Ответ

0 голосов
/ 25 сентября 2018

Для начала, это, кажется, проблема XY : если единственная проблема - метки, мешающие при наведении курсора, просто сделайте ...

.attr("pointer-events", "none")

... длявыбор текстов.

Кроме того, несмотря на то, что людям обычно нравится помещать все элементы данного узла (кружок, текст и т. д.) в группу, имейте в виду, что этот подход небез проблем: например, используя точно ваши координаты и радиус круга, в тот момент, когда вы поместите текст в качестве родного элемента круга в <g>, некоторые круги в других группах покроют некоторые тексты.

ВсеПри этом шаблон для групп может выглядеть примерно так:

let groups = pointsLayer.selectAll(".myGroups")
  .data(myData);

const groupsExit = groups.exit().remove();

const groupsEnter = groups.enter()
  .append("g")
  .attr("class", "myGroups");

groupsEnter.append("circle")
  .attr("r", 20)
  .attr("fill", d => circleColorChange(d));

groupsEnter.append("text")
  .style("text-anchor", "middle")
  .style("dominant-baseline", "central")
  .text(d => d.letter);

groups = groupsEnter.merge(groups)
  .attr("transform", d => "translate(" + xShift(d) + "," + yShift(d) + ")");
//        .on("mouseover", ...)
//        .on("mouseout", ...)

Обратите внимание, что мы не (повторно) привязываем данные для выбора выхода, как вы делали это в своем коде.

Имейте в виду, что все элементы здесь добавляются в выбор ввода.Если вам нужен шаблон обновления / ввода / выхода для них, вам придется его вложить.

Наконец, вы сказали ...

Я использую выход и ввод и обновление, но нигде не объединяюсь (не уверен, если нужно)

Выметод merge() вообще не нужен, вы можете написать любой код D3 без него.Мы используем его (как я сделал здесь) только для того, чтобы сохранить несколько повторяющихся строк.

Вот ваш обновленный код (с меньшим радиусом для кругов, чтобы мы могли видеть все тексты кругов):

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

    this.state = {
      mybutton: "A"
    }
  }

  handleButtonChange = (event) => {
    this.setState({
      mybutton: event.target.value
    });
  };

  drawPoints() {
    const {
      mybutton
    } = this.state;

    const myData = [{
        x1: 30,
        x2: 140,
        y1: 50,
        y2: 60,
        letter: "A"
      },
      {
        x1: 50,
        x2: 150,
        y1: 60,
        y2: 120,
        letter: "B"
      },
      {
        x1: 70,
        x2: 120,
        y1: 70,
        y2: 110,
        letter: "C"
      }
    ];

    const pointsLayer = d3.select('#my-svg').select('g.points')
    const xShift = function(d) {
      if (mybutton === "A") {
        return d.x1
      } else {
        return d.x2
      }
    }
    const yShift = function(d) {
      if (mybutton === "A") {
        return d.y1
      } else {
        return d.y2
      }
    }
    const textChange = function(d) {
      if (mybutton === "A") {
        return "white"
      } else {
        return "black"
      }
    }
    const circleColorChange = function(d) {
      if (mybutton === "A") {
        return "#FF0000"
      } else {
        return "#FFAAAA"
      }
    }

    let groups = pointsLayer.selectAll(".myGroups")
      .data(myData);

    const groupsExit = groups.exit().remove();

    const groupsEnter = groups.enter()
      .append("g")
      .attr("class", "myGroups");

    groupsEnter.append("circle")
      .attr("r", 20)
      .attr("fill", d => circleColorChange(d));

    groupsEnter.append("text")
      .style("text-anchor", "middle")
      .style("dominant-baseline", "central")
      .text(d => d.letter);

    groups = groupsEnter.merge(groups)
      .attr("transform", d => "translate(" + xShift(d) + "," + yShift(d) + ")");
    //        .on("mouseover", ...)
    //        .on("mouseout", ...)

  }

  componentDidMount() {
    d3.select('#my-svg')
      .attr('width', '100%')
      .attr('height', '100%')
      .attr('viewBox', "0 0 " + (800) + " " + 600)
      .attr('preserveAspectRatio', "xMaxYMax")

    this.drawPoints();
  }

  componentDidUpdate() {
    this.drawPoints()
  }

  render() {

    const {
      mybutton
    } = this.state;
    return ( <
      div >
      <
      form >
      <
      div >
      <
      label >
      <
      input type = {
        "radio"
      }
      value = {
        "A"
      }
      checked = {
        mybutton === "A"
      }
      onChange = {
        this.handleButtonChange
      }
      /> <
      span > {
        "A"
      } < /span> < /
      label > <
      /div> <
      div >
      <
      label >
      <
      input type = {
        "radio"
      }
      value = {
        "B"
      }
      checked = {
        mybutton === "B"
      }
      onChange = {
        this.handleButtonChange
      }
      /> <
      span > {
        "B"
      } < /span> < /
      label > <
      /div> < /
      form >

      <
      svg id = "my-svg" >
      <
      g className = "points" / >
      <
      /svg> < /
      div >
    );
  }
}

ReactDOM.render( <
  App / > ,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>


<div id='root'>
  Come On Work!
</div>
...