日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

svg 是一種矢量圖形,在 web 上應用很廣泛,但是很多時候由于應用的場景,常常需要將 svg 轉為 png 格式,下載到本地等。隨著瀏覽器對 html 5 的支持度越來越高,我們可以把 svg 轉為 png 的工作交給瀏覽器來完成。

一般方式

  1. 創建 imageimage,src = xxx.svg;
  2. 創建 canvas,dragImage 將圖片貼到 canvas 上;
  3. 利用 toDataUrl 函數,將 canvas 的表示為 url;
  4. new image, src = url, download = download.png;

但是,在轉換的時候有時有時會碰到如下的如下的兩個問題:

問題 1 :瀏覽器對 canvas 限制

Canvas 的 W3C 的標準上沒有提及 canvas 的最大高/寬度和面積,但是每個廠商的瀏覽器出于瀏覽器性能的考慮,在不同的平臺上設置了最大的高/寬度或者是渲染面積,超過了這個閾值渲染的結果會是空白。測試了幾種瀏覽器的 canvas 性能如下:

  • chrome (版本 46.0.2490.80 (64-bit))
  • 最大面積:268, 435, 456 px^2 = 16, 384 px * 16, 384 px
  • 最大寬/高:32, 767 px
  • firefox (版本 42.0)
  • 最大面積:32, 767 px * 16, 384 px
  • 最大寬/高:32, 767px
  • safari (版本 9.0.1 (11601.2.7.2))
  • 最大面積: 268, 435, 456 px^2 = 16, 384 px * 16, 384 px
  • ie 10(版本 10.0.9200.17414)
  • 最大寬/高: 8, 192px * 8, 192px

在一般的 web 應用中,可能很少會超過這些限制。但是,如果超過了這些限制,則會導致導出為空白或者由于內存泄露造成瀏覽器崩潰。

而且從另一方面來說,導出 png 也是一項很消耗內存的操作,粗略估算一下,導出 16, 384 px * 16, 384 px 的 svg 會消耗 16384 * 16384 * 4 / 1024 / 1024 = 1024 M 的內存。所以,在接近這些極限值的時候,瀏覽器也會反應變慢,能否導出成功也跟系統的可用內存大小等等都有關系。

對于這個問題,有如下兩種解決方法:

  1. 將數據發送給后端,在后端完成轉換;
  2. 前端將 svg 切分成多個圖片導出;

第一種方法可以使用 PhantomJS、inkscape、ImageMagick 等工具,相對來說比較簡單,這里我們主要探討第二種解決方法。

svg 切分成多個圖片導出

思路:瀏覽器雖然對 canvas 有尺寸和面積的限制,但是對于 image 元素并沒有明確的限制,也就是第一步生成的 image 其實顯示是正常的,我們要做的只是在第二步 dragImage 的時候分多次將 image 元素切分并貼到 canvas 上然后下載下來。 同時,應注意到 image 的載入是一個異步的過程。

關鍵代碼

// 構造 svg Url,此處省略將 svg 經字符過濾后轉為 url 的過程。
var svgUrl = DomURL.createObjectURL(blob);
var svgWidth = document.querySelector('#kity_svg').getAttribute('width');
var svgHeight = document.querySelector('#kity_svg').getAttribute('height');
// 分片的寬度和高度,可根據瀏覽器做適配
var w0 = 8192;
var h0 = 8192;
// 每行和每列能容納的分片數
var M = Math.ceil(svgWidth / w0);
var N = Math.ceil(svgHeight / h0);
var idx = 0;
loadImage(svgUrl).then(function(img) {
	while(idx < M * N) {
		// 要分割的面片在 image 上的坐標和尺寸
		var targetX = idx % M * w0,
		 targetY = idx / M * h0,
		 targetW = (idx + 1) % M ? w0 : (svgWidth - (M - 1) * w0),
		 targetH = idx >= (N - 1) * M ? (svgHeight - (N - 1) * h0) : h0;
		var canvas = document.createElement('canvas'),
			ctx = canvas.getContext('2d');
			canvas.width = targetW;
			canvas.height = targetH;
			ctx.drawImage(img, targetX, targetY, targetW, targetH, 0, 0, targetW, targetH);
			console.log('now it is ' + idx);
			// 準備在前端下載
			var a = document.createElement('a');
			a.download = 'naotu-' + idx + '.png';
			a.href = canvas.toDataURL('image/png');
			var clickEvent = new MouseEvent('click', {
			 'view': window,
			 'bubbles': true,
			 'cancelable': false
			});
			a.dispatchEvent(clickEvent);
		idx++;
	}
}, function(err) {
	console.log(err);
});
// 加載 image
function loadImage(url) {
	return new Promise(function(resolve, reject) {
		var image = new Image();
		image.src = url;
		image.crossOrigin = 'Anonymous';
		image.onload = function() {
			resolve(this);
		};
		image.onerror = function(err) {
			reject(err);
		};
	});
}

說明:

  1. 由于在前端下載有瀏覽器兼容性、用戶體驗等問題,在實際中,可能需要將生成后的數據發送到后端,并作為一個壓縮包下載。
  2. 分片的尺寸這里使用的是 8192 * 9192,在實際中,為了增強兼容性和體驗,可以根據瀏覽器和平臺做適配,例如在 IOS 下的 safari 的最大面積是 4096 *4096。

問題 2 :導出包含圖片的 svg

在導出的時候,還會碰到另一個問題:如果 svg 里面包含圖片,你會發現通過以上方法導出的 png 里面,原來的圖片是不顯示的。一般認為是 svg 里面包含的圖片跨域了,但是如果你把這個圖片換成本域的圖片,還是會出現這種情況。

前端實現 SVG 轉 PNG

 

圖片中上部分是導出前的%20svg,下圖是導出后的%20png。svg%20中的圖片是本域的,在導出后不顯示。

問題來源

我們按照文章最開始提出的步驟,逐步排查,會發現在第一步的時候,svg%20中的圖片就不顯示了。也就是,當%20image%20元素的%20src%20為一個%20svg,并且%20svg%20里面包含圖片,那么被包含的圖片是不會顯示的,即使這個圖片是本域的。

W3C%20關于這個問題并沒有做說明,最后在%20https://bugzilla.mozilla.org/show_bug.cgi?id=628747%20找到了關于這個問題的說明。意思是:禁止這么做是出于安全考慮,svg%20里面引用的所有 外部資源 包括%20image,%20stylesheet,%20script%20等都會被阻止。

里面還舉了一個例子:假設沒有這個限制,如果一個論壇允許用戶上傳這樣的%20svg%20作為頭像,就有可能出現這樣的場景,一位黑客上傳%20svg%20作為頭像,里面包含代碼:<image%20xlink:href="http://evilhacker.com/myimage.png">(假設這位黑客擁有對于 evilhacker.com 的控制權),那么這位黑客就完全能做到下面的事情:

  • 只要有人查看他的資料,evilhacker.com 就會接收到一次 ping 的請求(進而可以拿到查看者的 ip);
  • 可以做到對于不同的 ip 地址的人展示不一樣的頭像;
  • 可以隨時更換頭像的外觀(而不用通過論壇管理員的審核)。

看到這里,大概就明白了整個問題的來龍去脈了,當然還有一點原因可能是避免圖像遞歸。

解決辦法

思路:由于安全因素,其實第一步的時候,圖片已經顯示不出來了。那么我們現在考慮的方法是在第一步之后遍歷 svg 的結構,將所有的 image 元素的 url、位置和尺寸保存下來。在第三步之后,按順序貼到 canvas 上。這樣,最后導出的 png 圖片就會有 svg 里面的 image。關鍵代碼

// 此處略去生成 svg url 的過程
var svgUrl = DomURL.createObjectURL(blob);
var svgWidth = document.querySelector('#kity_svg').getAttribute('width');
var svgHeight = document.querySelector('#kity_svg').getAttribute('height');
var embededImages = document.querySelectorAll('#kity_svg image');
// 由 nodeList 轉為 array
embededImages = Array.prototype.slice.call(embededImages);
// 加載底層的圖
loadImage(svgUrl).then(function(img) {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
canvas.width = svgWidth;
canvas.height = svgHeight;
ctx.drawImage(img, 0, 0);
	// 遍歷 svg 里面所有的 image 元素
	embededImages.reduce(function(sequence, svgImg){
		return sequence.then(function() {
			var url = svgImg.getAttribute('xlink:href') + 'abc',
				dX = svgImg.getAttribute('x'),
				dY = svgImg.getAttribute('y'),
				dWidth = svgImg.getAttribute('width'),
				dHeight = svgImg.getAttribute('height');
			return loadImage(url).then(function(sImg) {
				ctx.drawImage(sImg, 0, 0, sImg.width, sImg.height, dX, dY, dWidth, dHeight);
			}, function(err) {
				console.log(err);
			});
		}, function(err) {
			console.log(err);
		});
	}, Promise.resolve()).then(function() {
		// 準備在前端下載
		var a = document.createElement("a");
		a.download = 'download.png';
		a.href = canvas.toDataURL("image/png");
		var clickEvent = new MouseEvent("click", {
		 "view": window,
		 "bubbles": true,
		 "cancelable": false
		});
		a.dispatchEvent(clickEvent);
		});
 }, function(err) {
	 	console.log(err);
 })
 // 省略了 loadImage 函數
 // 代碼和第一個例子相同

說明

  1. 例子中 svg 里面的圖像是根節點下面的,因此用于表示位置的 x, y 直接取來即可使用,在實際中,這些位置可能需要跟其他屬性做一些運算之后得出。如果是基于 svg 庫構建的,那么可以直接使用庫里面用于定位的函數,比直接從底層運算更加方便和準確。
  2. 我們這里討論的是本域的圖片的導出問題,跨域的圖片由于「污染了」畫布,在執行 toDataUrl 函數的時候會報錯。

結語

在這里和大家分享了在前端將 svg 轉為 png 的方法和過程中可能會遇到的兩個問題,一個是瀏覽器對 canvas 的尺寸限制,另一個是導出圖片的問題。當然,這兩個問題還有其他的解決方法,同時由于知識所限,本文內容難免有紕漏,歡迎大家批評指正。

希望本文能幫助到您!

點贊+轉發,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓-_-)

關注 {我},享受文章首發體驗!

每周重點攻克一個前端技術難點。更多精彩前端內容私信 我 回復“教程”

原文鏈接:http://fex.baidu.com/blog/2015/11/convert-svg-to-png-at-frontend/

作者:zhangbobell

前端實現 SVG 轉 PNG

分享到:
標簽:SVG
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定