Как взаимодействуют между собой viewport и viewBox

Alexandr_TT

Этот вопрос вызван следующей важной темой, затронутой в хорошем вопросе почти полгода назад. Рисует ли браузер то, что находится за областью видимости SVG холста?

Взаимодействие между собой viewport и viewBox довольно сложный вопрос, но это фундамент для понимания и успешного использования SVG в вёрстке и анимации веб страничек. Я изучил много зарубежных интернет ресурсов на эту тему и конечно прежде всего спецификацию w3С, но я не носитель английского языка, да и изложено там, на мой взгляд несколько запутанно. Ниже приведу на примерах, как я понял этот процесс взаимодействия viewport и viewBox. viewport - это область видимости, часть бесконечного SVG холста, которую видит пользователь на дисплее своего гаджета. Размеры viewport, допустим; - width="1000" height="600" задает автор файла SVG с началом координат в левом верхнем углу.

viewBox - в качестве примера - viewBox="0 0 500 300" - это виртуальная, прямоугольная область просмотра, которую пользователь не видит, но от которой зависит какая часть бесконечного полотна SVG будет показана на дисплее пользователя. Кроме того последние два атрибута viewBox отвечают за масштабирование изображения. Подробнее здесь.

Интересен сам процесс выборки с помощью viewBox фрагмента полотна SVG, последующего преобразования фрагмента и рендеринг его на дисплее пользователя. Ниже поясняющий рисунок.

<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="500" height="500" id="svg5452" version="1">
  <defs>
    <filter id="filter5435-6" color-interpolation-filters="sRGB">
      
    </filter>
  </defs>
   <g id="layer1" transform="translate(0,-552.362)">
    
    
    
    
    
    
    
    
    
    
    
    
    
    <g id="g4066" transform="matrix(0.38032348,0,0,0.37761044,232.98885,603.74368)">
      
      <g id="g4055">
        <g id="g3872" transform="matrix(0.97932405,0.21454349,-0.03440915,0.93598881,14.984082,-76.741419)">
          <g transform="matrix(0.96020224,0,0,1.0308731,23.199143,-22.772714)" id="g3863">
            
            <g id="g3856">
              
              
              <g id="g3852">
                <text space="preserve" x="245" y="256" id="text3848" transform="scale(2,2)" style="-inkscape-font-specification:Rosewood Std Regular;fill:#000;font-family:Rosewood Std Regular;font-size:64;letter-spacing:0;line-height:125;word-spacing:0">
                  
                </text>
              </g>
            </g>
          </g>
        </g>
      </g>
    </g>
    <text space="preserve" x="178" y="1320" id="text3906" transform="matrix(1.3253407,-0.71279682,0.02282854,0.74224528,0,0)" style="-inkscape-font-specification:Minion Pro Cond Bold;fill:#000;font-family:Minion Pro Cond;font-size:32;font-weight:bold;letter-spacing:0;line-height:125;word-spacing:0">
      <tspan id="tspan3908" x="178" y="1320">
        viewBox
      </tspan>
    </text>
    
    <text space="preserve" x="821" y="-182" id="text4079" transform="matrix(0.24480584,0.87602428,-0.94306552,0.71016161,0,0)" style="-inkscape-font-specification:Minion Pro Cond Bold;fill:#000;font-family:Minion Pro Cond;font-size:30.4;font-weight:bold;letter-spacing:0;line-height:125;word-spacing:0">
      <tspan id="tspan4081" x="821" y="-182">
        8
      </tspan>
    </text>
    
    
    <text transform="matrix(0.46123189,0.67913613,-1.0097074,0.6813736,0,0)" id="text5290" y="364" x="1089" space="preserve" style="-inkscape-font-specification:Minion Pro Cond Bold;fill:#000;font-family:Minion Pro Cond;font-size:30.8;font-weight:bold;letter-spacing:0;line-height:125;word-spacing:0">
      <tspan y="364" x="1089" id="tspan5292">
        8
      </tspan>
    </text>
    <text space="preserve" x="164" y="622" id="text5294" transform="matrix(0.83410914,-0.33044431,-0.00717685,1.2017271,0,0)" style="-inkscape-font-specification:Minion Pro Cond Bold;fill:#000;font-family:Minion Pro Cond;font-size:28.4;font-weight:bold;letter-spacing:0;line-height:125;word-spacing:0">
      <tspan id="tspan5296" x="164" y="622">
        (0,0)
      </tspan>
    </text>
    
    
    
    <g id="g4487" transform="matrix(1.4874974,0,0,1.2712695,-0.61034849,-201.51524)">
      
      
    </g>
    
    
    
    <text space="preserve" x="-22" y="784" id="text4596" transform="matrix(0.94153265,-0.49180502,0.20084042,0.95719004,0,0)" style="-inkscape-font-specification:Minion Pro Cond Bold;fill:#000;font-family:Minion Pro Cond;font-size:32;font-weight:bold;letter-spacing:0;line-height:125;word-spacing:0">
      <tspan id="tspan4598" x="-32" y="754">
        viewport
      </tspan>
    </text>
  </g>
</svg>

viewBox можно разместить в любом месте SVG полотна. Его положение зависит от первых двух атрибутов: min-x, min-y.

Далее идет захват фрагмента SVG полотна, расположенного под viewBoxом.

На следующем этапе система координат viewBox совмещается с началом системы координат viewport. И фрагмент захваченного viewBoxом изображения передается обратно во viewport. Идет процесс согласования и тут возможны варианты:

  1. Если min-x = 0 и min-y = 0, ширина и высота viewportа равны соответственно ширине и высоте viewBoxа, то изображение фрагмента не сдвигается и не масштабируется.

  2. Если viewBox сдвинут вправо - min-x > 0 изображение сдвигается влево. Понятно, что захватив изображение правее viewport и потом совмещая с началом координат мы тем самым сдвигаем изображение влево.

  3. Если viewBox сдвинут ниже viewporta - min-y > 0 изображение поднимется вверх.

Масштабирование зависит от соотношения стророн viewporta и viewBoxа

  1. При условии, что соотношение сторон viewport / viewBox > 1 - происходит пропорциональное увеличение масштаба исходного фрагмента SVG .

Допустим окно viewBox в два раза меньше viewport. Поэтому при обратном совмещении viewBox с viewport один пиксель из viewBox растягивается до 2-х пикселей viewport.

  1. При viewport / viewBox < 1 - происходит уменьшение изображения.

    viewBox, который больше viewport захватывает весь viewport и соседние с ним участки полотна SVG, а затем всё это уплотняет обратно во viewport.

    Повторяюсь, пользователь видит на дисплее изображение, которое после всех преобразований попало во viewport. Можно вывести правило, которое легко запомнить:

    У viewBox всё наоборот.

    Поэтому, если вправо перемещаем viewBox - min-x > 0, то изображение сдвигается влево.

    Если увеличиваем viewBox, то изображение уменьшается.

    На основе этого приходят мысли, что можно реализовать горизонтальный и вертикальный параллакс, не используя CSS, JavaScript. Для этого надо просто перемещать viewBox вдоль полотна SVG, как показано на рисунке ниже. Нажмите кнопку Start.

    Верхнее окно это viewport, который видит пользователь, а цветная полоса - это полотно SVG. Реализация горизонтального параллакса - здесь

    <!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
    <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" width="600" height="360" viewBox="0 0 600 360">
      <title> Explanation horizontal of parallax viewBox </title>
      <desc> animate the horizontal parallax svg-art.ru by modifying a coordinate of the viewBox </desc>
     <defs>
    <g id="canvas-svg" stroke-width="2px">	
      <g id="canvas-frame1">
        
    	<text id="t-port1" x="75" y="255" style="font-size: 16pt;">1 </text>
    	<text x="26" y="303"> 0 </text>
     </g>		    
      <g id="canvas-frame2">		
    		 
    		<text id="t-port2" x="185" y="255" style="font-size: 16pt;">2 </text>
    		<text x="136" y="303"> 1168 </text>
     </g>		  
      <g id="canvas-frame3">		
    		 
    		<text id="t-port3" x="295" y="255" style="font-size: 16pt;">3 </text>
    		<text x="246" y="303"> 2336 </text>
      </g>
          <g id="canvas-frame4">		
    		 
    		<text id="t-port4" x="405" y="255" style="font-size: 16pt;">4 </text>
    		<text x="356" y="303"> 3504 </text>
         </g>
           <g id="canvas-frame5">		
    		 
    		<text id="t-port5" x="515" y="255" style="font-size: 16pt;">5 </text>
    		<text x="466" y="303"> 4672 </text>
           </g>   
     </g>
    	
     </defs>
     
      <g id="first-rect">
        
    	<text x="75" y="85" style="font-size: 16pt;">1 </text>
    	<text x="26" y="135"> 0 </text>
     </g>		    
    
    
      <desc>The SVG canvas is infinite in size. In our example, user a viewport of SVG is in the leftmost position.</desc>  
    <use href="#canvas-svg" x="0" y="0"> </use>
      	
    <desc> viewBox is moved along canvas SVG</desc>
     <g id="viewBox1">
     
    	 <text id="t-port1" x="45" y="225" style="font-size: 16pt; fill:blue;">viewBox </text>   
    	
     </g>	
     
    
    <desc> The image moves to the left viewport</desc>
    <use href="#canvas-svg" x="0" y="0">
        
      </use>
    
    <desc> Grey background image of the canvas SVG</desc>
     <g fill="#E5E5E5" stroke="#E5E5E5">
        
         
         
         
         
      
      
     </g> 
      
      <g stroke-width="1px" stroke-dasharray="5 5"> 
      	
     
      </g>		
       <g style="font-size: 16pt; fill:blue;">
    	<text x="45" y="170"> viewport </text> 
    	 <text x="15" y="20" style="font-size: 14pt;"> display the user's  </text>
    	    <text x="230" y="90" style="font-size: 40pt; fill:#1E90FF"> canvas SVG </text> 
       </g> 
    
    <g id="startButton">
    	
    	<text x="550" y="340" font-size="16" font-weight="bold" font-family="Arial" text-anchor="middle" fill="white">Start</text>
    </g>
            <g id="stopButton">
    			
    			<text x="480" y="340" font-size="16" font-weight="bold" font-family="Arial" text-anchor="middle" fill="white">Stop</text>
    		</g>	
    
    </svg>

    Практический пример взаимодействия viewport и viewBox в переведенной статье с enSO на нашем сайте.

2 ответа

Alexandr_TT

Выше были примеры взаимодействия viewport и viewBox.

А что происходит, когда в SVG документе есть только viewport, а viewBox не прописан?

svg version="1.1" width="1280" height="1024" 1. Масштабирования изображения не будет происходить при изменении размеров родительского контейнера или размеров окна браузера.

Команда - preserveAspectRatio тоже не будет работать. Кстати это единственный способ избавиться от неё, так как, если даже её не написать в шапке SVG файла, то по умолчанию она будет иметь значения xMid yMid

  1. Второй вариант. В шапке SVG файла присутствует только viewBox

svg version="1.1" viewBox = "0 0 640 516" В этом случае viewport принимает значения по умолчанию - 100% ширины и высоты окна браузера. Масштабирование становится возможным. Масштаб изображения высчитывается из соотношения соответственно высоты окна браузера и viewBox. То есть при уменьшении viewBox мы увеличиваем изображение на дисплее, так как при этом не изменяем размер дисплея. И наоборот,- уменьшая окно браузера мы уменьшаем размер изображения.

Команда - preserveAspectRatio становится активной. Ниже три примера, как она оказывает влияние на позиционирование изображения при изменении своих атрибутов.

preserveAspectRatio="xMinYMin meet"

preserveAspectRatio="xMidYMid meet"

preserveAspectRatio1="xMaxYMax meet"


Alexandr_TT

1. Пример использования viewport и viewBox для скрытия, показа изображения с одновременным масштабированием

  • Из описания выше следует, что при соотношении параметров viewport / viewBox < 1 происходит уменьшение изображения относительно окна браузера.

В примере: 300 / 2700 = 1/9

<svg id="svg1" width="300" height="300" viewBox="200 200 2700 2700">    
</svg>

То есть изображение будет занимать одну девятую часть от видимого полотна svg (300х300) Так как начало координат у svg - верхний левый угол, то там и будет находиться уменьшенное изображение.

  • Если уменьшать viewBox от 2700 до 300, то есть до размера viewport изображение будет увеличиваться от одной девятой до полного размера.

При этом viewport / viewBox = 1

Анимацию изменения размера viewBox выполняет команда:

<animate attributename="viewBox" begin="svg1.click+0.25s" values="200 200 2700 2700;0 0 300 300" dur="1.5s" fill="freeze" restart="whenNotActive"> 
</animate>

Ниже пример кода целиком. Для начала анимации кликните в любом месте прямоугольника.

body {
padding:0;
margin:0;
}
svg text {
fill:white;
}
<svg id="svg1" width="300" height="300" viewBox="200 200 2700 2700" style="border:1px solid grey;">

 











<text x="35" y="55" font-size="24"> R1 </text>
<text x="135" y="55" font-size="24"> R2 </text>
<text x="235" y="55" font-size="24"> R3</text>

<text x="35" y="155" font-size="24" style="fill:grey;"> R4 </text>
<text x="135" y="155" font-size="24"> R5 </text>
<text x="235" y="155" font-size="24"> R6 </text> 

<text x="35" y="255" font-size="24"> R7 </text>
<text x="135" y="255" font-size="24" style="fill:grey;"> R8 </text>
<text x="235" y="255" font-size="24"> R9 </text> 



 </svg>

2. Пример использования анимации растровых изображений

Добавим к svg фигурам *.png иконки:


                

Ниже весь код целиком:

body {
padding:0;
margin:0;
}
svg {
background: linear-gradient(to right, white 33%,  skyblue 90%);
}
svg rect {
fill:none;
stroke:#c3c3c3;
stroke-width:0.5;
}
<svg id="svg1" width="300" height="300" viewBox="200 200 2700 2700" style="border:0px solid grey;">

 









 

 
 
   

 
 
    

 
 
   


 </svg>

licensed under cc by-sa 3.0 with attribution.