svg foreignObject的作用(文本換行,生成圖片)

  • 2019 年 10 月 10 日
  • 筆記

SVG內部利用foreignObject嵌入XHTML元素

<foreignObject>元素的作用是可以在其中使用具有其它XML命名空間的XML元素,換句話說藉助<foreignObject>標籤,我們可以直接在SVG內部嵌入XHTML元素,舉個簡單的例子:

<svg xmlns="http://www.w3.org/2000/svg">    <foreignObject width="120" height="50">        <body xmlns="http://www.w3.org/1999/xhtml">          <p>文字。</p>        </body>      </foreignObject>  </svg>

可以看到<foreignObject>標籤裡面有一個設置了xmlns="http://www.w3.org/1999/xhtml"命名空間的<body>標籤,此時<body>標籤及其子標籤都會按照XHTML標準渲染,實現了SVG和XHTML的混合使用。

 

 

SVG forginObject實現文本自動換行

SVG要實現文本換行,往往需要手動阻斷,類似下面的程式碼:

<svg xmlns="http://www.w3.org/2000/svg">    <text font-size="12">      <tspan x="0" y="10">一段需要word wrap</tspan>      <tspan x="0" y="26">的文字。</tspan>    </text>  </svg>

需要2<tspan>元素,這一點都不工程。雖然Chrome瀏覽器可以對<text>標籤進行white-space:normal的強制設置,但也只是Chrome瀏覽器可以。

但是如果使用<foreignObject>元素,則自動換行就是小菜:

<svg xmlns="http://www.w3.org/2000/svg">    <foreignObject width="120" height="50">        <body xmlns="http://www.w3.org/1999/xhtml">          <p style="font-size:12px;margin:0;">一段需要word wrap的文字。</p>        </body>      </foreignObject>  </svg>

SVG forginObject生成圖片

除了輕鬆實現文本換行,SVG <foreignObject>元素還有其他更高級的應用,就是可以將頁面上的DOM元素輕鬆變成圖片,原理如下:

  1. 獲取對應DOM元素的outerHTML程式碼;
  2. 放在<foreignObject>元素中;
  3. 圖片方式顯示我們的SVG圖形,例如:
    <img width="300" height="150" src='data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="120" height="50"><body xmlns="http://www.w3.org/1999/xhtml"><p style="font-size:12px;margin:0;">一段需要word wrap的文字。</p></body></foreignObject></svg>'>
  4. 上一步的圖片本質還是SVG,我們可以藉助canvas drawImage()方法將圖片放在畫布上,然後使用canvas.toDataURL()方法轉換成pngjpg圖片,核心程式碼:
    var canvas = document.createElement('canvas');  var context = canvas.getContext('2d');  canvas.drawImage(img, 0, 0);  img.src = canvas.toDataURL('image/png');

一旦我們可以把DOM元素轉換成圖片,我們就可以輕輕鬆鬆配合JS在前端實現網頁截圖功能。

 

之前項目中有將highcharts.js的表格轉換成圖片,程式碼如下:

        private generateTable(chartSetting: Core.ChartConfig) {              if (chartSetting.firstColColor.toLocaleLowerCase() == "rgb(255,255,254)") {                  chartSetting.firstColColor = chartSetting.bgColor;              }              if (chartSetting.firstRowColor.toLocaleLowerCase() == "rgb(255,255,254)") {                  chartSetting.firstRowColor = chartSetting.bgColor;              }              var tableHtml = document.createElement("table");              tableHtml.style.border = "1px solid";              tableHtml.style.borderCollapse = "collapse";              tableHtml.style.fontSize = "14px";              tableHtml.style.lineHeight = "26px";                //tableHtml.style.backgroundColor = (chartSetting.bgColor.toLocaleLowerCase().replace(")", "") + "," + (1 - chartSetting.opacityValue) + ")").replace("rgb", "rgba");              tableHtml.style.color = chartSetting.fontColor;              tableHtml.style.borderColor = chartSetting.borderColor;              tableHtml.style.fontFamily = chartSetting.fontFamily;              var tableData: any = chartSetting.chartData;              var series = [];              for (var i = 1; i < tableData[0].length - 1; i++) {                  var y = tableData[0][i];                  if (y == '' || y == null) {                      break;                  }                  series.push(y);              }              for (var i = 0; i < tableData.length - 1; i++) {                  var x = tableData[i][0];                  if (i != 0 && (x == '' || x == null)) {                      break;                  }                  var tr = document.createElement("TR");                  tableHtml.appendChild(tr);                    for (var j = 0; j < tableData[i].length - 1; j++) {                      if (j != 0 && (tableData[0][j] == null || tableData[0][j] == "")) {                          break;                      }                      //var y: any = parseFloat(tableData[i][j]);                      var td = document.createElement("TD");                      tr.appendChild(td);                      td.style.border = "1px solid " + chartSetting.borderColor;                      td.style.padding = "0px 7px 0px 7px";                      if (tableData[i][j] || i == 0 || j == 0) {                          td.innerHTML = tableData[i][j];                      } else {                          td.innerHTML = "";                      }                      if (i != 0 && j == 0) {                          td.style.backgroundColor = (chartSetting.firstColColor.toLocaleLowerCase().replace(")", "") + "," + (1 - chartSetting.opacityValue) + ")").replace("rgb", "rgba");                      } else if (i == 0) {                          td.style.backgroundColor = (chartSetting.firstRowColor.toLocaleLowerCase().replace(")", "") + "," + (1 - chartSetting.opacityValue) + ")").replace("rgb", "rgba");                      } else {                          td.style.backgroundColor = (chartSetting.bgColor.toLocaleLowerCase().replace(")", "") + "," + (1 - chartSetting.opacityValue) + ")").replace("rgb", "rgba");                      }                  }              }              var table_img_div = document.getElementById("table_img");              table_img_div.innerHTML = tableHtml.outerHTML.replace(/n/g, "<br>");              var tableW = $(table_img_div).width();              var tableH = $(table_img_div).height();              var img_svg = '<svg width="' + (tableW) + 'px" height="' + tableH + 'px" xmlns = "http://www.w3.org/2000/svg" ><desc>Created with Highcharts 4.2.4</desc> <switch> <foreignObject width="' + (tableW) + '" height="' + tableH + '" requiredFeatures = "http://www.w3.org/TR/SVG11/feature#Extensibility"><body style="margin:0" xmlns="http://www.w3.org/1999/xhtml">' + tableHtml.outerHTML                  + ' </body></foreignObject><text font-size="10" font-family="Verdana"><tspan x= "10" y= "10" > 請使用其他瀏覽器,</tspan><tspan x= "10" y= "20" > 以正常顯示錶格。</tspan> </text></switch></svg>';              img_svg = img_svg.replace(/n/g, "<br/>");              return {                  svg: img_svg,                  width: tableW,                  height: tableH              };          }