1. 程式人生 > >重溫---HTML5高階---SVG繪圖

重溫---HTML5高階---SVG繪圖

1.1 如何等待所有圖片載入完成後,才開始繪圖?

 

多張圖片繪製需要按照特定的順序,而載入完成順序是完全無法預測,只能等待所有圖片載入完成,才能開始繪製。

/***************************

  //典型的錯誤程式碼

  var img1 = new Image();

  img1.src = "";

  img1.onload = function(){

       ctx.drawImage(img1, x, y);

  }

  var img2 = new Image();

  img2.src = "";

  img2.onload = function(){

       ctx.drawImage(img2, x, y);

  }

  ******************************/

 

  var progress = 0;    //載入進度 0~100

  var img1 = new Image();

  img1.src = "";

  img1.onload = function(){

       progress += 80;

       if(progress===100){

              startDraw();

       }

  }

  var img2 = new Image();

  img2.src = "";

  img2.onload = function(){

       progress += 20;

       if(progress===100){

              startDraw();

       }

  }

1.2 如何為Canvas上的圖形/影象繫結事件監聽?——難點

    網頁只能為DOM元素繫結監聽函式,Canvas繪圖技術中只要一個Canvas元素,其它圖形影象都不是元素——無法進行事件繫結!——

    解決辦法:為Canvas繫結監聽函式,獲取事件發生的座標,是否處於目標圖形/影象所在範圍內——只能為規則的圖形“繫結”監聽

 

Adobe Photoshop:處理點陣圖——每幅影象由點(rgb)組成,善於描述顏色的細節變化,可用於照片等領域——放大後會出現馬賽克失真。

Adobe Illustrator:處理向量圖——每幅影象由線條(需要指定方向、值),可以無限縮放而不失真——不善於描述顏色的細節變化。

 

1.3 HTML5新特性之五——SVG繪圖

 

 

Canvas繪圖

SVG繪圖

繪圖型別

點陣圖

向量圖

縮放

失真

不失真

顏色細節

豐富

不夠豐富

應用領域

照片、遊戲

統計圖、圖示、地圖

內容

JS繪製

每個圖形都是標籤

事件繫結

不方便

方便

 

Scalable Vector Graphiph,可縮放的向量圖,此技術在2000年就已經存在了,獨立於網頁的一門技術;HTML5之後,納入了HTML5標準標籤庫,並進行了一定的瘦身。

 

SVG技術的使用方法:

 

(1)HTML5之前的使用方法:

  SVG標籤不屬於HTML4或XHTML標籤,只能編寫在獨立的XML檔案中,首先編寫一個SVG檔案(本質是一個XML檔案),然後在HTML中使用IMG/IFRAME/OBJECT/EMBED等標籤引入.svg檔案

(2)HTML5之後的使用方法——H5已經把SVG標籤採納:

  在HTML檔案中直接使用SVG相關標籤即可

  <svg>預設為300*150的inline-block</svg>

 

練習:

(1)使用SVG矩形繪製一個國際象棋的棋盤——使用HTML中的rect標籤

(2)使用SVG矩形繪製一個國際象棋的棋盤——使用JS動態建立rect標籤

 

1.4 使用SVG繪製矩形

  <rect width="" height="" x="" y="" fill="" fill-opacity="" stroke="" stroke-width="" stroke-opacity=""></rect>

  注意:

  (1)SVG圖形的樣式可以用HTML屬性賦值,也可以使用CSS形式,但不接受普通的CSS屬性!只能使用SVG元素的專有樣式屬性。

  (2)SVG圖形的屬性不屬於HTMLDOM標準,只能使用核心DOM方法操作其屬性: rect.setAttribute('','')

  (3)使用JS動態建立SVG元素,1)用svg.innerHTML ='' 2)用document.createElementNS('', ''),不能使用document.createElement()

  (4)SVG元素的nodeName都是純小寫形式!與普通的HTML元素不同!

 

練習: 

(1)在SVG畫布上繪製一個矩形,從左移動到右,同時填充顏色還在不停的隨機改變

(2)根據如下的JSON資料,繪製柱狀統計圖——有坑!!

  [   

       {"label": "1月", "value": 350},

       {"label": "2月", "value": 300},

       {"label": "3月", "value": 450},

       {"label": "4月", "value": 380}

  ]

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    body {
      text-align: center;
    }

    svg {
      background: #f0f0f0;
    }
  </style>
</head>
<body>
  <h3>使用SVG繪圖——矩形</h3>
  <svg width="600" height="400" id="svg10">

  </svg>

<script>
  var data = [
    {"label": "1月", "value": 250},
    {"label": "2月", "value": 200},
    {"label": "3月", "value": 350},
    {"label": "4月", "value": 280}
  ];
  var html = '';
  for(var i=0; i<data.length; i++){
    var d = data[i];
    //動態建立的svg元素,必須指定所在的xmlns
    // var rect=document.createElement('rect');
    var rect=document.createElementNS('http://www.w3.org/2000/svg','rect');
    rect.setAttribute('width',50);
    rect.setAttribute('height',d.value);
    rect.setAttribute('x',i*80);
    rect.setAttribute('y',10);
    svg10.appendChild(rect);

  }
  svg9.innerHTML = html;
</script>
</body>
</html>

1.5 使用SVG繪製圓形

 

  <circle r="" cx="" cy=""></circle>

 

練習:     

(1)建立30個大小隨機、顏色隨機、透明度隨機的圓形

  提示:使用createElementNS()建立元素,setAttribute()修改屬性

(2)點選某個圓形,其慢慢變大、變淡...... 直至消失,從DOM樹上刪除該元素

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    body {
      text-align: center;
    }

    svg {
      background: #f0f0f0;
    }
  </style>
</head>
<body>
<h3>使用SVG繪圖——圓</h3>
<svg width="600" height="400" id="svg12">

</svg>

<script>
  //動態新增30個圓形
  for(var i=0;i<30;i++){
    var c=document.createElementNS('http://www.w3.org/2000/svg','circle');
    c.setAttribute('r',rn(10,80));
    c.setAttribute('cx',rn(0,600));
    c.setAttribute('cy',rn(0,400));
    c.setAttribute('fill',rc(0,256));
    c.setAttribute('fill-opacity',Math.random());

    svg12.appendChild(c);
  }

  //使用事件代理方式為每個圓形繫結單擊監聽函式
  svg12.onclick=function (e) {
    var target=e.target;
    if(target.nodeName==='circle'){
      var timer=setInterval(function () {
        // target.setAttribute('data-animate','true');
        //讓半徑變大
        var r=target.getAttribute('r');
        r=parseFloat(r);
        r*=1.1;
        target.setAttribute('r',r);
        //讓透明度變小
        var p=target.getAttribute('fill-opacity');
        p=parseFloat(p);
        p*=0.9;
        target.setAttribute('fill-opacity',p);
        if(p<0.001){ //幾乎看不見時刪除
          clearInterval(timer);
          svg12.removeChild(target);
        }
      },30);
    }

    
  }

  // random number:生成指定範圍內的隨機整數
  function rn(min,max) {
    return Math.floor(Math.random()*(max-min)+min);
  }
  //random color:生成指定範圍內的隨機顏色
  function rc(min,max) {
    var r=rn(min,max);
    var g=rn(min,max);
    var b=rn(min,max);
    return `rgb(${r},${g},${b})`;
  }
</script>
</body>
</html>

(3)實現蜻蜓點水效果,點選svg畫布的某處,即在此處生成一個圓形,立即變大、變淡...直至消失

 

1.6 使用SVG繪製橢圓

 

  <ellipse rx="" ry="" cx="" cy=""></ellipse>

 

1.7 使用SVG繪製直線

 

  <line x1="" y1="" x2="" y2="" stroke=""></line>

  注意:所有的SVG圖形預設只有填充色,沒有描邊色。

  練習:使用line建立如下的圖示:

       

  提示:可以把多個元素放在一個<g></g>小組中,可以自動繼承小組的公共屬性

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    body {
      text-align: center;
    }

    svg {
      background: #f0f0f0;
    }
  </style>
</head>
<body>
<h3>使用SVG繪圖</h3>
<svg width="600" height="400" id="svg15">
  <!--group:小組-->
  <g stroke="#800" stroke-width="50">
    <line x1="100" y1="100" x2="200" y2="100"></line>
    <line x1="250" y1="100" x2="500" y2="100"></line>
    <line x1="100" y1="200" x2="200" y2="200"></line>
    <line x1="250" y1="200" x2="500" y2="200"></line>
    <line x1="100" y1="300" x2="200" y2="300"></line>
    <line x1="250" y1="300" x2="500" y2="300"></line>
  </g>

</svg>

<script>

</script>
</body>
</html>

1.8 使用SVG繪製折線

 

  <polyline points="x1,y1  x2,y2  x3,y3  ....."  stroke="" fill-opacity="0"></polyline>

 

  練習:使用折線繪製如下的圖示

       

 

1.9使用SVG繪製多邊形    

                

  <polygon points="x1,y1  x2,y2  ...."></polygon>

 練習:使用多邊形繪製如下的圖示:

 

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    body {
      text-align: center;
    }

    svg {
      background: #f0f0f0;
    }
  </style>
</head>
<body>
<h3>使用SVG繪圖——信封</h3>
<svg width="600" height="400" id="svg15">
  <g fill="#030">
    <polygon points="100,100 300,200 500,100"></polygon>
    <polygon points="100,150 100,300 500,300 500,150 300,250" ></polygon>
  </g>

</svg>

<script>

</script>
</body>
</html>

1.10 使用SVG繪製文字

 

  提示:傳統的標籤不能置於SVG內部!同理,SVG的標籤也不能放在其它元素內部!

  <text x="" y="" font-size="" alignment-baseline="before-edge">文字內容</text>

 

5.11使用SVG繪製圖像

 

  提示:在SVG中繪製圖像使用image元素,引入點陣圖後,此SVG圖片放大後會失真。

  <image width="" height="" xlink:href="x.jpg" x="" y=""></image>

 

1.12 如何使用漸變效果

 

<svg width="600" height="400" id="svg15">

  <!--漸變屬於特效,必須宣告在“特效列表”-->

  <defs>

    <linearGradient id="g1" x1="0" y1="0" x2="100%" y2="0">

      <stop offset="0" stop-color="#f00"></stop>

      <stop offset="100%" stop-color="#0f0"></stop>

    </linearGradient>

  </defs>

  <rect fill="url(#g1)"></rect>

</svg>

 

課後練習:

  1. 假設前端頁面獲取到後臺返回瞭如下的JSON資料:

'[{"label":"HTML",value:3}, {"label":"CSS",value:5},....]'

根據這段JSON字串,繪製下圖:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <style>
    body{
      text-align: center;
    }
    svg{
      background:#f0f0f0;
    }
  </style>
</head>
<body>
  <h3>svg繪圖--各階段難度係數</h3>
  <svg id="s1">
    <!--所有的特效物件-->
    <defs id="effectList">
      <!--<linearGradient id="g0" x1="0" y1="0" x2="0" y2="100%">-->
        <!--<stop offset="0" stop-color="#f00"></stop>-->
        <!--<stop offset="100%" stop-color="#f00" stop-opacity="0"></stop>-->
      <!--</linearGradient>-->
    </defs>
    <!--座標軸小組-->
    <g stroke="#555" stroke-width="2">
      <!--x軸-->
      <line x1="50" y1="450" x2="650" y2="450"></line>
      <line x1="630" y1="440" x2="650" y2="450"></line>
      <line x1="630" y1="460" x2="650" y2="450"></line>
      <!--Y軸-->
      <line x1="50" y1="450" x2="50" y2="50"></line>
      <line x1="40" y1="70" x2="50" y2="50"></line>
      <line x1="60" y1="70" x2="50" y2="50"></line>
    </g>
  </svg>
  <script>
    var w=600+100;
    var h=10*40+100;  //難度值*40高度倍率+padding*2
    s1.setAttribute('width',w);
    s1.setAttribute('height',h);

    var data=[
      {label:"html",value:3},
      {label:"css",value:5},
      {label:"js",value:7},
      {label:"dom",value:6},
      {label:"jquery",value:5.5},
      {label:"ajax",value:8},
      {label:"html5",value:5.5}
    ];
    var colWidth=600/(2*data.length+1);
    for(var i=0;i<data.length;i++){
      var d=data[i]; //遍歷每個資料物件
      var cw=colWidth; //柱子的寬
      var ch=d.value*40; //柱子的高
      var x=(2*i+1)*colWidth+50;
      var y=500-50-ch;

      //動態新增漸變物件
      var c=rc(); //漸變顏色
      var html=`
        <linearGradient id="g${i}" x1="0" y1="0" x2="0" y2="100%">
          <stop offset="0" stop-color="${c}"></stop>
          <stop offset="100%" stop-color="${c}" stop-opacity="0"></stop>
        </linearGradient>`;

      effectList.innerHTML+=html;
      //動態建立矩形
      var rect=document.createElementNS('http://www.w3.org/2000/svg','rect');
      rect.setAttribute('width',cw);
      rect.setAttribute('height',ch);
      rect.setAttribute('x',x);
      rect.setAttribute('y',y);
      rect.setAttribute('fill',`url(#g${i})`);

      s1.appendChild(rect);
    }
    // random color
    function rc() {
      var r=Math.floor(Math.random()*256);
      var g=Math.floor(Math.random()*256);
      var b=Math.floor(Math.random()*256);
      return `rgb(${r},${g},${b})`
    }
  </script>
</body>
</html>

2)自學two.js工具的使用,理解其作用,仿寫官方示例程式碼,學會使用方法,實現如下效果,月亮繞著地球轉,地球繞著太陽轉: