Усовершенствованное позиционирование и определение размера группы переключателей в верхней части SVG - PullRequest
0 голосов
/ 07 июня 2018

Редактировать: вот ссылка на мое приложение , в котором в настоящее время отображается проблема, на которую я ссылаюсь.

Я приложил все усилия, чтобы пост был кратким и полным, но извинения взаранее, если это немного долго.Я думаю, что длина полезна.

У меня есть то, что кажется сложной задачей, включающей расположение группы переключателей React поверх SVG, которая показывает график.Я создал следующий фрагмент кода, который включает в себя два компонента (граф и компонент контейнера), чтобы помочь выделить проблему, с которой я столкнулся.

Вместо реального графика (точечная диаграмма, гистограмма, ш/ д) я вместо этого сделал просто прямоугольный SVG с 3-мя красочными ректами.В компонент SVG я также добавил 6 черных ящиков справа, которые я использую в качестве группы переключателей, встроенной непосредственно в SVG с использованием D3.

По различным причинам состояния (главным образом потому, что яхотел бы, чтобы мой контейнерный компонент содержал состояние графика, так как у контейнерного компонента будут другие части, которые требуют значений кнопок), я работаю над созданием набора переключателей React, чтобы заменить переключатели D3 SVG, но яборется с позиционированием кнопок.Учитывая способ, которым я сделал SVG (используя Viewbox), переключатели React, встроенные непосредственно в SVG, изменяют масштаб при изменении ширины окна браузера.Это важно не потому, что зрители моего сайта будут активно изменять размер окна браузера (они не будут), а скорее потому, что я не знаю, насколько широк браузер пользователя, и это изменение размера кнопок с изменением размера SVG означаеткнопки всегда будут выглядеть хорошо.

Однако переключатели React не масштабируются.Как вы можете видеть здесь:

<div style={{"width":"85%", "margin":"0 auto", "marginTop":"75px", "padding":"0", "position":"relative"}}>
  <div style={{"width":"450px", "position":"absolute", "bottom":"32px", "left":"20px", "zIndex":"1"}}>
      <h3>X-Axis Stat</h3>
      {xStatButtons}
  </div>

  <div style={{"position":"absolute", "top":"5px", "left":"8px", "zIndex":"1"}}>
      <h3>Y-Axis Stat</h3>
      {yStatButtons}
  </div>
</div>

Я использую положение = абсолютное | относительное, наряду с верхним | нижним | левым стилями, чтобы расположить кнопки в верхней части SVG.Я также изменяю z-index на div-кнопках реагирования, чтобы они находились поверх SVG.

Перед просмотром кода, пожалуйста, запустите фрагмент кода и откройте на весь экран, а также увеличьте и уменьшите ширину окна вашего браузера.

class GraphComponent extends React.Component {

	constructor(props) { 
		super(props);
    
		this.chartProps = {
			chartWidth: 400, // Dont Change These, For Viewbox 
			chartHeight: 200 // Dont Change These, For Viewbox 
		};
	}

  // Lifecycle Components
	componentDidMount() {
    
		const { chartHeight, chartWidth, svgID } = this.chartProps;
		d3.select('#d3graph')
			.attr('width', '100%')
			.attr('height', '100%')
			.attr('viewBox', "0 0 " + chartWidth + " " + chartHeight)
			.attr('preserveAspectRatio', "xMaxYMax");
    
    const fakeButtons = d3.select('g.fakeButtons')
    for(var i = 0; i < 6; i++) {
      fakeButtons
        .append('rect')
        .attr('x', 370)
        .attr('y', 15 + i*25)
        .attr('width', 25)
        .attr('height', 20)
        .attr('fill', 'black')
        .attr('opacity', 1)
        .attr('stroke', 'black')
        .attr('stroke-width', 2) 
    }
    
    d3.select('g.myRect')
      .append('rect')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', chartWidth)
        .attr('height', chartHeight)
        .attr('fill', 'red')
        .attr('opacity', 0.8)
        .attr('stroke', 'black')
        .attr('stroke-width', 5)
    
    d3.select('g.myRect')
      .append('rect')
        .attr('x', 35)
        .attr('y', 35)
        .attr('width', chartWidth - 70)
        .attr('height', chartHeight - 70)
        .attr('fill', 'blue')
        .attr('opacity', 0.8)
        .attr('stroke', 'black')
        .attr('stroke-width', 2)
    
    d3.select('g.myRect')
      .append('rect')
        .attr('x', 70)
        .attr('y', 70)
        .attr('width', chartWidth - 140)
        .attr('height', chartHeight - 140)
        .attr('fill', 'green')
        .attr('opacity', 0.8)
        .attr('stroke', 'black')
        .attr('stroke-width', 2)
	}
  
	render() {
 		return (
			<div ref="scatter">
				<svg id="d3graph">
          <g className="myRect" />
          <g className="fakeButtons" />
				</svg>
			</div>
		)
	}
}

class GraphContainer extends React.Component {

	constructor(props) { 
		super(props);
    this.state = {
      statNameX: "AtBats",
      statNameY: "Saves"
    }
	}

	render() {
    
    const { statNameX, statNameY } = this.state;
    const xStats = [
      { value: "GamesPlayed", label: "G" },
      { value: "AtBats", label: "AB" },
      { value: "Runs", label: "R" },
      { value: "Hits", label: "H" },
      { value: "SecondBaseHits", label: "2B" },
      { value: "ThirdBaseHits", label: "3B" },
      { value: "Homeruns", label: "HR" },
      { value: "RunsBattedIn", label: "RBI" }];
    const yStats = [
      { value: "Wins", label: "W" },
      { value: "Losses", label: "L" },
      { value: "EarnedRunAvg", label: "ERA" },
      { value: "GamesPlayed", label: "G" },
      { value: "GamesStarted", label: "GS" },
      { value: "Saves", label: "SV" },
      { value: "RunsAllowed", label: "R"}]; 
    
    const xStatButtons = 
          <form>
            <div className="qualify-radio-group scatter-group">
              {xStats.map((d, i) => {
                return (
                  <label key={'xstat-' + i}>
                    <input
                      type={"radio"}
                      value={xStats[i].value}
                    />
                    <span>{xStats[i].label}</span>
                  </label>
                )
              })}
            </div>
          </form>;
    
    const yStatButtons = 
          <form>
            <div className="qualify-radio-group scatter-group">
              {yStats.map((d, i) => {
                return (
                  <label  className="go-vert" key={'ystat-' + i}>
                    <input
                      type={"radio"}
                      value={yStats[i].value}
                    />
                    <span>{yStats[i].label}</span>
                  </label>
                )
              })}
            </div>
          </form>;
    
 		return (
			<div style={{"width":"85%", "margin":"0 auto", "marginTop":"75px", "padding":"0", "position":"relative"}}>
        
        <div style={{"width":"450px", "position":"absolute", "bottom":"32px", "left":"20px", "zIndex":"1"}}>
          <h3>X-Axis Stat</h3>
          {xStatButtons}
        </div>

        <div style={{"position":"absolute", "top":"5px", "left":"8px", "zIndex":"1"}}>
          <h3>Y-Axis Stat</h3>
          {yStatButtons}
        </div>
        
        <GraphComponent />
      </div>
		)
	}
}



ReactDOM.render(
  <GraphContainer />,
  document.getElementById('root')
);
.scatter-group input[type=radio] {
	visibility:hidden;
	width:0px;
	height:0px;
	overflow:hidden;
}

.scatter-group input[type=radio] + span {
	cursor: pointer;
	display: inline-block;
	vertical-align: top;
	line-height: 25px;
	padding: 2px 6px;
	border-radius: 2px;
	color: #333;
	background: #EEE;

	border-radius: 5px;
	border: 2px solid #333;

	margin-right: 2px;
}

.scatter-group input[type=radio]:not(:checked) + span {
	cursor: pointer;
	background-color: #EEE;
	color: #333;
}

.scatter-group input[type=radio]:not(:checked) + span:hover{
	cursor: pointer;
	background: #888;
}

.scatter-group input[type=radio]:checked + span{
	cursor: pointer;
	background-color: #333;
	color: #EEE;
}

.go-vert {
	display: block;
}
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

<div id='root'>
  WORK
</div>

У меня такой вопрос: Как правильно настроить размеры и позиционирование с помощью переключателей React, чтобы они вели себя(с точки зрения размера и позиционирования) аналогично тому, как встроенные радиокнопки непосредственно в SVG (как изменяются размеры черных ящиков)?

Любая помощь вообще приветствуется.

1 Ответ

0 голосов
/ 12 июня 2018

Ваш пробег может варьироваться, но я бы посоветовал попробовать использовать vw единицы для определения размера переключателей.1vw равно 1% от области просмотра, которую можно использовать в свойствах font-size, padding и line-height элементов переключателя.

Извлечение изменений, которые я внес в ваш пример:

    .scatter-group input[type=radio] + span {
        font-size: calc(6px + 0.8vw);
        line-height: 1;
        padding: 0.8vw;
    }

Сначала я комбинирую calc и vw, чтобы получить плавный размер шрифта, который не опускается ниже 6px в маленьком окне просмотра.line-height влияет на общую высоту кнопки, поэтому я сбрасываю ее на 1, что означает, что она будет 100% от font-size.Наконец, я использую vw блок для заполнения.Вы можете использовать тот же трюк calc, что и раньше, для точного управления размерами.

Пример не идеален и потребует некоторой настройки, чтобы он соответствовал SVG в реальных условиях.Контейнеры для кнопок также следует настраивать в зависимости от области просмотра, я их не трогал, поскольку это не является вопросом вопроса.

vw и calc() поддерживаются во всех основных браузерах.

Ссылка:
- https://www.sitepoint.com/css-viewport-units-quick-start/
- https://css -tricks.com /фрагменты / css / жидкости типографии /
- https://www.smashingmagazine.com/2016/05/fluid-typography/

class GraphComponent extends React.Component {

	constructor(props) { 
		super(props);
    
		this.chartProps = {
			chartWidth: 400, // Dont Change These, For Viewbox 
			chartHeight: 200 // Dont Change These, For Viewbox 
		};
	}

  // Lifecycle Components
	componentDidMount() {
    
		const { chartHeight, chartWidth, svgID } = this.chartProps;
		d3.select('#d3graph')
			.attr('width', '100%')
			.attr('height', '100%')
			.attr('viewBox', "0 0 " + chartWidth + " " + chartHeight)
			.attr('preserveAspectRatio', "xMaxYMax");
    
    const fakeButtons = d3.select('g.fakeButtons')
    for(var i = 0; i < 6; i++) {
      fakeButtons
        .append('rect')
        .attr('x', 370)
        .attr('y', 15 + i*25)
        .attr('width', 25)
        .attr('height', 20)
        .attr('fill', 'black')
        .attr('opacity', 1)
        .attr('stroke', 'black')
        .attr('stroke-width', 2) 
    }
    
    d3.select('g.myRect')
      .append('rect')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', chartWidth)
        .attr('height', chartHeight)
        .attr('fill', 'red')
        .attr('opacity', 0.8)
        .attr('stroke', 'black')
        .attr('stroke-width', 5)
    
    d3.select('g.myRect')
      .append('rect')
        .attr('x', 35)
        .attr('y', 35)
        .attr('width', chartWidth - 70)
        .attr('height', chartHeight - 70)
        .attr('fill', 'blue')
        .attr('opacity', 0.8)
        .attr('stroke', 'black')
        .attr('stroke-width', 2)
    
    d3.select('g.myRect')
      .append('rect')
        .attr('x', 70)
        .attr('y', 70)
        .attr('width', chartWidth - 140)
        .attr('height', chartHeight - 140)
        .attr('fill', 'green')
        .attr('opacity', 0.8)
        .attr('stroke', 'black')
        .attr('stroke-width', 2)
	}
  
	render() {
 		return (
			<div ref="scatter">
				<svg id="d3graph">
          <g className="myRect" />
          <g className="fakeButtons" />
				</svg>
			</div>
		)
	}
}

class GraphContainer extends React.Component {

	constructor(props) { 
		super(props);
    this.state = {
      statNameX: "AtBats",
      statNameY: "Saves"
    }
	}

	render() {
    
    const { statNameX, statNameY } = this.state;
    const xStats = [
      { value: "GamesPlayed", label: "G" },
      { value: "AtBats", label: "AB" },
      { value: "Runs", label: "R" },
      { value: "Hits", label: "H" },
      { value: "SecondBaseHits", label: "2B" },
      { value: "ThirdBaseHits", label: "3B" },
      { value: "Homeruns", label: "HR" },
      { value: "RunsBattedIn", label: "RBI" }];
    const yStats = [
      { value: "Wins", label: "W" },
      { value: "Losses", label: "L" },
      { value: "EarnedRunAvg", label: "ERA" },
      { value: "GamesPlayed", label: "G" },
      { value: "GamesStarted", label: "GS" },
      { value: "Saves", label: "SV" },
      { value: "RunsAllowed", label: "R"}]; 
    
    const xStatButtons = 
          <form>
            <div className="qualify-radio-group scatter-group">
              {xStats.map((d, i) => {
                return (
                  <label key={'xstat-' + i}>
                    <input
                      type={"radio"}
                      value={xStats[i].value}
                    />
                    <span>{xStats[i].label}</span>
                  </label>
                )
              })}
            </div>
          </form>;
    
    const yStatButtons = 
          <form>
            <div className="qualify-radio-group scatter-group">
              {yStats.map((d, i) => {
                return (
                  <label  className="go-vert" key={'ystat-' + i}>
                    <input
                      type={"radio"}
                      value={yStats[i].value}
                    />
                    <span>{yStats[i].label}</span>
                  </label>
                )
              })}
            </div>
          </form>;
    
 		return (
			<div style={{"width":"85%", "margin":"0 auto", "marginTop":"75px", "padding":"0", "position":"relative"}}>
        
        <div style={{"width":"450px", "position":"absolute", "bottom":"32px", "left":"20px", "zIndex":"1"}}>
          <h3>X-Axis Stat</h3>
          {xStatButtons}
        </div>

        <div style={{"position":"absolute", "top":"5px", "left":"8px", "zIndex":"1"}}>
          <h3>Y-Axis Stat</h3>
          {yStatButtons}
        </div>
        
        <GraphComponent />
      </div>
		)
	}
}



ReactDOM.render(
  <GraphContainer />,
  document.getElementById('root')
);
.scatter-group input[type=radio] {
	visibility:hidden;
	width:0px;
	height:0px;
	overflow:hidden;
}

.scatter-group input[type=radio] + span {
	cursor: pointer;
	display: inline-block;
	vertical-align: top;
	line-height: 1;
	padding: 0.8vw;
    font-size: calc(6px + 0.8vw);
	border-radius: 2px;
	color: #333;
	background: #EEE;

	border-radius: 5px;
	border: 2px solid #333;

	margin-right: 2px;
}

.scatter-group input[type=radio]:not(:checked) + span {
	cursor: pointer;
	background-color: #EEE;
	color: #333;
}

.scatter-group input[type=radio]:not(:checked) + span:hover{
	cursor: pointer;
	background: #888;
}

.scatter-group input[type=radio]:checked + span{
	cursor: pointer;
	background-color: #333;
	color: #EEE;
}

.go-vert {
	display: block;
}
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

<div id='root'>
  WORK
</div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...