背景是這樣的,母親節的時候,咱們有個需求就是用戶能夠長按或者點擊一個按鈕進行截圖後去分享咱們的活動,然而咱們的圖片例如頭像,採用又拍雲作 cdn 優化,因此意味着圖片的連接跟主頁面所在域名不同,當須要須要對 canvas 圖片進行 getImageData()
或 toDataURL()
操做的時候,跨域問題就出來了。html
對於跨域的圖片,只要可以在網頁中正常顯示出來,就可使用 canvas 的 drawImage()
API 繪製出來。可是若是你想更進一步,經過 getImageData()
方法獲取圖片的完整的像素信息,則多半會出錯。前端
舉例來講,使用下面代碼獲取 github 上的本身頭像圖片信息:vue
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var img = new Image(); img.onload = function () { context.drawImage(this, 0, 0); context.getImageData(0, 0, this.width, this.height); }; img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';
結果在 Chrome 瀏覽器下顯示以下錯誤:nginx
Uncaught DOMException: Failed to execute ‘getImageData’ on ‘CanvasRenderingContext2D’: The canvas has been tainted by cross-origin data.git
Firefox 瀏覽器錯誤爲:github
SecurityError: The operation is insecure.web
若是使用的是 canvas.toDataURL()方法,則會報:ajax
Failed to execute ‘toDataURL’ on ’HTMLCanvasElement’: Tainted canvased may not be exportedcanvas
緣由其實都是同樣的,跨域致使。後端
那有沒有什麼辦法能夠解決這個問題呢?
能夠試試 crossOrigin
屬性。
在 HTML5 中,有些元素提供了支持 CORS(Cross-Origin Resource Sharing)(跨域資源共享)的屬性,這些元素包括 ,
,`` 等,而提供的屬性名就是 crossOrigin
屬性。
所以,上面的跨域問題能夠這麼處理:
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var img = new Image(); img.crossOrigin = ''; img.onload = function () { context.drawImage(this, 0, 0); context.getImageData(0, 0, this.width, this.height); }; img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';
增長一個 img.crossOrigin = ''
便可,雖然 JS 代碼這裏設置的是空字符串,實際上起做用的屬性值是 anonymous
。
crossOrigin
能夠有下面兩個值:
關鍵字 | 釋義 |
---|---|
anonymous | 元素的跨域資源請求不須要憑證標誌設置。 |
use-credentials | 元素的跨域資源請求須要憑證標誌設置,意味着該請求須要提供憑證。 |
其中,只要 crossOrigin
的屬性值不是 use-credentials
,所有都會解析爲 anonymous
,包括空字符串,包括相似 'abc'
這樣的字符。
例如:
img.crossOrigin = 'abc'; console.log(img.crossOrigin); // 結果是'anonymous'
另外還有一點須要注意,那就是雖然沒有 crossOrigin
屬性,和設置 crossOrigin="use-credentials"
在默認狀況下都會報跨域出錯,可是性質上卻不同,二者有較大區別。
crossOrigin 兼容性
IE11+(IE Edge),Safari,Chrome,Firefox 瀏覽器均支持,IE9 和 IE10 會報 SecurityError 安全錯誤,以下截圖:
crossOrigin 屬性爲何能夠解決資源跨域問題?
crossOrigin=anonymous
相對於告訴對方服務器,你不須要帶任何非匿名信息過來。例如 cookie,所以,當前瀏覽器確定是安全的。
就比如你要去別人家裏拿一件衣服,crossOrigin=anonymous
相對於告訴對方,我只要衣服,其餘都不要。若是不說,可能對方在衣服裏放個竊聽的工具什麼的,就不安全了,瀏覽器就會阻止。
IE10 瀏覽器不支持 crossOrigin 怎麼辦?
咱們請求圖片的時候,不是直接經過 new Image()
,而是藉助 ajax 和 URL.createObjectURL()
方法曲線救國。
代碼以下:
var xhr = new XMLHttpRequest(); xhr.onload = function () { var url = URL.createObjectURL(this.response); var img = new Image(); img.onload = function () { // 此時你就可使用canvas對img隨心所欲了 // ... code ... // 圖片用完後記得釋放內存 URL.revokeObjectURL(url); }; img.src = url; }; xhr.open('GET', url, true); xhr.responseType = 'blob'; xhr.send();
此方法不只 IE10 瀏覽器 OK,本來支持 crossOrigin 的諸位瀏覽器也是支持的。
也就多走一個 ajax 請求,還能夠!
根據,根據實踐發現,在 IE 瀏覽器下,若是請求的圖片過大,幾千像素那種,圖片會加載失敗,我猜是超過了 blob 尺寸限制。
後來採用的解決方案是:把圖片下載到本地(前端或者是後端均可以,最後採用我前端來作)
getAvator(user, func) { window.URL = window.URL || window.webkitURL; // Take care of vendor prefixes. var xhr = new XMLHttpRequest(); xhr.open('GET', user.avatar, true); xhr.responseType = 'blob'; xhr.send() xhr.onload = function(e) { const {target} = e const {status, response, readyState} = target if (readyState == 4 && status == 200) { var blob = response; var img = document.createElement('img'); img.classList.add("avatar") var reader = new window.FileReader(); reader.readAsDataURL(blob); reader.onloadend = function() { var base64data = reader.result; img.src = base64data; }; func && func(img) } }; },
如 PHP 添加響應頭信息,*
通配符表示容許任意域名:
header("Access-Control-Allow-Origin: *");
或者指定域名:
header("Access-Control-Allow-Origin: www.zhangxinxu.com");