6

svg的坐标系

svg采用类似canvas的坐标系统,以左上角为0,0原点,从左往右计算x值,从上到下计算y值。这个和HTML中标示位置的方法相同。
我们来看一个例子

<svg width="400" height="200" viewBox="0 0 200 50" style="border:1px solid greenyellow">
   <rect with="150" height="150" x="10" y="10" fill="#008d46"/>
</svg>

svg中的“像素pixels”

在绝大多数情况下SVG文档中的一个像素对应输出设备上的一个像素(例如显示屏幕上)
因为SVG是一个可无极缩放的矢量图形绘制标记语言。其做大的特点是在任意大小的显示设备上都可以不失精度的表现原始图形。SVG就有关于absolute unit和user unit的说法-在定义大小是不明确指定单位
没有特别的说明,一个user unit等于一个屏幕单位(一个像素),可以给通过设置viewBox属性的方法改变默认的情况,可以放大或缩小对应的值
例如
<svg width="100" height="100">
定义了一个100px_*_100px的svg canvas,这里一个user unit等于一个屏幕像素
我们再来看一个例子

<svg width="200" height="200" viewBox="0 0 100 100">

这里的svg在宽度上一个userunit为2个屏幕像素,高度上一个userunit为2个屏幕像素。也就是说整个以viewBox为参考系定义的svg图像需在宽度和高度上各放大2倍显示到svg viewport上

viewBox与viewPort关系

那么这里就有一个原始视图窗口和当前需要显示的目标视图窗口之间的映射关系的问题。这里的原始视图就是viewBox所设置的视图窗口,而目标视图窗口就是viewport。一个原则原始视图要求铺满viewport的大小,viewBox属性值格式为 (min-x,min-y,width,height),需要说明的时width和height并不是表示viewBox的实际的宽度和高度,这个是指在min-x和min-y都为0的情况西下成立,viewBox的实际独宽度为minx-x+width,高度为min-y+height

<svg width="400" height="200" viewBox="0 0 200 100" style="border:1px solid greenyellow">
   <rect with="100" height="100" x="0" y="0" fill="#008d46"/>
</svg>

以上面的svg为例

  • viewport的大小就是400200的矩形区域,具体的坐标位置以svg元素所在的页面的的位置。以HTML页面为参考系

  • viewBox的大小就是200500的矩形区域,位置坐标为(0,0)以viewBox为参考系

  • 因为viewBox的宽高比为2,viewport的宽高比也为2,那么放大2倍后viewBox刚好铺满viewport:),
    (我们就可以认为0.5个userunit对应一个屏幕像素)

  • 以viewBox为参考系的svg下的graphic elements也等同的放大2倍,也就是现在看到的绿色的矩形大小为200_*_200
    图片描述

但是实际很多SVG的viewBox的宽高比和viewport的宽高比是不同有差异的。那么在不同的情况下viewBox和viewport怎么处理?viewBox的4个参数到底有什么作用?viewBox比viewport区域大可以吗?SVG是怎么处理的呢?可就要和下面的preserveAspectRatio属性值相关了

preserveAspectRatio的属性

preserveAspetRation属性是和viewBox属性配合使用的。viewBox属性值可以指明graphic element是否可以等比例缩放(宽高比相同的情况下)以扩展到viewport指定的大小区域中。
preserveAspetRation属性指出了如何缩放及如果对齐viewBox到viewport上

如果没有指定viewBox属性,那么此属性是会被忽略的
先在这里声明一点,它的默认值为xMidYMid meet。

那么xMid、YMid、meet都是些什么样的值,干嘛用的?就要从preserveAspetRation的可选值讲起
preserveAspectRation的值为[defer]<align>[meetOrSlice]-以可选的defer开始+<align>+可选的meet或者Slice。

  • defer这个值只在image元素中有效表示只在img显示出来后才计算使用preserveAspectRation属性,对于其它元素就忽略这个值了

  • 在讲<align>之前,有必要说明下[meetOrSlice]候选值,默认值为meet

    • meet viewBox保持缩放比;整个viewBox在viewport中可见;在满足前2个约束条件的基础上,尽可能的放大viewBox。当viewport的宽高比和viewBox的宽高比不匹配时,取宽高缩放比中较小的那个注意这里说的是viewBox的缩放而不是图形

    • slice 修剪 viewBox保持缩放比;整个viewport区域被viewBox覆盖;在满足签个约束条件的基础上,尽可能的缩小viewBox。当viewport的宽高比和viewBox的宽高比不匹配时,取宽高缩放比中较大的那个

  • <align>在viewBox的宽高比和viewport的宽高比不匹配的情况,它的值必须是以下其中一个

    • none

      不强制进行等比例缩放,那么viewBox旗下的graphicelements就以viewBox和viewport实际的宽高比来缩放图形,尽量把宽度和高度扩展到这个viewport上。一个后果就是图形比例会失真
      
      <svg width="400" height="200" viewBox="0 0 200 50" preserveAspectRation="none" style="border:1px solid greenyellow">
      <rect with="100" height="100" x="0" y="0" fill="#008d46"/>
      </svg>

      clipboard.png

我们可以看到rect的实际的宽度变为200,而高度边变为400。这个和viewBox和viewPort的宽高比是相符合的,宽度放大2被,高度放大4倍
我们把viewBox和viewport的宽高换一下

<svg width="200" height="50"  preserveAspectRatio="none" viewBox="0 0 400 200" style="border:1px solid greenyellow">
<rect width="100" height="100" x="0" y="0" fill="#008d46" />
</svg>

那么看到的结果为
clipboard.png

rect的宽度缩小2倍,宽度缩小4倍,原始的正方形变成了长方形

  • xMinYMin 强制等比例缩放,viewBox的min-x坐标值和viewport的最左边的x对齐。viewBox的min-y坐标和viewport的最左边的y坐标对齐,
    同样以上面svg为例,黑色边框矩形表示viewport,粉色矩形表示viewBox,淡绿色矩形表示rect图形元素。必须先按meet或slice计算确定放大或缩小比例,viewBox中的数值按缩放后再执行对齐

图片描述

  • xMidYMin 强制等比例缩放,viewBox的x中点和viewport的x中点对齐,viewBox的min-y和viewport的最上边y值对齐。

图片描述

  • xMaxYMin 强制等比例缩放,viewBox的min-x+width和viewport最右边x值对齐,viewBox的min-y和viewport的最上边y值对齐

    图片描述

  • xMinYMid 强制等比例缩放,viewBox的min-x和viewport的最左边x对齐,viewBox的y的中点和viewport的中点对齐

    图片描述

  • xMidYMid 强制等比例缩放,viewBox的x中点和viewport的x中点对齐,viewBox的y中点和viewport的中点对齐。是默认配置。meet值决定了svg的缩放比为0.25(宽度上的缩放比为0.5,高度山的缩放比为0.25)。viewBox的x中点坐标为(50+400*0.5)*0.25=62.5,viewport的x中点坐标为200*0.5=100;viewBox的y中点坐标为(50+200*0.5)*0.25=37.5,viewport的y中点坐标为50*0.5=25;那么viewBox的x坐标为100-62.5=37.5,y坐标为25-37.5=-12.5,viewBox的宽度为(50+400)*0.25=112.5 高度为(50+200)*0.25=62.5。其它属性值也使类似,你可以运行附上的代码来查看效果。
    最终效果如下:

    图片描述

  • xMaxYMid 强制等比例缩放,viewBox的min-x+width和viewport的最右边的x对齐,viewBox的y中点和viewport的y中点对齐

    图片描述

  • xMinYMax 强制等比例缩放,viewBox的min-x和viewport最左边的x对齐,viewBox的min-y+height和viewport最下边的y值对齐

    图片描述

  • xMidYMax 强制等比例缩放,viewBox的x中点和viewport的x中点对齐,viewBox的min-y+height和viewport最下边的y值对齐
    图片描述

  • xMaxYMax 强制等比例缩放,viewBox的min-x+width和viewport的最右边x对齐,viewBox的min-y+height和viewport最下边的y值对齐

clipboard.png

附上代码

能看到完成的定位过程

<!DOCTYPE html>
<html>
<head>
    <title>HTML/SVG Example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script src="script/jquery-2.1.3.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            $("button.ts").click(function() {
                console.log('trigger select');
                var XaspectRatio=$('input[name=VP_width]').val()/$('input[name=VB_width]').val();
                var YaspectRatio=$('input[name=VP_height]').val()/$('input[name=VB_height]').val();


                var aspectRatio=Math.min(XaspectRatio,YaspectRatio);
                var preserveAspectRatioSetting=$('input[name=xAlignType]:checked').val()+''+$('input[name=YAlignType]:checked').val();

                var meetOrSlice=$('input[name=meetOrSlice]:checked').val();
                if(meetOrSlice==='meet'){
                    aspectRatio=Math.min(XaspectRatio,YaspectRatio);
                }else{
                    aspectRatio=Math.max(XaspectRatio,YaspectRatio);
                }
                console.log("meetOrSlice:"+meetOrSlice);
                var viewBoxX=$('input[name=VB_x]').val()*aspectRatio;
                var viewBoxY=$('input[name=VB_y]').val()*aspectRatio;

                var viewBoxWidth=$('input[name=VB_width]').val()*aspectRatio;
                var viewBoxHeight=$('input[name=VB_height]').val()*aspectRatio;

                var viewBoxxMid=viewBoxX+(viewBoxWidth)*0.5;
                var viewBoxYMid=viewBoxY+(viewBoxHeight)*0.5;
                var viewBoxxMax=(viewBoxX+viewBoxWidth);
                var viewBoxYMax=(viewBoxY+viewBoxHeight);

                var viewportX=0;
                var viewportY=0;
                var viewportWidth=$('input[name=VP_width]').val()*1;
                var viewportHeight=$('input[name=VP_height]').val()*1;
                var viewportxMid=viewportX+(viewportWidth)*0.5;
                var viewportYMid=viewportY+(viewportHeight)*0.5;
                var viewportxMax=(viewportX+viewportWidth)*1;
                var viewportYMax=(viewportY+viewportHeight)*1;



                var rectWidth=$('input[name=rect_width]').val()*aspectRatio;
                var rectHeight=$('input[name=rect_height]').val()*aspectRatio;
                var rectX=$('input[name=rect_x]').val()*aspectRatio;
                var rectY=$('input[name=rect_y]').val()*aspectRatio;


                console.log(viewBoxX+' '+viewBoxWidth+' '+viewBoxYMid);
                console.log(preserveAspectRatioSetting);
                console.log(viewportYMax+' '+viewBoxYMax);

                switch(preserveAspectRatioSetting){
                    case 'xMinYMin':
                        viewBoxX=viewportX-viewBoxX;
                        viewBoxY=viewportY-viewBoxY;
                        break;
                    case 'xMinYMid':
                        viewBoxX=viewportX-viewBoxX;
                        viewBoxY=viewportYMid-viewBoxYMid;
                        break;
                    case 'xMinYMax':
                        viewBoxX=viewportX-viewBoxX;
                        viewBoxY=viewportYMax-viewBoxYMax;

                        break;
                    case 'xMidYMin':
                        viewBoxX=viewportxMid-viewBoxxMid;
                        viewBoxY=viewportY-viewBoxY;

                        break;
                    case 'xMidYMid':
                        viewBoxX=viewportxMid-viewBoxxMid;
                        viewBoxY=viewportYMid-viewBoxYMid;
                        break;
                    case 'xMidYMax':
                        viewBoxX=viewportxMid-viewBoxxMid;
                        viewBoxY=viewportYMax-viewBoxYMax;
                        break;
                    case 'xMaxYMin':
                        viewBoxX=viewportxMax-viewBoxxMax;
                        viewBoxY=viewportY-viewBoxY;
                        break;
                    case 'xMaxYMid':
                        viewBoxX=viewportxMax-viewBoxxMax;
                        viewBoxY=viewportYMid-viewBoxYMid;
                        break;
                    case 'xMaxYMax':
                        viewBoxX=viewportxMax-viewBoxxMax;
                        viewBoxY=viewportYMax-viewBoxYMax;
                        break;
                    default:
                        viewBoxX=viewportX-viewBoxX;
                        viewBoxY=viewportY-viewBoxY;
                        //xMidYMid
                }

                $('div#svg').css({
                    width:$('input[name=VP_width]').val(),
                    height:$('input[name=VP_height]').val(),
                    "margin-left":"0px",
                    "margin-right":"0px"
                });

                $('div#viewBox').css({
                    width:($('input[name=VB_width]').val()*1+$('input[name=VB_x]').val()*1)*aspectRatio,
                    height:($('input[name=VB_height]').val()*1+$('input[name=VB_y]').val()*1)*aspectRatio,
                    "margin-left":(viewBoxX*1)+"px",
                    "margin-top":(viewBoxY*1)+"px"
                });
                $('div#rect').css({
                    width:$('input[name=rect_width]').val()*aspectRatio,
                    height:$('input[name=rect_height]').val()*aspectRatio,
                    "margin-left":(rectX*1)+"px",
                    "margin-top":(rectY*1)+"px"
                });
            });

        });

    </script>
</head>
<body>
<br>
<br>
<div style="margin-left:0px;width:300;height:200px;border:1px solid #cccccc;padding:30px;">
<div id="svg" style="margin-left:0px;margin-top:0px;width:200px;height:50px;border:1px solid black">
    <div id="viewBox" style="margin-left:-12.5px;margin-top:-12.5px;width:100px;height:50px;background-color:red;opacity:0.3;border:0px dashed red">
        <div id="rect" style="margin-left:0px;margin-top:0px;width:25px;height:25px;
        background-color:#008d46;opacity:1.0;border:0px dashed red"></div>
        </div>
</div>
    </div>

<div style="margin-top:10px;">
    <div>
        <label>xMin:<input type="radio" name="xAlignType" value="xMin" ></label>
        <label>xMid(default):<input type="radio" name="xAlignType" value="xMid" checked></label>
        <label>xMax:<input type="radio" name="xAlignType" value="xMax"></label>
    </div>
    <div>
        <label>YMin:<input type="radio" name="YAlignType" value="YMin"></label>
        <label>YMid(default):<input type="radio" name="YAlignType" value="YMid" checked></label>
        <label>YMax:<input type="radio" name="YAlignType" value="YMax"></label>
    </div>
    <div>
        <label>meet(default):<input type="radio" name="meetOrSlice" value="meet" checked></label>
        <label>slice:<input type="radio" name="meetOrSlice" value="slice"></label>
    </div>
    <div>
        <label>viewPort width:<input type="text" name="VP_width" value="200" style="width: 50px;"></label>
        <label> height:<input type="text" name="VP_height" value="50" style="width: 50px;"></label>
    </div>
    <div>
        <label>viewBox width:<input type="text" name="VB_width" value="400" style="width: 50px;"></label>
        <label> height:<input type="text" name="VB_height" value="200" style="width: 50px;"></label>
        <label> x:<input type="text" name="VB_x" value="50" style="width: 50px;"></label>
        <label> y:<input type="text" name="VB_y" value="50" style="width: 50px;"></label>
    </div>
    <div>
        <label>Rect width:<input type="text" name="rect_width" value="100" style="width: 50px;"></label>
        <label> height:<input type="text" name="rect_height" value="100" style="width: 50px;"></label>
        <label> x:<input type="text" name="rect_x" value="0" style="width: 50px;"></label>
        <label> y:<input type="text" name="rect_y" value="0" style="width: 50px;"></label>
    </div>
    <button class="ts">Show</button>

</div>


</body>
</html>



kikong
19.1k 声望902 粉丝

java/javascript/nodejs,制造更好的轮子~~