iT邦幫忙

3

Javascript:html2Canvas在forEach中執行順序異常問題

我的情境是這樣的

我的畫面上
有固定的3個component區塊
顯示著3種不太一樣的畫面

例如:

<component1 id="show1" :list="list1"></component1>
<component2 id="show2" :list="list2"></component1>
<component3 id="show3" :list="list3"></component1>

<button>列印</button>

3個component有屬於自己要塞的內容(list1.2.3)
這3個內容會由另一個比較複雜的陣列分析出來
暫定叫做dataList = [{...}, {...}]
內容大概長這樣

dataList = [
    {
        type:...,
        list:...,
    },
    {
        type:...,
        list:...,
    }
    ...
    ...
]

接下來的重點是
執行javascript跑dataList的forEach
然後判斷相關的參數來分別將資料丟到對應的list1.2.3
並將相關區塊的畫面個別轉成base64
好讓我最後可以統一印出來

例如:

let base64List = [];
let elementId = "";
let printList = [];
dataList.forEach(function(datas) {
    /*執行一些資料判斷*/
    if (datas.type = /*執行一些資料判斷*/) {
        elementId = "show1";
        this.list1 = data.list;
    } else if (datas.type = /*執行一些資料判斷*/) {
        elementId = "show2";
        this.list2 = data.list;
    } else if (datas.type = /*執行一些資料判斷*/) {
        elementId = "show3";
        this.list3 = data.list;
    }
    
    if (elementId !== "") {
        html2Canvas(document.getElementById(elementId), {
            allowTaint: true,
            useCORS: true,
            onclone: function (clonedDoc) {
            clonedDoc.getElementById(elementId).style.visibility = 'visible';
            }
        }).then(function (canvas) {
            let base64String = canvas.toDataURL('image/png', 1.0);
            base64List.push(base64String.replace(/^data:.*?;base64,/, ""));
        }).catch((error) => {
            alert(error);
        });
    }
})
    
printList.push({
    baseList: base64List
})

console.log(printList)

上面的例子最後的printList看似多餘的
是因為我想說簡單敘述問題
事實上外面還有一層迴圈
實際上的printList長這樣

printList = [
    {
        ip: 192.....110,
        baseList: [
            "base64",
            "base64",
            ...
            ...
        ]
    },
    {
        ip: 192.....111,
        baseList: [
            "base64",
            "base64",
            ...
            ...
        ]
    },
    ...
    ...
]

功能目的是要讓個別的資料在不同的印表機列印

我的問題在畫面轉base64那一大段
他的執行順序會forEach跑完才開始跑html2Canvas這段的then
怪的是
我本以為資料會不完整
沒想到
printList在forEach跑完後成功建立了
後面執行的html2Canvas卻持續對base64List這個變數進行更新
雖然最後所有的base64List都塞在printList的第一個元素裡

不知道有沒有人有遇過類似的問題
是否有解法

fillano iT邦超人 1 級 ‧ 2021-02-10 01:48:40 檢舉
所以你的問題在怎樣讓base64List塞在正確的printList元素裡?這部分你怎麼做的?
@fillano
這部分
我沒有把全部的回圈打出來
事實上
在上面那個範例外層還有一個for迴圈在跑那個ip
並在那一層的最後才加上上面範例部分
printList.push({
ip: //外層迴圈ip
baseList: base64List
})
至於base64部分才是我上述範例的回圈
在html2Canvas那邊會開始塞資料到component
在then那邊
然後截圖取得base64
最後push到base64List裏

等回圈都跑完才丟base64List到相對應的ip中那個baseList裡面

問題在於
會出現for回圈跑完了
那個html2Canvas才開始跑畫面
導致最後全部的base64都塞到第一組ip所屬的baseList裏

1 個回答

1
DanSnow
iT邦新手 1 級 ‧ 2021-02-09 20:44:37
最佳解答

js 的執行順序本來就是先把目前的部份執行完後才跑去執行非同步的程式 (你說的 then 的部份,它那邊回傳的是一個 Promise),不知道你有沒有用過 async/await ,有的話你可以試著這樣:

for (const data of dataList) {
  this.list = data // 設定資料的部份
  await Vue.nextTick() // 等待 Vue 更新畫面
  const canvas = await html2canvas(...) // 取得截圖
}

nextTick 的使用可以去看 Vue 的文件

沒用過 async/await 要用 then 的話也是可以,只是比較麻煩:

dataList.reduce((prev, data) => {
  return prev.then(() => {
    this.list = data
    return Vue.nextTick()
  }).then(() => {
      return html2canvas(...)
  }).then((canvas) => {
    // ... 取得 base64
  })
}, Promise.resolve())

我以你下面then的方法做參考
改了一點你的寫法
成功達到我要的目的了
謝謝~

我要發表回答

立即登入回答