Vue?處理異步加載順序問(wèn)題之如何在Konva中確保文本在圖片之上顯示
Vue 處理異步加載順序問(wèn)題:在Konva中確保文本在Konva之上顯示
在使用Konva開(kāi)發(fā)應(yīng)用時(shí),我們經(jīng)常會(huì)遇到需要將文本繪制在圖片之上的情況。一個(gè)常見(jiàn)的問(wèn)題是,由于圖像加載是異步的,文本有時(shí)會(huì)顯示在圖片下方。這篇博客將總結(jié)如何正確處理這種異步加載順序問(wèn)題。
我之前寫(xiě)過(guò)一篇博客,主要是為了說(shuō)明如何通過(guò)父子組件來(lái)控制Konva組件間的先后繪制順序,利用了Vue的生命周期(自定義父子組件mounted執(zhí)行順序)。這種方法適用于將Konva組件分開(kāi)到不同的Vue組件中,通過(guò)Vue的生命周期來(lái)確保正確的繪制順序。然而,這種方法并不適用于所有情況,比如說(shuō)必須在同一個(gè)文件中編寫(xiě),一起調(diào)用,或者都在setup語(yǔ)法糖中等情況。本文將探討在這些情況下如何確保文本在圖片之上顯示。
問(wèn)題描述
我們希望在繪制圖片后,再在圖片上方繪制文本。一個(gè)簡(jiǎn)單的代碼片段如下:
for (let i = 0; i < 4; i++) { const geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); }
初步實(shí)現(xiàn)
我們實(shí)現(xiàn)了兩個(gè)函數(shù):addWidgetImgToLayer
用于加載圖片,addShapesToLayer
用于添加Shapes(這里添加了Text)。
const addWidgetImgToLayer = (geometry, src, listening = false, parent, eventType, eventFunc, callback) => { const layer = konvaStore.layers['consoleLayer']; const imageObj = new Image(); let konvaImage = null; imageObj.onload = () => { konvaImage = new Konva.Image({ ...geometry, image: imageObj, listening: listening, }); if (eventType && eventFunc) { konvaImage.on(eventType, eventFunc); } if (parent) { parent.add(konvaImage); } else { layer.add(konvaImage); } layer.batchDraw(); if (callback) { callback(); } }; imageObj.src = src; return imageObj; }; const addShapesToLayer = (geometry, type, listening = false, parent, text) => { const layer = konvaStore.layers['consoleLayer']; let shape = null; if (type === 'text') { shape = new Konva.Text({ ...geometry, listening: listening, text: text, fontSize: 20, align: 'center', verticalAlign: 'middle', }); } if (parent) { parent.add(shape); } else { layer.add(shape); } layer.batchDraw(); return shape; };
異步問(wèn)題
這里的關(guān)鍵在于imageObj.onload
回調(diào)函數(shù)。圖片加載是異步的,代碼不會(huì)等待圖片加載完成才執(zhí)行接下來(lái)的語(yǔ)句。因此,必須確保回調(diào)函數(shù)在圖片加載完成后才執(zhí)行添加文本的操作。
嘗試1:直接回調(diào)
我們首先嘗試使用回調(diào)來(lái)確保順序執(zhí)行:
for (let i = 0; i < 4; i++) { const geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); }
然而,這種方式下,我們遇到了無(wú)法傳遞參數(shù)的問(wèn)題。在回調(diào)函數(shù)中,無(wú)法訪問(wèn)循環(huán)中的變量geometry
和group
。
嘗試2:在函數(shù)中寫(xiě)回調(diào)
我們嘗試在addWidgetImgToLayer
函數(shù)中編寫(xiě)回調(diào),但放在了imageObj.onload
之外:
const addWidgetImgToLayer = (geometry, src, listening = false, parent, eventType, eventFunc, callback) => { const layer = konvaStore.layers['consoleLayer']; const imageObj = new Image(); let konvaImage = null; imageObj.onload = () => { konvaImage = new Konva.Image({ ...geometry, image: imageObj, listening: listening, }); if (eventType && eventFunc) { konvaImage.on(eventType, eventFunc); } if (parent) { parent.add(konvaImage); } else { layer.add(konvaImage); } layer.batchDraw(); }; if (callback) { callback(); } imageObj.src = src; return imageObj; };
這導(dǎo)致了文本依然在圖片下方的問(wèn)題,因?yàn)榛卣{(diào)函數(shù)立即執(zhí)行,而不是等待圖片加載完成再執(zhí)行。分析發(fā)現(xiàn),問(wèn)題依然是異步加載的問(wèn)題,即使解決了參數(shù)傳遞問(wèn)題,這種方法也不能正確得到想要的結(jié)果。
嘗試3:將回調(diào)移至imageObj.onload
內(nèi)部
我們意識(shí)到了JavaScript的異步執(zhí)行機(jī)制。在函數(shù)嵌套的情況下,異步函數(shù)會(huì)在調(diào)用堆棧清空后才執(zhí)行。我們需要確?;卣{(diào)函數(shù)在圖片加載完成后才執(zhí)行。
const addWidgetImgToLayer = (geometry, src, listening = false, parent, eventType, eventFunc, callback) => { const layer = konvaStore.layers['consoleLayer']; const imageObj = new Image(); let konvaImage = null; imageObj.onload = () => { konvaImage = new Konva.Image({ ...geometry, image: imageObj, listening: listening, }); if (eventType && eventFunc) { konvaImage.on(eventType, eventFunc); } if (parent) { parent.add(konvaImage); } else { layer.add(konvaImage); } layer.batchDraw(); if (callback) { callback(); } }; imageObj.src = src; return imageObj; }; // 使用 addWidgetImgToLayer 并確保回調(diào)在圖片加載完成后執(zhí)行 for (let i = 0; i < 4; i++) { const geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; // 這里可以使用兩種不同的實(shí)現(xiàn)方式,現(xiàn)在是比較簡(jiǎn)潔的格式,下面會(huì)詳細(xì)說(shuō)明 addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); }
比較兩種實(shí)現(xiàn)方式
在解決這個(gè)問(wèn)題時(shí),我們還可以采用兩種不同的實(shí)現(xiàn)方式:
方式1:使用立即執(zhí)行函數(shù)表達(dá)式(IIFE)
addWidgetImgToLayer(dispBtnGeometrys[i], './img/b.png', true, group, 'click', () => { }, (function (geo, grp, idx) { return function () { addShapesToLayer(geo, 'text', false, grp, `${idx}`); }; })(dispBtnGeometrys[i], group, i + 1));
這種寫(xiě)法使用了一個(gè)立即執(zhí)行函數(shù)表達(dá)式(IIFE
),這個(gè)函數(shù)會(huì)立即執(zhí)行并返回一個(gè)新的函數(shù)。通過(guò)這種方式,可以捕獲循環(huán)中的當(dāng)前變量狀態(tài),并在異步回調(diào)中使用。
方式2:直接傳遞回調(diào)
for (let i = 0; i < 4; i++) { let geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); }
這種寫(xiě)法直接傳遞了一個(gè)回調(diào)函數(shù)給addWidgetImgToLayer
。通過(guò)使用let
聲明來(lái)確保每次循環(huán)迭代中創(chuàng)建一個(gè)新的塊作用域,變量捕獲是正確的。
主要區(qū)別
變量捕獲:
- 第一種寫(xiě)法通過(guò)IIFE來(lái)捕獲當(dāng)前循環(huán)迭代中的變量狀態(tài),確保異步回調(diào)中使用的是當(dāng)前迭代的值。
- 第二種寫(xiě)法直接傳遞回調(diào),如果使用
let
聲明或其他方法,能正確捕獲每次迭代中的變量狀態(tài)。
代碼可讀性:
第一種寫(xiě)法較為復(fù)雜,但能夠確保異步回調(diào)中的變量正確捕獲當(dāng)前狀態(tài)。
第二種寫(xiě)法更簡(jiǎn)潔,但需要確保使用let
聲明或其他方法正確捕獲變量,而不能使用var(此處使用到的是循環(huán)變量i
)。
在JavaScript中,var
聲明的變量在函數(shù)作用域內(nèi)是共享的,這意味著在異步回調(diào)中,它們的值可能會(huì)變成最后一次迭代的值。這會(huì)導(dǎo)致我們期望的結(jié)果不正確。而let
聲明的變量在每次循環(huán)迭代中都會(huì)創(chuàng)建一個(gè)新的塊作用域,從而確保異步回調(diào)中捕獲的是當(dāng)前迭代的值。
示例解釋
使用 var
聲明的問(wèn)題
for (var i = 0; i < 4; i++) { const geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { console.log(i); // 可能會(huì)輸出4,而不是期望的0, 1, 2, 3,可能在其他位置被修改過(guò)了,它們指向同一個(gè)地址 addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); }
因?yàn)?code>var聲明的變量
i
在函數(shù)作用域內(nèi)是共享的(有點(diǎn)像Python),所以在異步回調(diào)中,i
的值可能是循環(huán)結(jié)束時(shí)的值(4),而不是期望的0, 1, 2, 3。
使用 let
聲明解決問(wèn)題
for (let i = 0; i < 4; i++) { let geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { console.log(i); // 輸出期望的0, 1, 2, 3,使用let聲明變量不會(huì)出現(xiàn)問(wèn)題 addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); }
let
聲明的變量i
在每次循環(huán)迭代中都會(huì)創(chuàng)建一個(gè)新的塊作用域,因此在異步回調(diào)中,i
的值是當(dāng)前迭代的值,確保輸出的是期望的0, 1, 2, 3。
其他捕獲變量的方法
另一種確保變量捕獲正確的方法是使用立即執(zhí)行函數(shù)表達(dá)式(IIFE):
for (var i = 0; i < 4; i++) { (function(i) { let geometry = { x: 100 + (i % 2 === 0 ? 0 : 150), y: 50 + (i < 2 ? 0 : 100), width: 100, height: 100, }; addWidgetImgToLayer(geometry, './img/b.png', true, group, 'click', () => {}, () => { console.log(i); // 輸出期望的0, 1, 2, 3,可以理解為使用IIFE的話(huà)開(kāi)辟空間事會(huì)先對(duì)使用到的數(shù)據(jù)進(jìn)行了值拷貝 addShapesToLayer(geometry, 'text', false, group, `${i + 1}`); }); })(i); }
通過(guò)IIFE,每次循環(huán)迭代都會(huì)創(chuàng)建一個(gè)新的函數(shù)作用域,確保異步回調(diào)中的變量是當(dāng)前迭代的值。
結(jié)論
在第二種寫(xiě)法中,通過(guò)使用let
聲明或IIFE,確保異步回調(diào)函數(shù)中捕獲的變量值是當(dāng)前迭代的值,而不是循環(huán)結(jié)束后的值。這是確保變量正確捕獲和代碼正確執(zhí)行的關(guān)鍵。
總結(jié)
在處理Konva中的異步加載順序問(wèn)題時(shí),確保在圖像加載完成后再添加其他元素是關(guān)鍵。通過(guò)將回調(diào)函數(shù)放在imageObj.onload
中,并正確處理變量捕獲,我們可以確保文本總是繪制在圖片之上。這不僅解決了顯示順序的問(wèn)題,也為未來(lái)的調(diào)試提供了明確的方向。
到此這篇關(guān)于Vue 處理異步加載順序問(wèn)題之如何在Konva中確保文本在圖片之上顯示的文章就介紹到這了,更多相關(guān)Vue 異步加載順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue?組件異步加載方式(按需加載)
- 解決vue動(dòng)態(tài)路由異步加載import組件,加載不到module的問(wèn)題
- 詳解vue-router的Import異步加載模塊問(wèn)題的解決方案
- vue+echarts實(shí)現(xiàn)動(dòng)態(tài)繪制圖表及異步加載數(shù)據(jù)的方法
- vue awesome swiper異步加載數(shù)據(jù)出現(xiàn)的bug問(wèn)題
- vue異步加載高德地圖的實(shí)現(xiàn)
- vue+webpack實(shí)現(xiàn)異步加載三種用法示例詳解
- 詳解Vue-Cli 異步加載數(shù)據(jù)的一些注意點(diǎn)
- Javascript vue.js表格分頁(yè),ajax異步加載數(shù)據(jù)
相關(guān)文章
Vue中scrollIntoView()方法詳解與實(shí)際運(yùn)用舉例
這篇文章主要給大家介紹了關(guān)于Vue中scrollIntoView()方法詳解與實(shí)際運(yùn)用舉例的相關(guān)資料,該scrollIntoView()方法將調(diào)用它的元素滾動(dòng)到瀏覽器窗口的可見(jiàn)區(qū)域,需要的朋友可以參考下2023-12-12動(dòng)畫(huà)詳解Vue3的Composition?Api
為讓大家更好的理解Vue3的Composition?Api本文采用了詳細(xì)的動(dòng)畫(huà)演繹,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07解決vue3傳屬性時(shí)報(bào)錯(cuò)[Vue?warn]:Component?is?missing?template?or
這篇文章主要給大家介紹了關(guān)于解決vue3傳屬性時(shí)報(bào)錯(cuò)[Vue?warn]:Component?is?missing?template?or?render?function的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01基于Vue.js實(shí)現(xiàn)一個(gè)完整的登錄功能
在現(xiàn)代Web應(yīng)用中,用戶(hù)登錄功能是一個(gè)核心模塊,它不僅涉及到用戶(hù)身份驗(yàn)證,還需要處理表單驗(yàn)證、狀態(tài)管理、接口調(diào)用等多個(gè)環(huán)節(jié),本文將基于一個(gè)Vue.js項(xiàng)目中的登錄功能實(shí)現(xiàn),深入解析其背后的技術(shù)細(xì)節(jié),幫助開(kāi)發(fā)者更好地理解和實(shí)現(xiàn)類(lèi)似功能,需要的朋友可以參考下2025-02-02Vue3中的createGlobalState用法及示例詳解
createGlobalState 是 Vue 3 中一種管理全局狀態(tài)的簡(jiǎn)便方式,通常用于管理多個(gè)組件間共享的狀態(tài),由 @vueuse/core 提供的,允許創(chuàng)建一個(gè)響應(yīng)式的全局狀態(tài),本文給大家介紹了Vue3中的createGlobalState用法及示例,需要的朋友可以參考下2024-10-10在vue中獲取token,并將token寫(xiě)進(jìn)header的方法
今天小編就為大家分享一篇在vue中獲取token,并將token寫(xiě)進(jìn)header的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue3輸入框生成的時(shí)候如何自動(dòng)獲取焦點(diǎn)詳解
記錄一下自己最近開(kāi)發(fā)vue3.0的小小問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于vue3輸入框生成的時(shí)候如何自動(dòng)獲取焦點(diǎn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09