Stories

Detail Return Return

詳解fabric.js圖片跨域 - Stories Detail

大家好,我是vue-fabric-editor開源圖片編輯器項目的作者,很多開發者都會問我圖片跨域的問題如何處理,今天就詳細的跟大家分享一下fabric.js圖片跨域怎麼處理。

問題現象

會報兩種錯誤,要麼保存新圖片時報錯,要麼插入圖片時報錯:

  1. 圖片插入到畫布成功了,畫布保存新圖片時報錯。
  2. 圖片無法插入到畫布,直接提示CORS報錯。

image.png

image-1.png

先説結論:必須後端設置跨域,然後前端也設置跨越,才能正常將圖片插入畫布,並保存為新圖片。

跨域的設置

跨域區分前端跨域與後端跨域設置,我通過http-server模擬跨域,8080下的HTML文件 訪問8081的圖片,測試視頻,得出如下結果:

  1. 都不設置跨域:渲染正常,保存不正常。
  2. 僅前端設置跨域:渲染不正常。
  3. 僅後端設置跨域:渲染正常,保存不正常。
  4. 前後端均設置跨域:渲染正常,保存正常。

    4種插入圖片的方式

    fabric.js 提供了多種插入圖片的方式,主要流程都是將原生的Img對象轉換成fabric.Image對象,然後插入到畫布中。

  5. fabric.Image.fromURL:通過回調直接返回fabric的img對象,直接插入到畫布中。
  6. fabric.util.loadImage:通過回調返回原生img對象,轉換為fabric對象後插入畫布中。
  7. new Image()document.createElement('img'):創建原生img對象,轉換為fabric對象後插入畫布中。
  8. <img />:將頁面中的img元素,通過選擇器獲取到原生img對象,轉換為fabric對象後插入畫布中。

    // 1. 通過fabric.Image.fromURL插入圖片
    function addImgByUrl(url) {
     fabric.Image.fromURL(
         url,
         (imgEl) => {
         imgEl.set({
             left: 100,
             top: 100,
         });
         // 設置縮放
         canvas.add(imgEl);
         canvas.setActiveObject(imgEl);
         canvas.renderAll();
         },
         { crossOrigin: 'anonymous' }
     );
    }
    // 2. fabric.util.loadImage
    function addImgByUtils(url) {
     fabric.util.loadImage(url, function(catImg) {
         const mycatImg = new fabric.Image(catImg)
         mycatImg.set({ left: 180, top: 180, });
         canvas.add(mycatImg);
         canvas.setActiveObject(mycatImg);
         canvas.renderAll();
      }, null, {crossOrigin: 'Anonymous'});
    }
    
    // 3. new Image() 與 document.createElement('img')是等價的方式
    // MDN文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLImageElement/Image
    function addImgByNewImage(url) {
     const image = new Image()
     image.setAttribute('crossOrigin', 'anonymous')
     image.onload = function () {
         fabricImage = new fabric.Image(image)
         fabricImage.set({ left: 150, top: 150, });
         canvas.add(fabricImage);
         canvas.setActiveObject(fabricImage);
         canvas.renderAll();
     }
     image.src = url
    }
    function addImgByCreateTag(url) {
     const tagImg = document.createElement('img')
     tagImg.crossOrigin = 'anonymous'
     tagImg.src = url
     // 等待圖片加載完
     tagImg.onload = function() {
         const myTagImg = new fabric.Image(tagImg)
         myTagImg.set({ left: 190, top: 190, });
         canvas.add(myTagImg);
         canvas.setActiveObject(myTagImg);
         canvas.renderAll();
     }
    }
    
    // 4.<img />
    // <img id="imgTag" src="http://127.0.0.1:8081/1.png" crossorigin="anonymous" alt="">
    function addImgByTag() {
     const catImg = document.getElementById('imgTag')
     // 等待圖片加載完
     catImg.onload = function() {
         const mycatImg = new fabric.Image(catImg)
         mycatImg.set({ left: 190, top: 190, });
         canvas.add(mycatImg);
         canvas.setActiveObject(mycatImg);
         canvas.renderAll();
     }
    }

源碼實現

源碼中原生Img對象與fabric.Image對象的關係

  • new fabric.Image() 只接收原生image對象。

image-2.png

  • fabric.Image.fromURL(url) 等於 fabric.util.loadImage + new fabric.Image,在源碼內部實現了轉換的操作。

image-3.png

  • fabric.util.loadImage() 等於 fabric.util.createImage = document.createElement('img'),等同於手動創建原生img對象。

image-4.png

image-5.png

image-6.png

其他

vue-fabric-editor開源圖片編輯器項目

Add a new Comments

Some HTML is okay.