先說結論:
-
canvas.toDataURL API中用到的圖片,必須新增crossOrigin屬性設定,否則會報被汙染的canvas無法被匯出的錯誤
-
url相同,crossOrigin屬性的圖,在頁面中通過html img標籤和js-dom Image物件不管載入多少次,瀏覽器只請求伺服器一次。從快取中讀取時,多次載入也只讀取一次。
-
頁面載入多幅url相同的圖片,如果這些圖片中有些設定了跨域屬性,有的未跨域屬性,只要設定了跨域屬性的圖之後會載入沒有跨域屬性的圖,那麼最後快取的就是沒有跨域屬性的圖。
-
如果快取的是沒有跨域屬性的圖片,設定了跨域屬性的html img標籤,js-dom Image物件從快取中載入圖片,會報跨域錯誤。如果快取的是設定了跨域屬性的圖片,html img標籤,js-dom Image物件 無論是否設定跨域屬性,都可以從快取中正常載入圖片。
再看實驗過程:
1.分別載入沒有設定crossOrigin屬性的html-img和js-img圖片,呼叫canvas.toDataURL轉換data URI,執行時都會報錯-被汙染的canvas無法被匯出,這個錯誤是由於canvas使用了未設定跨域的圖片資源引起的,只有設定了crossOrigin屬性的圖片資源,才能被canvas複用。
1.1 載入沒有設定crossOrigin="anonymous"屬性的html-img圖片,執行canvas.toDataURL
<img alt="" id="html-img" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box"></div> <script> // 將圖片繪製在canvas畫布上 function convertCanvasToImage(image) { const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; canvas.getContext('2d').drawImage(image, 0, 0); // 從canvas畫布匯出圖片 const img = new Image(); img.src = canvas.toDataURL('image/png'); return img; } // 通過html-img標籤載入圖片 const htmlImg = document.querySelector('#html-img'); htmlImg.onload = function () { const img=convertCanvasToImage(this); document.querySelector('#js-canvas-box').appendChild(img); }; </script>
報如下錯誤:
1.2 載入沒有設定crossOrigin="anonymous"屬性的js-image圖片,執行canvas.toDataURL
<div id="js-canvas-box" /> <script> // 將圖片繪製在canvas畫布上 function convertCanvasToImage(image) { const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; canvas.getContext('2d').drawImage(image, 0, 0); // 從canvas畫布匯出圖片 const img = new Image(); img.src = canvas.toDataURL('image/png'); return img; } // 通過js-dom載入圖片 const jsImg = new Image(); jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { const img=convertCanvasToImage(this); document.querySelector('#js-canvas-box').appendChild(img); }; </script>
報如下錯誤:
2.分別載入設定crossOrigin屬性時html-img和js-img圖片,呼叫canvas.toDataURL執行都正常。
crossOrigin
可以有下面兩個值:
anonymous | 元素的跨域資源請求不需要憑證標誌設定。 |
use-credentials | 元素的跨域資源請求需要憑證標誌設定,意味著該請求需要提供憑證 |
只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字串。
2.1 載入設定crossOrigin屬性時html-img圖片,執行canvas.toDataURL,結果正確。
<img alt="" id="html-img" crossOrigin="" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box" /> <script> // 將圖片繪製在canvas畫布上 function convertCanvasToImage(image) { const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; canvas.getContext('2d').drawImage(image, 0, 0); // 從canvas畫布匯出圖片 const img = new Image(); img.src = canvas.toDataURL('image/png'); return img; } // 通過html-img標籤載入圖片 const htmlImg = document.querySelector('#html-img'); htmlImg.onload = function () { const img=convertCanvasToImage(this); document.querySelector('#js-canvas-box').appendChild(img); }; </script>
2.2 載入設定crossOrigin屬性時js-img圖片,執行canvas.toDataURL,結果正確。
<div id="js-canvas-box" /> <script> function convertCanvasToImage(image) { // 將圖片繪製在canvas畫布上 const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; canvas.getContext('2d').drawImage(image, 0, 0); // 從canvas畫布匯出圖片 const img = new Image(); img.src = canvas.toDataURL('image/jpg'); return img } // 通過js-dom載入圖片 const jsImg = new Image(); // 只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字串。 jsImg.crossOrigin="" jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { const image=convertCanvasToImage(jsImg); document.querySelector('#js-canvas-box').appendChild(image); }; </script>
3.再看看從快取載入,會不會報錯。啟用快取,分別載入設定crossOrigin屬性時html-img和js-img圖片,執行canvas.toDataURL,也都沒有報錯。
3.1 啟用快取,載入設定crossOrigin屬性時html-img圖片,執行canvas.toDataURL,結果正確。
<img alt="" id="html-img" crossOrigin="anonymous" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box" /> <script> function convertCanvasToImage(image) { // 將圖片繪製在canvas畫布上 const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; canvas.getContext('2d').drawImage(image, 0, 0); // 從canvas畫布匯出圖片 const img = new Image(); img.src = canvas.toDataURL('image/jpg'); return img } // 通過html-img載入圖片 const htmlImg = document.querySelector('#html-img'); htmlImg.onload = function () { const image=convertCanvasToImage(this); document.querySelector('#js-canvas-box').appendChild(image); }; </script>
3.2 啟用快取,載入設定crossOrigin屬性時js-img圖片,執行canvas.toDataURL,結果正確。
<div id="js-canvas-box" /> <script> function convertCanvasToImage(image) { // 將圖片繪製在canvas畫布上 const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; canvas.getContext('2d').drawImage(image, 0, 0); // 從canvas畫布匯出圖片 const img = new Image(); img.src = canvas.toDataURL('image/jpg'); return img } // 通過js-dom載入圖片 const jsImg = new Image(); // 只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字串。 jsImg.crossOrigin="" jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { const image=convertCanvasToImage(this); document.querySelector('#js-canvas-box').appendChild(image); }; </script>
4. 通過html-img和js-img載入url相同,crossOrigin屬性的圖,只載入一次。從快取中讀取時,也只讀取一次
4.1 html-img和js-img都未設定crossOrigin,載入同一幅圖,只載入一次。
<img alt="img" id="html-img" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box"></div> <script> const jsImg = new Image(); jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { document.querySelector('#js-canvas-box').appendChild(jsImg); }; </script>
從快取中讀取,只讀取了一次。
4.2 html-img和js-img都設定crossOrigin,載入同一幅圖,只載入一次。
<img alt="img" id="html-img" crossOrigin="" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box"></div> <script> const jsImg = new Image(); jsImg.crossOrigin="" jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { document.querySelector('#js-canvas-box').appendChild(this); }; </script>
從快取中讀取,只讀取一次。
5. 再看看通過html-img的方式載入url相同,crossOrigin屬性不同的情景,頁面載入多個url一樣的html-img圖片,如果在跨域屬性圖片之後載入了沒有跨域屬性的圖片,那麼最後快取的是未設定crossOrigin屬性的圖片,重新整理頁面,那些設定了crossOrigin屬性的圖片,從快取中載入圖片時,會報跨域錯誤
這是因為:
- 在頁面載入的過程中,圖片會被瀏覽器快取,如果再次遇到url和crossOrigin屬性相同的圖片,直接會從快取中讀取,如果url相同,crossOrigin屬性與之前快取的圖片不同,瀏覽器會重新請求,並重新快取,覆蓋之前快取的同一張圖。可是快取中的圖片跨域屬性一旦從跨域變成不跨域,之後瀏覽器便不會在覆蓋之前的快取。快取的圖片始終保持為不跨域。
- 快取的圖片如果是未設定跨域屬性的圖片,html-img標籤設定了crossOrigin屬性,從快取載入,會觸發跨域問題。快取的圖片如果是設定了跨域屬性的圖片,無論html-img標籤是否設定crossOrigin屬性,從快取載入,都不會觸發跨域問題。
5.1 最後快取的是沒有設定crossOrigin屬性的圖片, 從快取中載入時,觸發了html img標籤中設定了crossOrigin屬性圖片的跨域。
<!-- 快取的是沒有跨域屬性的圖片 --> <img alt="img-3-no" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <!-- 快取被覆蓋,快取的是有跨域屬性的圖片 --> <img alt="img-2-anonymous" crossOrigin="anonymous" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <!-- 快取再次被覆蓋,快取的是沒有跨域屬性的圖片 --> <img alt="img-3-no" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <!-- 快取中同一幅圖的跨域屬性一旦由跨域變成不跨域,之後瀏覽器不會再修改圖片的跨域屬性 --> <img alt="img-4-anonymous" crossOrigin="anonymous" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" />
1.5kb的圖片是沒有跨域屬性的圖片,1.6kb的圖片是設定了跨域屬性的圖片,從網路請求皮膚可以看到,最後請求的是沒有跨域屬性的圖片,意味著最後快取的也是沒有跨域屬性的圖片。
設定了跨域屬性的html img標籤,從快取中載入沒有跨域屬性的圖片,瀏覽器會報跨域錯誤。
5.2 最後快取的圖是設定了crossOrigin的圖片,從快取中載入時, 不會觸發html img標籤中未設定crossOrigin屬性圖片的跨域。
<!-- 快取的是沒有跨域屬性的圖片 --> <img alt="img-3-no" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <!-- 快取過,不再快取 --> <img alt="img-2-no" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <!-- 快取過,不再快取 --> <img alt="img-3-no" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <!-- 快取被覆蓋,快取的是有跨域屬性的圖片 --> <img alt="img-4-anonymous" crossOrigin="anonymous" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" />
從網路請求皮膚可以看出,最後請求的是1.6kb的設定了跨域屬性的圖片,意味著快取中最後保持的也是有跨域屬性的圖片
html img標籤即使未設定跨域屬性,也能利用快取中儲存的設定了跨域屬性的圖片,不會報錯。
6.再看看html-img和js-img混搭載入的情景, 頁面載入多個url相同的html-img和js-img混搭圖片,前面的結論依舊成立。
6.1 最後快取的是沒有設定crossOrigin屬性的圖片,從快取載入時,設定了crossOrigin屬性的圖片會報跨域錯誤。
<img alt="img" id="html-img" crossOrigin="anonymous" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box"></div> <script> const jsImg = new Image(); jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { document.querySelector('#js-canvas-box').appendChild(jsImg); }; </script>
6.2 最後快取的是設定了crossOrigin屬性的圖片,從快取載入時,不會觸發沒有設定crossOrigin屬性的圖片跨域錯誤。
<img alt="img" id="html-img" src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png" /> <div id="js-canvas-box"></div> <script> const jsImg = new Image(); jsImg.crossOrigin="anonymous" jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png'; jsImg.onload = function () { document.querySelector('#js-canvas-box').appendChild(jsImg); }; </script>