You need to make an adaptive SVG .

How to make it so that when the image is resized (or viewport ), the child elements of the SVG moved and rearranged (as in the adaptive HTML layout).

How to make such a SVG ?

Example diagram

  • This is yes, but the question is not in the implementation of the principle itself, but in how to do it with a single svg file and / or xml file in it. - Nixi1024
  • It was planned to implement the ability to upload a banner (to the site) with a single image (easily downloadable), and in the image to adjust some adaptability ... - Nixi1024
  • one
    It is difficult to call the task impossible ¯_ (ツ) _ / ¯ - Arthur
  • There was hope for svg and its internal markup, but alas, it seems =) - Nixi1024
  • 3
    I'm afraid there is hardly any in svg, for it was not for that purpose - andreymal

5 answers 5

You can create a "adaptive" svg image. To do this, you need to register css-media queries inside it and move elements through transform . Sample image:

 <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> 

Next, paste it into the page (used data: url because SO doesn't want to take svg pictures and links without https, but this is the same 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,+DQogIDxjaXJjbGUgY3g9IjQwIiBjeT0iNDAiIHI9IjM0IiAvPg0KICA8cmVjdCB4PSIxMDAiIHk9IjEwIiB3aWR0aD0iODAiIGhlaWdodD0iNjAiIC8+DQo8L3N2Zz4=" /> <label for="controller">Тут задавать ширину для картинки</label> <input id="controller" type="number" min="70" max="500" step="5" /> 

  • Surely the only way I did it too. But I would like to know if there is any plugin for this at all. - Arthur
  • one
    @Arthur, I don't know. We must wait, maybe someone will offer. It would be interesting if the contours could be changed. - zhurof
  • 50 points deserve this answer, since the very first one, and even 2 such answers in essence are no different. - Arthur
  • @Arthur is different - since there is no jquery in the water of answers ..... without which adaptability will not work, I generally see many people like js solutions ... - user33274
  • @Arthur you didn’t understand me ... you wrote that other answers were similar .. I checked that answer you chose and other answers as well .. and didn’t see any similarities ... this is not sarcasm ... but there are no similarities .. . although I light bulb - user33274

Sketched an example of code on pure svg and of course here we will not see how the location of svg objects changes, I give an example in: https://codepen.io/topicstarter/pen/oPBzvw?editors=1000 in which the object position changes at a resolution less than 600px.

to see the adaptability of svg here, open sample code for the whole page and compress the browser itself to less than 600px

img preview:

enter image description here

 <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> 

    Option on JS (like flex-wrap in 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> 

    • doesn't normally work in Edge or in Firefox - user33274
    • @MaksimLensky, I'll fix it now, I need to render it to the width & height attributes - Arthur
    • by the way that example about a fisheye - look I threw off the link there - user33274 September
    • Arthur now does not work at all ... ie, the blocks appeared, but they stand in a row ... although the Edge supports flex and also flex-wrap .... probably this is a feature of the Edge - user33274 September
    • @MaksimLensky, you need to add height and width for elements separately, for some reason css properties are not supported in svg)) - Arthur

    This can be done using any js library for working with SVG.

    For example, I took SVG.js :

    Immediately provide a working example , and below is the code of the example:

     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> 

    • > Uncaught ReferenceError: SVG is not defined - ThisMan
    • That's the link to the working example, and you immediately minus one. I fixed the code so that I could execute it - zzzoryn
    • I did not put a minus, and if I didn’t point this out to you, you wouldn’t change it, people would run your example with an error - ThisMan
    • 3
      By the way, according to the rules, the link is not the answer - ThisMan

    It is surprising that all the answers written before me do not correspond to the condition of the task. Pay attention to the fact that you need to track exactly the image size:

    How to make the image resize (or viewport)

    And all 4 answers written to me are not taken into account at all and are tracked as one window resizing. It is also not clear why, after 4 months, nobody paid attention to this ?! It reminded me of a funny old joke about developers .

    But if someone still needs to respond to a window change, then study the first 4 answers and the article by reference (it's about CSS media requests such as @media (max-width:600px) ).

    Correct solution

    If we translate the word “adaptive” into Russian, we will get two words “adaptable” and “adaptable”. The ending "-th" always implies that the object of the action itself (reduction-end at the end) will adapt. But “adaptable” means that the subject of the action will be adapted by someone (something). For example, a higher window will adjust it - that is, someone (something), but not himself.

    A case where it is “adaptable”

    In this case, this adaptive will be our SVG object, which will track changes in itself and adapt itself . To do this, we are in the SVG object itself (although it is possible outside) through JavaScript using the window.setTimeout function every 50 милисекунд monitor image resizing by comparing the width property svg.getAttribute('width') . And if this value changes, then we change the location of the rectangle by changing the values ​​of its properties (or so-called attributes) x and y with the help of the function rect.setAttribute(); . We can also instead change the CSS property transform:translate(...px, ...px); , the reference to which in JavaScript looks like this:

     rect.style.transform = 'translate(...px, ...px)'; 

    But it makes no sense to give an example of transform , since This has already been done in the first two examples.

    It is also important to note that svg.setAttribute('width', ...); image using svg.setAttribute('width', ...); we must also change the size of its viewBox , for otherwise there will be a discrepancy and the image will be distorted.

    So, the first example / case:

     var svg = document.querySelector('#svg'); function changeWidth(obj) { var width = +obj.options[obj.selectedIndex].text; svg.setAttribute('width', width); svg.setAttribute('viewBox', '0,0,' + width + ',320'); } 
     body{background:#000} 
     <font color="white">Ширина <s>окна</s> SVG изображения: <select onchange="changeWidth(this)"> <option>320</option> <option>210</option> </select> px.</font><br><br> <svg id="svg" viewBox="0 0 320 320" width="320" height="320" style="background:#fff"> <g fill="none" stroke="#f93" stroke-width="3"> <polygon points="60,10,110,60,60,110,10,60"/> <rect id="rect" x="130" y="10" width="180" height="100"/> </g> <script type="text/javascript"> var svg = document.querySelector('#svg'), rect = svg.getElementsByTagName('rect')[0]; (function checkWidth() { var w = +svg.getAttribute('width'); //+ значит перевод из string в integer if(w < 211) { rect.setAttribute('x', 10); rect.setAttribute('y', 120) } else { rect.setAttribute('x', 130); rect.setAttribute('y', 10) } window.setTimeout(checkWidth, 50) })() </script> </svg> 

    A case where it is “adaptable”

    This case is more complicated for this task, since SVG is a vector image and there must be certain coordinates for each point. And although the decision, when the properties of the rectangle are changed from the outside, one could, with a stretch, be called “adjustable”, but this is still wrong, because we change its location properties.

    So, see what I mean by “adjustable” SVG (an example that also solves the problem):

     var svg = document.querySelector('#svg'); function changeWidth(obj) { var size = obj.options[obj.selectedIndex].text.split(' x '); svg.setAttribute('width', size[0]); svg.setAttribute('height', size[1]); svg.setAttribute('viewBox', '0,0,' + size[0] + ','+size[1]); } 
     body{background:#000} 
     <font color="white">Размер SVG изображения: <select onchange="changeWidth(this)"> <option value="">320 x 120</option> <option value="">200 x 240</option> </select> px.</font><br><br> <svg id="svg" viewBox="0 0 320 120" width="320" height="120" style="background:#fff"> <svg preserveAspectRatio="xMinYMin meet" width="120" height="120" viewBox="0 0 120 120"> <polygon points="60,10,110,60,60,110,10,60" fill="none" stroke="#f93" stroke-width="3"/> </svg> <svg preserveAspectRatio="xMaxYMax meet" width="100%" height="100%" viewBox="0 0 200 120"> <rect x="10" y="10" width="180" height="100" fill="none" stroke="#f93" stroke-width="3"/> </svg> </svg> 

    Notice how the preserveAspectRatio properties are displayed in the internal SVG images, which help us to arrange our images in this way. Also important are the width and height properties, which are set to 100% in the second inner SVG. And when used together, they act in such a way that our second internal SVG is built either to the far right or to the very bottom.

    This is what I call an “adjustable” SVG here, because Nowhere in the code does not change anything (not specified) depending on the size of the image as in the case of the "adapting" SVG. All we do is only resize the image and nothing more .