【问题标题】:SVG viewBox: Exact order of translation and scalingSVG viewBox:平移和缩放的准确顺序
【发布时间】:2018-12-02 16:37:42
【问题描述】:

从技术角度(没有隐喻),我很难准确理解viewBox 上的min-xmin-y 的工作原理。

我花了很多时间在两个有用的资源上:

根据SVG 1.1 specification

‘viewBox’属性的值是一个由四个数字组成的列表 , , and , 用空格和/或分隔 一个逗号,它指定用户空间中的一个矩形,它应该是 映射到给定元素建立的视口边界, 考虑到属性“preserveAspectRatio”。

还有:

‘viewBox’属性的作用是用户代理 自动提供适当的变换矩阵来映射 用户空间中的指定矩形到指定的边界 区域(通常是视口)。

还有:

(注意:在某些情况下,用户代理需要提供翻译 除了规模转换之外的转换。例如,在 最外层的 svg 元素,如果 “viewBox”属性指定非零值 或。)

所以,我的期望是定义 viewBox 与以下内容相同:

  1. 首先缩放视图框,使其填充视口(假设视口和视图框的纵横比相同)
  2. 然后翻译 viewBox,所以根据min-xmin-y viewBox属性放在视口中。

如果我们看一下 Sara 的两个例子,starting here,这似乎不是正在发生的事情。

在她的第一个示例 (<svg width="800" height="600" viewbox="100 100 200 150">...</svg>) 中,它看起来像:

  1. viewBox在视口中根据min-x/min-y放置
  2. viewBox 被缩放到与视口相同的大小
  3. viewBox 原点被翻译(移动)以与视口原点重合

然而,在她的第二个示例 (<svg width="800" height="600" viewbox="-100 -100 400 300">...</svg>) 中,它看起来完全不同:

  1. viewBox 被缩放到与视口相同的大小
  2. viewBox 原点被翻译(移动)不知何故与 viewBox min-x min-y 所指示的方向相反。它与视口原点不一致 - 这与第一个示例不同

因此,我承认我并不完全理解它,因为从技术上讲,它在两种情况下的工作方式应该相同。

最后,在 Sara 的示例中,我不明白为什么蓝色坐标系(用户坐标系)本身不会移动到视口坐标系中的 (100, 100) 或 (-100, -100)。我认为 viewBox 应该 translatescale 用户坐标系?


编辑:

根据this SO answermin-xmin-y 确实遵循我的第一组步骤。 viewBox 原点根据min-xmin-y 放置在视口中,然后进行平移,使其原点位于视口原点之上。然后(之前或之后)缩放以填充视口。

如果这是正确的,我很难理解为什么 Sara 示例中的蓝色用户坐标系并不总是以其原点位于视口原点之上。毕竟viewBox要修改用户坐标系。

【问题讨论】:

  • SVG 2 规范对equivalent transform of a SVG viewport 有正式定义。
  • @ccprog 很有意思,我研究了一下。这是否意味着由 viewBox 创建的新用户坐标系总是紧贴任何后代图形元素(假设图形的 x/y 坐标为 0)?
  • 没有。该算法定义了两个矩形:一个在无限画布中(vb- 值),一个在渲染器/浏览器中(e- 值),然后在渲染矩形中绘制画布中的每个图形内容,使得 矩形“适合”,不考虑内容。涂鸦内容可以在画布中的任何地方,因此可以在渲染器中的任何地方结束。只有在渲染矩形之外的所有内容都被切掉之后,里面的所有内容都是可见的。
  • @ccprog 知道了。让我感到困惑的是 Sara 的蓝色坐标系(请参阅 OP 中的链接)。假设 viewBox 矩形从 (-100, -100) 开始。当该 viewBox 矩形映射到视口(渲染矩形)时,viewBox 矩形的原点被平移(移动)到与视口的原点(渲染矩形)重叠。但是,Sara 的蓝色坐标系与其内容的边缘之间没有空间(参见 viewbox="-100 -100 400 300 示例)。因此,我们的 viewBox 矩形与她的用户坐标系不匹配。
  • @ccprog 更简单的说,将 vb-x/y 设置为 (100, 100) 并不意味着用户坐标系从视口的 100, 100 开始。相反,当 viewBox 矩形映射到视口时,用户坐标系只是沿着(固定在其内容周围)标记,在映射完成后结束。

标签: svg


【解决方案1】:

viewBox坐标原点在x轴上的偏移量min-x=70px

<svg width="400" height="400" viewBox="70px, 0, 400px, 400px">

图中用户坐标原点右移70px,从而使整个矩形可视区域viewBox (400 x 400px)沿横轴右移。

当这种情况发生时,viewBox 下的 SVG 文档片段的图像被捕获,然后带有捕获片段的 viewBox 查看区域与原点 (0,0) 的固定用户视口区域重新对齐在左上角。

图形的坐标重新计算,最后一次向左移动 70px。事实证明,当应用 viewBox 时,在视口的固定查看区域中,SVG 文档的片段已向左移动。

Live Demo

viewBox原点沿两个轴的偏移量

min-x=70px, min-y="70px"

<svg width="400" height="400" viewBox="70px, 70px, 400px, 400px">

为清楚起见,在图片底部添加另一个红色矩形 - 6

在将原点转移到 viewBox 后,一个矩形 400 × 400 px SVG 文档片段的宽度和高度从原点 (70.70) 开始计算到 viewBox 中。

图像捕获发生。接下来,将 viewBox 的原点 (70,70) 与视口的原点 (0,0) 组合在一起。重新计算图形的坐标。

因此,红色矩形 5 和 6 变得完全可见。所有不落入该区域的东西都会被切断。例如彩色圆圈 1,2 和 4 的部分区域。

Live Demo

使用 viewBox 进行缩放

SVG 文档片段的大小取决于纵横比:viewportviewBox

如果viewport /viewBox = 1,则比例为1

如果viewport /viewBox 不等于一,则刻度会按增大或减小的方向变化。

规模增加如何解释下图

一个像素viewBox 拉伸到两个像素viewport

Live Demo

缩小 svg 图片 1:2

<svg width="400" height="400" version="1.1" viewBox="0 0 800 800">

viewport / viewBox = 1/2

viewBox 捕获一个矩形片段800 x 800 px,即 SVG 视口400 x 400 px 的整个范围以及视口右侧和底部的附加400px

viewBox的两个像素被压缩成viewport的一个像素。因此 SVG 图像减少了一半。

Live Demo

【讨论】:

  • 谢谢,亚历山大。很好的说明性解释。我实际上是想了解 viewBox 和视口如何与用户坐标系和视口坐标系相关,并且在与 OP 的 cmets 中的@ccprog 的讨论中深入了解了它。简而言之,用户坐标系最初与视口坐标系相同。然后,在 SVG 画布上绘制一个所谓的 viewBox 矩形,它外面的所有东西都被剪掉了。随后,viewBox 矩形被映射到适合视口矩形。 1/2
  • 2/2 该映射过程导致用户坐标系(以及其中的所有可见图形)被平移(移动)和缩放。重要的是要记住,UCS 实际上不是由 viewBox 定义的。 UCS 仍然与其中的图形相关联,就像它最初一样。由于 viewBox 映射,它与图形一起简单地平移和缩放。为什么了解 UCS 的最终位置很重要?因为,对后代元素的所有其他转换都适用于该 UCS 的副本,而不适用于元素本身(如 HTML 中)。
  • 例如,在您的第一个插图中,UCS 实际上在视口之外(左侧)70 像素处结束。它仍然像在应用 viewBox 之前那样围绕图形。可以说 viewBox 修改 UCS,但它确实没有定义它。
  • 来吧,亚历克斯。敬请期待。
  • Alexandr,我授予您SVG 铜牌,以表彰您为社区所做的出色工作!
【解决方案2】:

  1. 在图片中,一个灰色的矩形是一个无限大的SVG canvas

  2. 绿色矩形是用户在其显示屏上看到的viewport

  3. 黄色矩形是虚拟的viewBox 区域,用户通过该区域查看viewport

viewBox 可以沿着无限svg 画布的坐标轴像正方向x-min&gt; 0 一样移动; y-min&gt; 0 和负方向-x-min; -y-min

图像处理 svg

  • 接下来是 SVG 画布片段的捕获,位于 视图框。
  • 下一步,对齐viewBox的坐标系 与viewport 的坐标系的原点。和 viewBox 图像捕获的片段被传递回 viewport
  • 这里有一个谈判过程和选项:

    1. 如果min-x = 0min-y = 0viewports 的宽度和高度分别等于viewBoxs 的宽度和高度,则片段图像不会移动或缩放。
    2. 如果viewBox 向右移动 - min-x&gt; 0,则图像向左移动。很明显,通过捕获viewport 右侧的图像,然后将其与原点结合,我们将图像向左移动。
    3. 如果viewBox 移动到viewports - min-y&gt; 0 的下方,则图像会上升。

基于此,有想法可以不使用CSS,JavaScript 来实现水平和垂直视差。为此,只需沿 SVG 画布移动 viewBox,如下图所示。点击开始按钮。

<svg version="1.1"   xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink"
   width="600" height="360" viewBox="0 0 600 360"   >
  <title> Explanation horizontal of parallax viewBox </title>
  <desc> animate the horizontal parallax  by modifying a coordinate of the viewBox </desc>
 <defs>
<g id="canvas-svg" stroke-width="2px">	
  <g id="canvas-frame1">
   <rect id="v-port1" x="25" y="200" width="110" height="110" stroke="skyblue"   fill="yellowgreen" /> 
	<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">		
		<rect id="v-port2" x="135" y="200" width="110" height="110" stroke="skyblue"  fill="dodgerblue" /> 
		<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">		
		<rect id="v-port3" x="245" y="200" width="110" height="110" stroke="skyblue"  fill="crimson"  /> 
		<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">		
		<rect id="v-port4" x="355" y="200" width="110" height="110" stroke="skyblue"  fill="orange" /> 
		<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">		
		<rect id="v-port5" x="465" y="200" width="110" height="110" stroke="skyblue" stroke-width="1px" fill="yellow" /> 
		<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">
   <rect  x="25" y="25" width="110" height="110" stroke="skyblue" stroke-width="1px" fill="yellowgreen" /> 
	<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 xlink:href ="#canvas-svg" x="0" y="0"> </use>
  	
<desc> viewBox is moved along canvas SVG</desc>
 <g id="viewBox1">
 <rect id="v-box" x="25" y="200" width="110" height="110" stroke="skyblue" stroke-width="5px" fill="none" />
	 <text id="t-port1" x="45" y="225" style="font-size: 16pt; fill:blue;">viewBox </text>   
	<animateTransform attributeName="transform" type="translate" begin="startButton.click+0.5s" end="stopButton.click" dur="20s" from="0 0" to="440 0" repeatCount="indefinite" restart="whenNotActive" fill="freeze"/>
 </g>	
 

<desc> The image moves to the left viewport</desc>
<use xlink:href ="#canvas-svg" x="0" y="0">
    <animateTransform attributeName="transform" type="translate" begin="startButton.click+0.5s" end="stopButton.click" dur="20s" from="0 -170" to="-440 -170" repeatCount="indefinite" restart="whenNotActive" fill="freeze" />
  </use>

<desc> Grey background image of the canvas SVG</desc>
 <g fill="#E5E5E5" stroke="#E5E5E5">
 <rect  x="135" y="0" width="465" height="195"    />   
  <rect  x="0" y="0" width="25" height="195"    />   
  <rect  x="0" y="0" width="135" height="30"    />   
  <rect  x="25" y="135" width="135" height="60" />   
  <rect  x="0" y="315" width="600" height="85"  />   
  <rect  x="0" y="195" width="25" height="120"  />
  <rect  x="575" y="195" width="25" height="120" />
 </g> 
  
  <g stroke-width="1px" stroke-dasharray = "5 5"> 
  	<line x1="25" y1="140" x2="25" y2="195" stroke="blue"  />
 <line x1="135" y1="140" x2="135" y2="195" stroke="blue" stroke-width="1px"  />
  </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">
	<rect  x="520" y="325" rx="8" ry="8" width="60" height="20" fill="#58AE2A" />
	<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">
			<rect  x="450" y="325" rx="8" ry="8" width="60" height="20" fill="#1E90FF" />
			<text  x="480" y="340" font-size="16" font-weight="bold" font-family="Arial" text-anchor="middle" 
			fill="white" >Stop</text>
		</g>	

</svg>

【讨论】:

  • 伟大的亚历克斯。我会更清楚地了解视口坐标系与用户坐标系(又名当前坐标系/用户空间中的坐标系),以避免未来读者混淆。这两个坐标系确实是唯一的。在用户坐标系上绘制图形。 viewBox 和转换将为元素创建当前坐标系的副本,然后对其进行转换。
  • 否则,很好的解释。我期待着深入了解您的视差代码。我会将您之前的答案标记为已接受,因为我们所有的 cmets 都应该画出完整的画面。
【解决方案3】:

我总是混淆 viewBoxviewport。所以我会尽量避免它。而且我不完全了解您是否要为浏览器或 SVG 设置转换矩阵。所以我也会尽量避免。

viewBox 属性向浏览器提供有关 SVG 图形的大小和坐标原点的信息。它定义了 SVG 的窗口。只有窗口内的部分可见。

那么我们来看一个例子:

<svg width="800" height="600" viewbox="100 100 200 150">

这告诉浏览器它应该在浏览器的坐标系中绘制一个尺寸为 800 像素 x 600 像素的 SVG 图形。因此,在浏览器 DOM 中,SVG 组件将具有该大小。

viewbox 属性告诉浏览器 SVG 图形的相关/可见部分的大小为 200pt x 150pt(在 SVG 坐标系中)。所以浏览器知道它需要应用 400% 的缩放来将 SVG 坐标转换为浏览器坐标。

此外,viewbox 属性告诉浏览器 SVG 坐标系中的点 (100, 100) 将是可见 SVG 图形窗口的左上角。所以浏览器会相应地翻译它。

在 SVG 坐标系中具有较小 xy 值的所有内容都将被剪裁,即不可见,因为它位于窗口之外和浏览器创建的空间之外对于 SVG。同样,SVG 坐标 300 (100 + 200) 右侧和坐标 250 (100 + 150) 以下的所有内容都将在窗口之外并且不可见。

【讨论】:

  • 谢谢你,科多!听起来我的第一组步骤是正确的。它将新的用户坐标系 (UCS) 映射到视口坐标系 (VCS)。换句话说: 1) UCS 根据 min-y/x 放置在视口内,然后平移(包括其包含的图形),使其原点位于 VCS 原点之上。 2) 然后对 UCS 进行缩放,使其填充整个 VCS(假设纵横比相同)。听起来对吗?
  • 我不太了解 UCS 和 VCS。所以我无法判断它是否正确。
猜你喜欢
  • 1970-01-01
  • 2015-01-21
  • 1970-01-01
  • 2012-02-09
  • 2018-12-03
  • 2018-07-05
  • 2020-04-01
  • 1970-01-01
  • 2017-08-30
相关资源
最近更新 更多