天天看點

SVG之圖形的引用use、剪切clipPath和蒙闆mask

一、使用<use>标簽實作圖形的引用

  當我們在使用SVG繪制圖形時,有時候會出現大量重複圖形,這個時候我們就可以使用<use>标簽進行圖形引用,相當于圖形的克隆。

<use>标簽

在SVG文檔内取得目标節點,并在别的地方複制它們。它的效果等同于這些節點被深克隆到一個不可見的DOM中,然後将其粘貼到

use

元素的位置,很像HTML5中的克隆模闆元素。因為克隆的節點是不可見的,是以當使用CSS樣式化一個

use

元素以及它的隐藏的後代元素的時候,必須小心注意。隐藏的、克隆的DOM不能保證繼承CSS屬性,除非你明文設定使用CSS繼承。

  假如我們現在想繪制滿天繁星圖,首先我們可以先繪制出一顆星星然後再多次引用這顆星星即可。

  • 1 <!DOCTYPE html>
     2 <html>
     3 
     4 <head>
     5     <meta charset="utf-8">
     6     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     7     <title>星空</title>
     8     <meta name="description" content="">
     9     <meta name="keywords" content="">
    10     <link href="" rel="stylesheet">
    11     <style type="text/css">
    12         html,
    13         body {
    14             margin: 0;
    15             padding: 0;
    16             width: 100%;
    17             height: 100%;
    18             background: #001122;
    19             line-height: 0;
    20             font-size: 0;   
    21             /*svg标簽是内聯元素,防止撐滿後會有滾動條出現*/
    22         }
    23     </style>
    24 </head>
    25 <body>
    26     <svg width="100%" height="100%" 
    27         viewBox="-400 -300 800 600" 
    28         preserveAspectRatio="xMidYMid slice"
    29         xmlns="http://www.w3.org/2000/svg">
    30         <!--一顆星星-->
    31         <s>
    32             <polygon id="star" points="0 -10 2 -2 10 0 2 2 0 10 -2 2 -10 0 -2 -2" fill="inhert" />
    33         </>
    34         <!--引用出很多星星-->
    35         <use x="0" y="0" xlink:href="#star" fill="red" />
    36         <use x="-100" y="-100" xlink:href="#star" fill="white" />
    37         <use x="100" y="100" xlink:href="#star" fill="blue" />
    38         <use x="-100" y="100" xlink:href="#star" fill="green" />
    39         <use x="100" y="-100" xlink:href="#star" fill="yellow" />
    40     </svg>
    41 </body>
    42 </html>      

   

SVG之圖形的引用use、剪切clipPath和蒙闆mask

  現在有一個問題就是我們希望漫天繁星的效果,那麼我們就不能通過手動添加<user>标簽,下面我們使用js腳本來幫助我們完成這種效果:

  • 1 <!DOCTYPE html>
     2 <html>
     3 
     4 <head>
     5     <meta charset="utf-8">
     6     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     7     <title>浪漫星空</title>
     8     <meta name="description" content="">
     9     <meta name="keywords" content="">
    10     <link href="" rel="stylesheet">
    11     <style type="text/css">
    12         html,
    13         body {
    14             margin: 0;
    15             padding: 0;
    16             width: 100%;
    17             height: 100%;
    18             background: #001122;
    19             line-height: 0;
    20             font-size: 0;   
    21             /*svg标簽是内聯元素,防止撐滿後會有滾動條出現*/
    22         }
    23     </style>
    24 </head>
    25 <body>
    26     <svg width="100%" height="100%" 
    27         viewBox="-400 -300 800 600" 
    28         preserveAspectRatio="xMidYMid slice"
    29         xmlns="http://www.w3.org/2000/svg">
    30         <!--一顆星星-->
    31         <defs>
    32             <polygon id="star" points="0 -10 2 -2 10 0 2 2 0 10 -2 2 -10 0 -2 -2" fill="white" />
    33         </defs>
    34         <!--引用出很多星星-->
    35         <g id="star-group"></g>
    36     </svg>
    37 </body>
    38     <script type="text/javascript">
    39         var SVG_NS = \'http://www.w3.org/2000/svg\';
    40         var XLINK_NS = \'http://www.w3.org/1999/xlink\';
    41 
    42         renderStart();
    43         //建立user标簽
    44         function use(origin){
    45             var _use = document.createElementNS(SVG_NS,\'use\');
    46             _use.setAttributeNS(XLINK_NS,\'xlink:href\',\'#\'+origin.id);
    47             return _use;
    48         }
    49         //建立若幹顆星星
    50         function renderStart(){
    51             var starRef = document.getElementById(\'star\');
    52             var starGroup = document.getElementById(\'star-group\');
    53             var starCount = 500;//星星數量
    54             console.log(starGroup);//友善調試js
    55             var star;
    56             while (starCount--) {
    57                 star = use(starRef);
    58                 star.setAttribute(\'opacity\', random(0.1, 0.8));//随機透明度
    59                 star.setAttribute(\'transform\', \'translate(\' + random(-400, 400) + \',\' + random(-300, 50) + \') scale(\' + random(0.1, 0.6) + \')\');//随機位移/縮放
    60                 starGroup.appendChild(star);
    61             }
    62         }
    63         //産生随機數字
    64         function random(min, max) {
    65             return min + (max - min) * Math.random();
    66         }
    67     </script>
    68 </html>      

    

SVG之圖形的引用use、剪切clipPath和蒙闆mask

二、使用<clipPath>标簽實作圖形的剪切

   clip即剪切的意思,我們使用clipPath标簽來描述剪切路徑,然後在圖形中使用clip-path屬性來使用。很容易了解,我們一定都見過這樣的頭像:

   

  這就是剪切的效果,我們使用<clipPath>标簽來定義剪切路勁,比如上圖的剪切路徑就是一個圓,然後我們在需要被剪切的圖像中使用clip-path屬性進行使用就可以了,來看具體的例子:

  • <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>clipPath</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="600px">
            <image xlink:href="https://cdn.duitang.com/uploads/item/201508/30/20150830105732_nZCLV.jpeg" x="0" y="0" height="300px" width="400px" clip-path="url(#header)"></image>
            <circle cx="100" cy="100" r="71" stroke="red" stroke-width="2px" fill="none" transform="translate(94,62)"></circle>
            
        </svg>
    </body>
    </html>      
    SVG之圖形的引用use、剪切clipPath和蒙闆mask
    這是一個圖形和一個矩形構成的圖檔,如果我們需要使用圓圈作為剪切路勁,我們需要使用<clipPath>來建立,并在圖形中引用:
  • <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>clipPath</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="600px">
            <image xlink:href="https://cdn.duitang.com/uploads/item/201508/30/20150830105732_nZCLV.jpeg" x="0" y="0" height="300px" width="400px" clip-path="url(#header)"></image>
            <defs>
                <clipPath id="header">
                    <circle cx="100" cy="100" r="71" transform="translate(94,62)"></circle>
                </clipPath>
            </defs>
        </svg>
    </body>
    </html>      
    SVG之圖形的引用use、剪切clipPath和蒙闆mask

三、使用<mask>标簽實作圖形的蒙闆

  mask的意思是“面具”,而蒙闆的意思是“蒙在上面的闆子”,都是遮擋的意思。之前看過很多mask的使用說明和案例,都覺得不好了解,是以在這裡我盡量簡單明了。首先看一個圖形:

  • <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>mask</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="600px">
            <circle cx="240" cy="90" r="80" fill="red" opacity="0.7"></circle>
            <circle cx="200" cy="130" r="80" fill="yellow" opacity="0.7"></circle>     
        </svg>
        <div></div>
    </body>
    </html>      
    SVG之圖形的引用use、剪切clipPath和蒙闆mask

    很簡單的兩個透明矩形重疊的效果,假設現在我有一個需求,就是畫一個月牙,也就是圖中黃色未遮擋的那部分(當然你可以使用<path>或其他标簽來畫),如果我們能讓黃色未遮擋部分顯示而其餘部分完全透明,那麼我們就可以實作這個效果。

    很明顯,現在要說的mask就有這個功能,mask是遮罩、蒙闆的意思,而且可以讓蒙闆中的一部分透明而其餘部分完全顯示。如果你覺得抽象我們直接看執行個體,我們先畫一個圓月:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>mask</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="600px">
            <circle cx="200" cy="130" r="80" fill="yellow"></circle>     
        </svg>
        <div></div>
    </body>
    </html>      
    SVG之圖形的引用use、剪切clipPath和蒙闆mask
    然後我們定義一個蒙闆,就是剛才畫的那兩個圓:
    <s>
        <mask id="myMask">
            <!--mask中使用顔色來控制透明度:white表示透明度為0,即完全顯示;black表示透明度為1,即完全透明-->
            <circle cx="200" cy="130" r="80" fill="white"></circle>
            <circle cx="240" cy="90" r="80" fill="balck"></circle>
        </mask>
    </s>      
    即我們讓剛才紅色圓的位置不顯示,黃色位置的圓顯示,然後我們在上面這個圓月中使用這個蒙闆:
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>clip</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="600px">
            <!--預定義蒙闆-->
            <defs>
                <mask id="myMask">
                    <!--mask中使用顔色來控制透明度:white表示透明度為0,即完全顯示;black表示透明度為1,即完全透明-->
                    <circle cx="200" cy="130" r="80" fill="white"></circle>
                    <circle cx="240" cy="90" r="80" fill="balck"></circle>
                </mask>
            </defs>
            <!--使用蒙闆-->
            <circle cx="200" cy="130" r="80" fill="yellow" mask="url(#myMask)"></circle>     
        </svg>
        <div></div>
    </body>
    </html>      

  

SVG之圖形的引用use、剪切clipPath和蒙闆mask

  可能還是有些抽象,是以我們應該多嘗試,以此來驗證自己想法的正确性。

  ps:當然mask不隻是一部分透明一部分不透明,我們還可以使用半透明等蒙闆,根據實際需要來調整蒙闆中的fill即可。

四、綜合運用

  結合我們之前畫的星星月亮,我們可以畫出一幅星空圖,圖中除了燈塔的動畫效果,其餘都是前面用過的知識點,這裡就不在贅述,之後的筆記中我會總結一些SVG動畫效果,歡迎移步:

  • SVG之圖形的引用use、剪切clipPath和蒙闆mask
    SVG之圖形的引用use、剪切clipPath和蒙闆mask
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Star Sky</title>
        <style>
            html, body {
                margin: 0;
                padding: 0;
                width: 100%;
                height: 100%;
                background: #001122;
                line-height: 0;
                font-size: 0;
            }
        </style>
    </head>
    <body>
        <svg width="100%" height="100%" 
            viewBox="-400 -300 800 600"
            preserveAspectRatio="xMidYMid slice">
            <!--預定義一顆星星-->
            <defs>
                <polygon id="star" points="0 -10 2 -2 10 0 2 2 0 10 -2 2 -10 0 -2 -2" fill="white"></polygon>
            </defs>
            <!--實景-->
            <g id="real">
                <!--滿天繁星(動态生成)-->
                <g id="star-group"></g>
                <g id="moon-group" transform="translate(0 50)">
                    <mask id="moon-mask">
                        <circle cx="-250" cy="-150" r="100" fill="white"></circle>
                        <circle cx="-200" cy="-200" r="100" fill="black"></circle>
                    </mask>
                    <circle mask="url(#moon-mask)" cx="-250" cy="-150" r="100" fill="yellow"></circle>
                </g>
                <!--燈塔-->
                <g id="light-tower" transform="translate(250, 0)">
                    <defs>
                        <linearGradient id="tower" x1="0" y1="0" x2="1" y2="0">
                            <stop offset="0" stop-color="#999"></stop>
                            <stop offset="1" stop-color="#333"></stop>
                        </linearGradient>
                        <radialGradient id="light" cx="0.5" cy="0.5" r="0.5">
                            <stop offset="0" stop-color="rgba(255, 255, 255, .8)"></stop>
                            <stop offset="1" stop-color="rgba(255, 255, 255, 0)"></stop>
                        </radialGradient>
                        <clipPath id="light-mask">
                            <polygon transform="rotate(10)" points="0 0 -300 -15 -300 15">
                                <!--SVG動畫效果-->
                                <animateTransform 
                                    attributeName="transform"
                                    attributeType="XML"
                                    type="rotate"
                                    from="0"
                                    to="360"
                                    dur="10s"
                                    repeatCount="indefinite">
                                </animateTransform>
                            </polygon>
                            <circle cx="0" cy="0" r="2"></circle>
                        </clipPath>
                    </defs>
                    <polygon points="0 0 5 50 -5 50" fill="url(#tower)"></polygon>
                    <ellipse cx="0" cy="0" rx="300" ry="100" clip-path="url(#light-mask)" fill="url(#light)"></ellipse>
                </g>
            </g>
    
            <!--虛景:使用use标簽複制實景,使用mask等設定相關效果-->
            <g id="reflact" mask="url(#fading)" transform="translate(0 50)">
                <defs>
                    <linearGradient id="fade" x1="0" y1="0" x2="0" y2="1">
                        <stop offset="0" stop-color="rgba(255,255,255,.3)"></stop>
                        <stop offset="0.5" stop-color="rgba(255,255,255,0)"></stop>
                    </linearGradient>
                    <mask id="fading">
                        <rect x="-800" y="0" width="1600" height="400" fill="url(#fade)"></rect>
                    </mask>
                </defs>
                <use xlink:href="#real" transform="scale(1, -1) translate(0 -40)"></use>    
            </g>   
            <rect x="-800" y="50" width="1600" height="400" opacity="0.2" fill="url(#fade)"></rect>
        </svg>
    </body>
        <script>
            /* jshint browser: true */
            var SVG_NS = \'http://www.w3.org/2000/svg\';
            var XLINK_NS = \'http://www.w3.org/1999/xlink\';
            var paper = document.querySelector(\'svg\');
            var defs = document.querySelector(\'svg defs\');
            renderStar();
            function use(origin) {
                var _use = document.createElementNS(SVG_NS, \'use\');
                _use.setAttributeNS(XLINK_NS, \'xlink:href\', \'#\' + origin.id);
                return _use;
            }
            function random(min, max) {
                return min + (max - min) * Math.random();
            }
            function renderStar() {
                var starRef = document.getElementById(\'star\');
                var starGroup = document.getElementById(\'star-group\');
                var starCount = 500;
                var star;
                while (starCount--) {
                    star = use(starRef);
                    star.setAttribute(\'opacity\', random(0.1, 0.4));
                    star.setAttribute(\'transform\', 
                        \'translate(\' + random(-400, 400) + \',\' + random(-300, 50) + \') \' +
                        \'scale(\' + random(0.1, 0.6) + \')\');
                    starGroup.appendChild(star);
                }
            }
        </script>
    </html>      
    start sky
SVG之圖形的引用use、剪切clipPath和蒙闆mask