Как я могу элегантно растянуть SVG с фиксированным соотношением сторон, чтобы соответствовать ширине ИЛИ высоте его содержащего элемента? - PullRequest
1 голос
/ 04 марта 2020

Я делаю простой HTML макет с SVG, отображаемым над рядом кнопок:

<!DOCTYPE html>
<html>
	<head>
		<style type="text/css">
			#playgroundForTest
			{
				width: 500px;
				height: 260px;

				border-width: 3px;
				border-color: gray;
				border-style: solid;
			}

			#buttonContainer
			{
				display: flex;
				flex-direction: row;
				justify-content: center;
			}

			#mainContainer
			{
				display: flex;
				flex-direction: column;
				align-items: center;
			}

			svg
			{
				height: 200px;
			}
		</style>
	</head>
	<body>
		<div id="playgroundForTest">
			<div id="mainContainer">
				<svg viewBox="0 0 1000 1000">
					<circle cx="500" cy="500" r="450" fill="#22f" />
					<circle cx="50" cy="50" r="50" fill="#88f" />
					<circle cx="70" cy="930" r="70" fill="#bbf" />
					<circle cx="910" cy="910" r="90" fill="#44f" />
					<circle cx="900" cy="100" r="100" fill="#a0a0ff" />
				</svg>
				<div id="buttonContainer">
					<button style="height: 60px">88</button>
					<button style="height: 60px">888</button>
				</div>
			</div>
		</div>
	</body>
</html>

В любой данный момент серый прямоугольник вокруг него ("playsForTest") имеет фиксированный размер с произвольным значением. Я хочу, чтобы SVG растягивался при изменении размера, как в этой анимации:

<!DOCTYPE html>
<html>
	<head>
		<style type="text/css">
			@keyframes playgroundPulse
			{
				from
				{
					height: 260px;
				}

				to
				{
					height: 410px;
				}
			}

			@keyframes svgPulse
			{
				from
				{
					height: 200px;
				}

				to
				{
					height: 350px;
				}
			}

			#playgroundForTest
			{
				width: 500px;
				height: 260px;

				border-width: 3px;
				border-color: gray;
				border-style: solid;

				animation-duration: 1s;
				animation-name: playgroundPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}

			#buttonContainer
			{
				display: flex;
				flex-direction: row;
				justify-content: center;
			}

			#mainContainer
			{
				display: flex;
				flex-direction: column;
				align-items: center;
			}

			svg
			{
				height: 200px;

				animation-duration: 1s;
				animation-name: svgPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}
		</style>
	</head>
	<body>
		<div id="playgroundForTest">
			<div id="mainContainer">
				<svg viewBox="0 0 1000 1000">
					<circle cx="500" cy="500" r="450" fill="#22f" />
					<circle cx="50" cy="50" r="50" fill="#88f" />
					<circle cx="70" cy="930" r="70" fill="#bbf" />
					<circle cx="910" cy="910" r="90" fill="#44f" />
					<circle cx="900" cy="100" r="100" fill="#a0a0ff" />
				</svg>
				<div id="buttonContainer">
					<button style="height: 60px">88</button>
					<button style="height: 60px">888</button>
				</div>
			</div>
		</div>
	</body>
</html>

Я также хочу, чтобы он точно соответствовал ширине, как в этой взломанной анимации:

<!DOCTYPE html>
<html>
	<head>
		<style type="text/css">
			@keyframes playgroundPulse
			{
				from
				{
					width: 250px;
				}

				to
				{
					width: 450px;
				}
			}

			@keyframes svgPulse
			{
				from
				{
					top: 140px;
					width: 250px;
				}

				to
				{
					top: 40px;
					width: 450px;
				}
			}

			@keyframes buttonPulse
			{
				from
				{
					top: 277px;
				}

				to
				{
					top: 77px;
				}
			}

			#playgroundForTest
			{
				width: 250px;
				height: 590px;

				border-width: 3px;
				border-color: gray;
				border-style: solid;

				animation-duration: 1s;
				animation-name: playgroundPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}

			#buttonContainer
			{
				display: flex;
				flex-direction: row;
				justify-content: center;

				position: relative;

				animation-duration: 1s;
				animation-name: buttonPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}

			svg
			{
				position: relative;
				width: 250px;

				animation-duration: 1s;
				animation-name: svgPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}
		</style>
	</head>
	<body>
		<div id="playgroundForTest">
			<div id="mainContainer">
				<svg viewBox="0 0 1000 1000">
					<circle cx="500" cy="500" r="450" fill="#22f" />
					<circle cx="50" cy="50" r="50" fill="#88f" />
					<circle cx="70" cy="930" r="70" fill="#bbf" />
					<circle cx="910" cy="910" r="90" fill="#44f" />
					<circle cx="900" cy="100" r="100" fill="#a0a0ff" />
				</svg>
				<div id="buttonContainer">
					<button style="height: 60px">88</button>
					<button style="height: 60px">888</button>
				</div>
			</div>
		</div>
	</body>
</html>

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

<!DOCTYPE html>
<html>
	<head>
		<style type="text/css">
			@keyframes playgroundPulse
			{
				from
				{
					width: 250px;
				}

				to
				{
					width: 450px;
				}
			}

			@keyframes svgPulse
			{
				from
				{
					top: 0px;
					width: 250px;
				}

				to
				{
					top: 0px;
					width: 450px;
				}
			}

			#playgroundForTest
			{
				width: 250px;
				height: 590px;

				border-width: 3px;
				border-color: gray;
				border-style: solid;

				animation-duration: 1s;
				animation-name: playgroundPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}

			#buttonContainer
			{
				display: flex;
				flex-direction: row;
				justify-content: center;
			}

			svg
			{
				position: relative;
				width: 250px;

				animation-duration: 1s;
				animation-name: svgPulse;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}
		</style>
	</head>
	<body>
		<div id="playgroundForTest">
			<div id="mainContainer">
				<svg viewBox="0 0 1000 1000">
					<circle cx="500" cy="500" r="450" fill="#22f" />
					<circle cx="50" cy="50" r="50" fill="#88f" />
					<circle cx="70" cy="930" r="70" fill="#bbf" />
					<circle cx="910" cy="910" r="90" fill="#44f" />
					<circle cx="900" cy="100" r="100" fill="#a0a0ff" />
				</svg>
				<div id="buttonContainer">
					<button style="height: 60px">88</button>
					<button style="height: 60px">888</button>
				</div>
			</div>
		</div>
	</body>
</html>

Я хочу, чтобы все это было сделано "элегантно" - то есть, без JavaScript, жестко закодированных значений расстояний (за возможным исключением "100%"), глупых хаков с участием padding-top или что-то еще, или что-нибудь еще уродливое. Я предполагаю, что это означает, что я буду использовать только макеты flexbox и grid. Но я не могу справиться с этим.

Есть ли способ сделать это? Кажется, что flexbox и / или grid должны быть достаточно мощными, но я не могу заставить их работать ... Если это невозможно, я откажусь от некоторых ограничений и, возможно, сделаю что-то уродливое, но сейчас я спрашиваю как это сделать красиво.

1 Ответ

2 голосов
/ 05 марта 2020

Может быть, я не совсем понял, но я думаю, что это можно сделать так:

Демо: https://codepen.io/Alexander9111/pen/eYNGQPz:

body {
  background-color: #e9ecef;
}

.chart {
  /* background: #eee; */
  /* background: white; */
  border: 1px solid black;
  padding: 3px;
}

.chart div {
  width: 0;
  transition: all 1s ease-out;
  -moz-transition: all 1s ease-out;
  -webkit-transition: all 1s ease-out;
}

.chart div {
  font: 10px sans-serif;
  /* background-color: steelblue; */
  background-color: #262262;
  text-align: right;
  padding: 3px;
  margin: 5px;
  color: white;
  /* box-shadow: 2px 2px 2px #666; */
}

.bar {
  /* fill: #262262; */
}

canvas {
  display: block;
  width: 100%;
  visibility: hidden;
}

svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

.graph {
  width: 100%;
  position: relative;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css"
  integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
<div id="container" class="container-fluid">
  <div id="jumbotron" class="jumbotron">
    <h3>SVG Chart Example</h3>
    <div class="row">
      <div class="col-lg-6 col-xl-4" style="padding: 10px 10px;">
        <div class="card">
          <h4 class="card-header">Horizontal Bars</h4>
          <div class="card-body">
            <div class="graph">
              <canvas width="600" height="400"></canvas>
              <svg viewBox="0 0 600 400" preserveAspectRatio="xMinYMin">
                <g class="chart_area" transform="translate(50,0)">
                  <g class="chart_data">
                    <rect class="bar" x="0.7142857142857143" y="8" fill="rgb(110, 64, 170)" fill-opacity="0.6"
                      width="107.14285714285714" height="54"></rect>
                    <rect class="bar" x="0.7142857142857143" y="68" fill="rgb(238, 67, 149)" fill-opacity="0.6"
                      width="357.14285714285717" height="54"></rect>
                    <rect class="bar" x="0.7142857142857143" y="128" fill="rgb(255, 140, 56)" fill-opacity="0.6"
                      width="214.28571428571428" height="54"></rect>
                    <rect class="bar" x="0.7142857142857143" y="188" fill="rgb(175, 240, 91)" fill-opacity="0.6"
                      width="500" height="54"></rect>
                    <rect class="bar" x="0.7142857142857143" y="248" fill="rgb(40, 234, 141)" fill-opacity="0.6"
                      width="285.7142857142857" height="54"></rect>
                    <rect class="bar" x="0.7142857142857143" y="308" fill="rgb(47, 150, 224)" fill-opacity="0.6"
                      width="339.2857142857143" height="54"></rect><text class="label" y="35" font-size="12"
                      text-anchor="left" alignment-baseline="middle" x="112.14285714285714">300</text><text
                      class="label" y="95" font-size="12" text-anchor="left" alignment-baseline="middle"
                      x="362.14285714285717">1000</text><text class="label" y="155" font-size="12" text-anchor="left"
                      alignment-baseline="middle" x="219.28571428571428">600</text><text class="label" y="215"
                      font-size="12" text-anchor="left" alignment-baseline="middle" x="505">1400</text><text
                      class="label" y="275" font-size="12" text-anchor="left" alignment-baseline="middle"
                      x="290.7142857142857">800</text><text class="label" y="335" font-size="12" text-anchor="left"
                      alignment-baseline="middle" x="344.2857142857143">950</text>
                  </g>
                  <g class="x axis" font-size="10" transform="translate(0,370)" fill="none" font-family="sans-serif"
                    text-anchor="middle">
                    <path class="domain" stroke="currentColor" d="M0.5,6V0.5H500.5V6"></path>
                    <g class="tick" opacity="1" transform="translate(0.5,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">0</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(71.92857142857143,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">200</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(143.35714285714286,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">400</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(214.78571428571428,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">600</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(286.2142857142857,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">800</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(357.64285714285717,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">1,000</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(429.07142857142856,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">1,200</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(500.5,0)">
                      <line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">1,400</text>
                    </g>
                  </g>
                  <g class="y axis" font-size="10" fill="none" font-family="sans-serif" text-anchor="end">
                    <path class="domain" stroke="currentColor" d="M-6,0.5H0.5V370.5H-6"></path>
                    <g class="tick" opacity="1" transform="translate(0,35.5)">
                      <line stroke="currentColor" x2="-6"></line><text fill="currentColor" x="-9" dy="0.32em">Jan</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(0,95.5)">
                      <line stroke="currentColor" x2="-6"></line><text fill="currentColor" x="-9" dy="0.32em">Feb</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(0,155.5)">
                      <line stroke="currentColor" x2="-6"></line><text fill="currentColor" x="-9" dy="0.32em">Mar</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(0,215.5)">
                      <line stroke="currentColor" x2="-6"></line><text fill="currentColor" x="-9" dy="0.32em">Apr</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(0,275.5)">
                      <line stroke="currentColor" x2="-6"></line><text fill="currentColor" x="-9" dy="0.32em">May</text>
                    </g>
                    <g class="tick" opacity="1" transform="translate(0,335.5)">
                      <line stroke="currentColor" x2="-6"></line><text fill="currentColor" x="-9" dy="0.32em">Jun</text>
                    </g>
                  </g>
                </g>
              </svg></div>
            </h-bars-chart>
            <h5 class="card-title">Special title treatment</h5>
            <p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
          </div>
        </div>
      </div>
    </div>
  </div>

Обратите внимание, что важными строками являются:

CSS (см. Svg заполняет родительский элемент как ширина 100%):

svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

Тогда у меня svg, использующий viewbox, и он всегда заполняет свой родительский div.

Так что вы можете анимировать размер этого родительского элемента (здесь div), и он должен следовать et c. всегда заполняющая ширина 100%

HTML упрощенный пример:

<div class="card-body">
    <div class="graph">
        <canvas width="600" height="400"></canvas>
        <svg viewBox="0 0 600 400" preserveAspectRatio="xMinYMin">
    </div>
</div>

Примечание. Я использую пустой холст только потому, что в IE он не работает должным образом.

CSS:

canvas {
  display: block;
  width: 100%;
  visibility: hidden;
}

Демонстрация:

enter image description here

Обновление - 2-я итерация

После комментариев от ОП я придумал другой пример:

#holder {
  background: #bfbfbf;
  height: 98vh;
  width: 98vw;
}
svg {
  border: 2px solid blue;
  max-height: 98vh;
}
<div id="holder">
  <svg viewBox="0 0 600 400" preserveAspectRatio="xMinYMin">
    <circle r="100" cx="300" cy="200" />
    <circle r="10" cx="10" cy="10" />
    <circle r="10" cx="10" cy="390" />
    <circle r="10" cx="590" cy="10" />
    <circle r="10" cx="590" cy="390" />
    <rect x="0" y="0" width="600" height="400" stroke="black" stroke-width="2" fill="none" />
</div>

Демонстрация здесь:

enter image description here

Примечание: граница элемента svg синяя и граница прямоугольного элемента черная, что соответствует максимальному значению области просмотра, здесь вы можете увидеть, что svg иногда шире, чем область просмотра, но область просмотра svg выровнена / закреплена в верхнем левом углу. Если вы измените соотношение сторон сохранения элемента svg следующим образом:

<svg viewBox="0 0 600 400" preserveAspectRatio="xMaxYMax">

Затем вы увидите окно просмотра, выровненное по правому нижнему углу.

Демонстрация также в https://codepen.io/Alexander9111/pen/rNVpWBp

ОБНОВЛЕНИЕ - 3-я итерация

#holder {
  background: #bfbfbf;
  height: 65vh;
  width: 65vw;
}
svg {
  border: 2px solid blue;
  max-height: 80%;
}
#buttonContainer {
    text-align: center;
}
<div id="holder">
  <svg viewBox="0 0 600 400" preserveAspectRatio="xMinYMin">
    <circle r="100" cx="300" cy="200" />
    <circle r="10" cx="10" cy="10" />
    <circle r="10" cx="10" cy="390" />
    <circle r="10" cx="590" cy="10" />
    <circle r="10" cx="590" cy="390" />
    <rect x="0" y="0" width="600" height="400" stroke="black" stroke-width="2" fill="none" />
    <!--  <foreignObject x="0" y="330" width="600" height="150">
      <div id="buttonContainer">
        <button style="height: 60px">88</button>
        <button style="height: 60px">888</button>
      </div>
    </foreignObject> -->
  <svg/> 
  <div id="buttonContainer">
    <button style="height: 10vh">88</button>
    <button style="height: 10vh">888</button>
  </div>
</div>

Демонстрация и здесь (https://codepen.io/Alexander9111/pen/rNVpWBp):

enter image description here

Обратите внимание, что здесь div с id = "holder" может быть любой ширины и высоты.

Тогда svg css устанавливается на максимальную 100% высоту родительского элемента:

svg {
  border: 2px solid blue;
  max-height: 100%;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...