SVG基础以及使用Javascript DOM操作SVG

简介

首先要明白SVG是基于XML格式定义图像的一种技术,并且是矢量的,也就是说在不同分辨率下都显示得很好。想到这里,有人会想起Canvas这东西,它跟SVG有什么不同?

下面是W3SCHOOL给出的对比:

SVG

  • 不依赖分辨率
  • 支持事件处理器
  • 最适合带有大型渲染区域的应用程序(比如谷歌地图)
  • 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
  • 不适合游戏应用

Canvas

  • 依赖分辨率
  • 不支持事件处理器
  • 弱的文本渲染能力
  • 能够以 .png 或 .jpg 格式保存结果图像
  • 最适合图像密集型的游戏,其中的许多对象会被频繁重绘

大概就是这样,如果你要使用SVG,应该考虑是否更应该采用Canvas,并且还需要知道并不是所有浏览器都支持它,IE8或以下就不支持SVG,除此以外的现代浏览器包括IE9+基本都支持。IE它有自己的一套方案:VML,这个你自己去搜索相关资料。另外也可以通过安装插件使得其支持SVG,比如adobe出品的svg viewer。

在HTML中使用SVG

基础部分看W3SCHOOL的教程:http://www.w3school.com.cn/svg/index.asp,很简单,花30分钟就可以扫完。

特别要注意SVG in HTML部分,它介绍了如何在HTML中使用SVG,可能会比较麻烦,庆幸的是在支持HTML5的浏览器中,可以直接创建SVG标签:

<html>
<body>
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="190">
        <polygon points="100,10 40,180 190,60 10,60 160,180"
          style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" />
    </svg>
</body>
</html>

这种方式称为内联SVG,有了这个内联方式javascript就好控制得多了。

分组元素<g>

g元素可以对多个元素进行分组,使其更具语义化,并且方便对分组里的元素进行统一处理,比如样式、动画等。

<g>
    <rect x="10" y="10" width="40" height="40" ry="10"/>
    <rect x="80" y="80" width="40" height="40" ry="10"/>
    <rect x="150" y="150" width="40" height="40" ry="10"/>
</g>
解决text文本排版问题

在SVG中对文本排版比较头疼,你不能像HTML那样轻易的把文本放到一个“矩形容器”(比如DIV)里,因为那些标签都是封闭的(在开始标签中进行关闭)。简单的做法是把文字和矩形设定到相近的坐标中,使其看起来是“一起的”(难道这就是世界上最遥远的距离?):

<rect x="10" y="10" width="100" height="40" style="fill:rgb(255,255,255);stroke-width:1;stroke:rgb(0,0,0)"/>
<text x="35" y="35" font-size="16" style="fill:rgb(0,0,0);">text</text>;

效果:
QQ截图20140811155954.png

显然这种方式是很难管理,当我想更换一个位置的时候,我必需把矩形和文本的位置都进行调整,现在只有两个元素还好说,一旦多起来,那简直就是噩梦。而g元素可以帮我们解决这个问题。

先把它们都放到一个g元素里,这样里面所有元素的位置都是相对于这个g元素的,通过更改g元素的位置,可以达到调整整个分组的位置的效果。但需要通过transform才能有效,而不是x和y:

<g transform="translate(50,50)">
    <rect x="0" y="0" width="100" height="40" style="fill:rgb(255,255,255);stroke-width:1;stroke:rgb(0,0,0)"/>
    <text x="25" y="25" font-size="16" style="fill:rgb(0,0,0);">text</text>;
</g>

现在矩形和文本的x和y轴都是相对于g的位置而言的,translate(50,50)表示 x="50", y="50" 这样应该很好理解吧?

通过Javascript DOM控制SVG

页面上已经存在一个SVG容器,这个容器带有带有一个XML命名空间http://www.w3.org/2000/svg和一个idmain

<svg xmlns="http://www.w3.org/2000/svg" id="main" version="1.1" height="200"></svg>
    

下面我们使用一系列的DOM方法在容器里添加一个矩形:

  • 通过document.getElementById根据元素ID获取这个容器对象
  • 使用document.createElementNS创建一个带http://www.w3.org/2000/svg命名空间的矩形对象
  • 使用element.setAttribute设置这个矩形对象的属性
  • 使用element.appendChild把它添加到容器里

代码:

<script>
    var main = document.getElementById( "main" );
    var rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
    rect.setAttribute( "width", 100 );
    rect.setAttribute( "height", 30 );
    rect.setAttribute( "style", "fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)" );
    main.appendChild( rect );
</script>

效果
QQ截图20140811152842.png

设置文本

textContent属性可以获取和设置text元素文本:

// SVG
<text id="text" x="25" y="25" font-size="16" style="fill:rgb(0,0,0);">foo</text>;

// JS
<script>
    var text = document.getElementById( "text" );
    console.log( text.textContent ); // foo
    text.textContent = "Hello world!"; // 重新设置文本
</script>

获取元素高宽和坐标

getBBox方法可以获取所有元素的高宽和坐标:

// SVG
<text id="text" x="25" y="25" font-size="16" style="fill:rgb(0,0,0);">foo</text>;

// JS
<script>
    var text = document.getElementById( "text" );
    console.log( text.getBBox() ); // {height: 16, width: 32, y: 11, x: 25} 
</script>

事件处理

SVG也可以像HTML那样为元素添加自定义事件。
使用on + 事件名属性监听:

// SVG
<text id="text" x="25" y="25" font-size="16" style="fill:rgb(0,0,0);">foo</text>;

// JS
<script>
    var text = document.getElementById( "text" );
    // 点击文本时弹出其内容
    text.onclick = function() {
        alert( this.textContent );
    };
</script>

也可以使用element.addEventListener方法监听:

// SVG
<text id="text" x="25" y="25" font-size="16" style="fill:rgb(0,0,0);">foo</text>;

// JS
<script>
    var text = document.getElementById( "text" );
    // 点击文本时弹出其内容
    // 事件名前面不带on
    text.addEventListener( 'click', function() {
        alert( this.textContent );
    } );
</script>

两种方法有什么不同?on + 事件名属性只能为同一个事件添加一个处理方法,再对这个属性进行设置时会覆盖掉上一个方法,而element.addEventListener多次使用也不会覆盖上一个,而是从原来的事件上叠加。

自定义的z-index层级属性

在SVG中,怎样定义每个元素的层级关系?比如我想把一个圆形叠在矩形上。这是大多数人会遇到的问题,事实上SVG是按元素的书写顺序进行堆叠的,就是说后面的元素会叠在前面的元素上,并且没有相关属性进行设置。这使得层级的控制变得不那么灵活,但是可以通过Javascript DOM增加一个自定义的层级属性z-index,当然逻辑还是要靠我们自己去处理,先看SVG代码:

<svg xmlns="http://www.w3.org/2000/svg" id="main" version="1.1">
    <g transform="translate(80,80)" z-index="3">
        <text x="25" y="25" font-size="16" style="fill:rgb(0,0,0);" z-index="2">text1</text>
        <rect x="0" y="0" width="100" height="40" style="fill:rgb(255,255,255);stroke-width:1;stroke:rgb(0,0,0)" z-index="1" />
    </g>

    <g transform="translate(50,50)" z-index="1">
        <rect x="0" y="0" width="100" height="40" style="fill:rgb(255,255,255);stroke-width:1;stroke:rgb(0,0,0)" z-index="1" />
        <text x="25" y="25" font-size="16" style="fill:rgb(0,0,0);" z-index="2">text2</text>
    </g>
</svg>

不加任何处理的效果:

QQ截图20140811182004.png

可以看到text2文本出来了,但挡住了text1,并且text1也被所在的矩形挡住了,连文本也显示不出来,此时的z-index毫无作用(因为是我自己定义的属性)。于是乎,我使用下面的JS代码对其进行处理:

<script>
    SVG_ZIndex( document.getElementById( "main" ).childNodes );
    
    /**
     * 解析SVG元素z-index属性,并根据其值定义元素的层级
     * 规则:z-index越大,层级越高
     *
     * @param {elements} elements 一个包含DOM节点的类数组对象或者数组
     * @return {void}
     */
    function SVG_ZIndex( elements ) {
        var elements_arr = [];
        // 遍历节点列表,初始化一些设置
        for( var i = 0, len = elements.length; i < len; i++ ) {
            var elem = elements[ i ];
            // 某些类型的节点可能没有getAttribute属性,你也可以根据nodeType属性来判断
            if( ! elem.getAttribute ) continue;
            
            // 递归子节点
            if( elem.childNodes ) {
                SVG_ZIndex( elem.childNodes );
            }
            // 默认所有元素都处于第1级
            if( ! elem.getAttribute( "z-index" ) ) {
                elem.setAttribute( "z-index", 1 );
            }
            elements_arr.push( elem );
        }

        if( elements_arr.length === 0 ) return;
        
        // 根据z-index属性进行排序
        elements_arr.sort( function( e1, e2 ) {
            var z1 = e1.getAttribute( "z-index" );
            var z2 = e2.getAttribute( "z-index" );
            if( z1 === z2 ) {
                return 0;
            } else if( z1 < z2 ) {
                return -1;
            } else {
                return 1;
            }
        } );
        // 排序完成后,按顺序移动这些元素
        var parent = elements_arr[0] && elements_arr[0].parentNode;
        for( var i = 0, len = elements_arr.length; i < len; i++ ) {
            var elem = elements_arr[ i ];
            // 提示:appendChild里的elem节点如果在页面中已经存在
            // 那么表示这个节点从原来的地方移动到parent最后的地方,而不是以一个新节点插入
            parent.appendChild( elem );
        }
    }
</script>

效果:
QQ截图20140811180021.png

经JS处理后的SVG代码:

<svg xmlns="http://www.w3.org/2000/svg" id="main" version="1.1">
    <g transform="translate(50,50)" z-index="1">
        <rect x="0" y="0" width="100" height="40" style="fill:rgb(255,255,255);stroke-width:1;stroke:rgb(0,0,0)" z-index="1" />
        <text x="25" y="25" font-size="16" style="fill:rgb(0,0,0);" z-index="2">text2</text>
    </g>
    <g transform="translate(80,80)" z-index="3">
        <rect x="0" y="0" width="100" height="40" style="fill:rgb(255,255,255);stroke-width:1;stroke:rgb(0,0,0)" z-index="1" />
        <text x="25" y="25" font-size="16" style="fill:rgb(0,0,0);" z-index="2">text1</text>
    </g>
</svg>

可以看到元素都按照我们定义的z-index的值进行排列并显示。如果你还不怎么明白,你可以继续了解更多的DOM API。

  • 13
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值