Journal
在 iOS Safari 把套牌截成圖:canvas 被 CORS 汙染的那個坑
怪乙口(GEAIKOU) 套牌工具的「匯出成圖」,在 iOS Safari 上踩到的一個坑。這篇照踩的順序記。
第一版是最天真的寫法:把套牌的 DOM 畫進 canvas toDataURL 下載。桌機一切正常。丟上 iPhone toDataURL 當場拋 SecurityError,一張圖都出不來。
翻了才知道是 canvas tainting(畫布汙染):canvas 上只要畫過一張沒有 CORS 授權的跨來源圖片,整塊 canvas 就被瀏覽器標記成「受汙染」,toDataURL 從此拒絕輸出。而 iOS Safari 對這件事又比桌機嚴格。卡圖全掛在 CDN 上,張張跨來源——等於每次都踩。
方向因此變成「截圖前,先把畫面裡的外部圖片就地換成 base64」。能穩定跑的版本,是照這個順序一層層退:
- 先試從已經在畫面上的那張
<img>提取——用canvas.drawImage畫進暫存 canvas 再toDataURL。它用的是瀏覽器已快取的像素,不必重新 fetch,iOS 相容性最好。 - 如果還是因為汙染拋錯,用
try/catch接住,退到 fetch:fetch(src, { mode: 'cors' })拿 blob,FileReader轉 base64。 - 兩條都失敗,就保留原圖 URL——至少別讓一張圖搞垮整次匯出。
try {
ctx.drawImage(img, 0, 0)
return canvas.toDataURL('image/png')
} catch {
// SecurityError: canvas tainted(CORS 未支援)→ 交給 fetch fallback
return null
}
以為這樣就結束了,結果截出來的圖有時還是舊的。base64 塞回 img.src 之後,瀏覽器要時間 decode、重繪,我截得太快,截到還沒換完的畫面。補上「等兩個 animation frame 再截」(double rAF),畫面才真的換上去。
canvas 匯出在電腦版會過,不代表 iOS 會過。要上手機,一開始就假設每張跨來源圖都會汙染 canvas,退路和 paint 時序先留好。