Как сделать адаптивный svg?

238
28 ноября 2018, 08:40

Необходимо сделать адаптивный SVG.

Как сделать так, чтобы при изменении размеров изображения (или viewport-а), дочерние элементы SVG перемещались и перестраивались (как в адаптивной вёрстке сайтов).

Как сделать такой SVG?

Answer 1

Можно создать "адаптивное" svg-изображение. Для этого нужно прописать внутри него css-медиазапросы и двигать элементы через transform. Пример изображения:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200px" height="200px" preserveAspectRatio="none"> 
  <style> 
    circle, 
    rect{ 
      stroke-width:2px; 
      fill:none; 
      stroke:orange; 
    } 
    @media (max-width: 120px){ 
      circle{ 
        transform:translateX(50px); 
      } 
      rect{ 
        transform:translate(-60px,80px); 
      } 
    } 
  </style> 
  <circle cx="40" cy="40" r="34" /> 
  <rect x="100" y="10" width="80" height="60" /> 
</svg>

Далее вставляем его на страницу(использовал data:url т.к. SO не хочет принимать svg картинки и ссылки без https, но это тот же самый svg):

$('input').on('input',function(){ 
  $('img').width(this.value); 
})
*{ 
  box-sizing:border-box; 
  vertical-align:top; 
} 
img{ 
  max-width:100%; 
  border:1px solid; 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjAwIDIwMCIgd2lkdGg9IjIwMHB4IiBoZWlnaHQ9IjIwMHB4IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj4NCiAgPHN0eWxlPg0KICAgIGNpcmNsZSwNCiAgICByZWN0ew0KICAgICAgc3Ryb2tlLXdpZHRoOjJweDsNCiAgICAgIGZpbGw6bm9uZTsNCiAgICAgIHN0cm9rZTpvcmFuZ2U7DQogICAgfQ0KICAgIEBtZWRpYSAobWF4LXdpZHRoOiAxMjBweCl7DQogICAgICBjaXJjbGV7DQoJCXRyYW5zZm9ybTp0cmFuc2xhdGVYKDUwcHgpOw0KCSAgfQ0KCSAgcmVjdHsNCgkJdHJhbnNmb3JtOnRyYW5zbGF0ZSgtNjBweCw4MHB4KTsNCgkgIH0NCiAgICB9DQogIDwvc3R5bGU+DQogIDxjaXJjbGUgY3g9IjQwIiBjeT0iNDAiIHI9IjM0IiAvPg0KICA8cmVjdCB4PSIxMDAiIHk9IjEwIiB3aWR0aD0iODAiIGhlaWdodD0iNjAiIC8+DQo8L3N2Zz4=" /> 
 
<label for="controller">Тут задавать ширину для картинки</label> 
<input id="controller" type="number" min="70" max="500" step="5" />

Answer 2

Набросал пример кода на чистом svg и разумеется здесь мы не увидим как меняется расположение svg объектов то привожу пример в: https://codepen.io/topicstarter/pen/oPBzvw?editors=1000 в котором изменение положения объектов произойдёт при разрешении меньше 600px.

что бы увидеть здесь адаптивность svg надо открыть пример кода на всю страницу а сжать сам браузер до менее 600px

img preview :

<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"> 
 
<svg viewbox="0 250 200" id="svg"> 
  <defs> 
    <style> 
      #svg{ 
        width:210px; 
        height:100px; 
      } 
      #rect{ 
        width:50px; 
        height:40px; 
        fill:red; 
        x:20px; 
        y:30px; 
      } 
      #circle{ 
        cx:150px; 
        cy:50px; 
        fill:blue; 
        r:40px; 
      } 
       
      @media (max-width:600px){ 
        #svg{ 
          width:200px; 
          height:140px; 
        } 
        #rect{ 
        width:50px; 
        height:40px; 
        fill:red; 
        x:70px; 
        y:5px; 
          fill:blue; 
      } 
      #circle{ 
        cx:95px; 
        cy:90px; 
        fill:blue; 
        r:40px; 
        fill:red; 
      }     
      } 
    </style> 
  </defs> 
  <rect id="rect"></rect> 
  <circle id="circle"></circle> 
</svg>

Answer 3

Вариант на JS (подобно flex-wrap в CSS):

const $d = document; 
 
let rects = $d.getElementsByTagName('rect'); 
 
const arrangement = e => { 
  let windowWidth = $d.documentElement.clientWidth; 
  let offset_x = 20; 
  let offset_y = 20; 
  let next_offset_y = offset_y; 
  let offset = 20; 
 
  let maxH = 0; 
  let maxW = 0; 
 
  for (let i = 0, len = e.length; i < len; i++) { 
    let w = parseInt(getComputedStyle(e[i]).width); 
    let h = parseInt(getComputedStyle(e[i]).height); 
 
    if (h > maxH) { 
      maxH = h; 
    } 
 
    if (w > maxW) { 
      maxW = w; 
    } 
 
    if (maxW + w + offset_x + offset + offset + offset > windowWidth) { 
      set(e, i, offset_x, offset_y); 
      offset_y += offset + next_offset_y; 
      offset_x = 20; 
      maxH = h; 
    } else { 
      set(e, i, offset_x, offset_y); 
      offset_x += w + offset; 
      next_offset_y = maxH; 
    } 
  } 
}; 
 
const set = (e, i, offset_x, offset_y) => { 
  e[i].setAttribute('x', offset_x); 
  e[i].setAttribute('y', offset_y); 
} 
 
arrangement(rects); 
window.onresize = function(e) { 
  arrangement(rects); 
}
* { 
  margin: 0; 
  padding: 0; 
  box-sizing: border-box; 
  overflow: hidden; 
} 
 
html { 
  font-size: 62.5%; 
} 
 
html, 
body { 
  height: 100%; 
} 
 
.wrapper { 
  width: 100%; 
  height: 100%; 
} 
 
.wrapper svg { 
  width: inherit; 
  height: inherit; 
  background-color: hsl(10, 0%, 70%); 
} 
 
.wrapper svg .main-area rect { 
  width: 25px; 
  height: 25px; 
  stroke: #000; 
  stroke-width: 0.125rem; 
} 
 
.wrapper svg .main-area rect.rect1 { 
  width: 70px; 
  height: 25px; 
} 
 
.wrapper svg .main-area rect.rect2 { 
  width: 25px; 
  height: 70px; 
} 
 
.wrapper svg .main-area rect.rect3 { 
  width: 70px; 
  height: 50px; 
}
<div class="wrapper"> 
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/2000/svg" version="1.1"> 
    <g class="main-area" fill="hsl(50,100%,70%)"> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect class='rect3'/> 
      <rect class='rect3'/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect class='rect1'/> 
      <rect/> 
      <rect class='rect2'/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect class='rect2'/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
      <rect/> 
    </g> 
  </svg> 
</div>

Answer 4

Это можно сделать, используя любую js библиотеку для работы с SVG.

К примеру я взял SVG.js:

Сразу предоставлю рабочий пример, а ниже код примера:

var draw = SVG('banner'); 
 
var bg = draw 
          .rect('100%', '100%') 
          .attr({ fill: '#ffeb3b' }); 
 
var elem1 = draw 
            .ellipse(100, 100) 
            .attr({ fill: '#f06' }); 
             
var elem2 = draw 
            .rect(150, 100) 
            .attr({ fill: '#f06' }); 
 
bunnerUpdate(); 
window.onresize = bunnerUpdate; 
 
function bunnerUpdate() { 
  if (window.innerWidth > 400) { 
    draw.size(400, 200); 
    elem1.move(50, 50); 
    elem2.move(200, 50); 
  } 
  else { 
    draw.size('100%', 350); 
    elem1.move(window.innerWidth / 2 - 50, 50); 
    elem2.move(window.innerWidth / 2 - 75, 200); 
  } 
}
body { 
  margin: 0; 
  padding: 0; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.5/svg.min.js"></script> 
<div id="banner"></div>

READ ALSO
Media запрос под моб устройства

Media запрос под моб устройства

Есть media запрос под оптимизацию картинки на мобустройства

187
Перенос строки в переменной шаблона vue

Перенос строки в переменной шаблона vue

В следующем коде я ожидаю, что при нажатии на кнопку отобразится текст из трех строкОднако, после нажатия появляется одна строка Some text<br>Some...

178