不須要任何依賴的圖片加載錯誤處理的工具類load-image.js

需求的誕生:

先簡單介紹一下業務場景,咱們的項目是一個微博輿情分析系統,能夠根據用戶設置的關鍵字監測相關微博輿情,並進行實時推送。監測範圍涵蓋境內和境外微博平臺(境內:新浪、騰訊,境外:twitter)。瀏覽器

由於訪問境外的微博須要通過代理轉發,致使常常有圖片加載不出來,嚴重影響用戶觀感。此時就須要一個錯誤重試的處理,不要讓界面上出現這個圖片加載錯誤的圖標,以下所示: 函數

思路:

經過外部傳進來的表明圖片來源的參數,判斷是不是境外圖片。若是是,則先顯示一張默認圖片,等圖片加載成功後再替換默認圖片。若是圖片加載錯誤了,間隔一段時間進行重試,直到加載成功,或失敗次數大於設定的最大閾值時中止。ui

預備知識:

圖片加載主要有三種狀態:成功、失敗、加載中(pendding),這個能夠在控制檯的NetWork裏面看到。url

Image對象有一個屬性complete,可返回瀏覽器是否已完成對圖像的加載。complete爲true表示圖片已經加載完成,爲false則有兩種狀況,一是加載失敗,而是正在加載中尚未結果。

開始擼:

先寫一個函數,裏面定義圖片加載失敗後的最大重試次數maxCount,初始化錯誤重試次數,並對圖片url進行一個非空驗證spa

/** * @param imageUrl {string} 圖片URL * @param callBack {function} 圖片加載成功後的回調(參數:{imageUrl}) */
function loadImage(imageUrl, callBack) {
  if (!imageUrl) {
    return callBack({
      imageUrl: ''
    });
  }
  const maxCount = 5; // 最大重試次數
  let count = 0;      // 重試次數
  
  // 驗證圖片是否加載成功
  validateImage(imageUrl, callBack, maxCount, count);
}
複製代碼

接下來就是核心代碼validateImage函數的實現了, 首先定義好函數內部的變量代理

function validateImage(imageUrl, callBack, maxCount, count) {
  var img = new Image(),   // 建立一個image對象
    timeId,
    timer,
    finished,              // 圖片加載成功標誌位
    time = 40,             // 觀察圖片狀態的定時器執行間隔
    timeCount = 0,         // 觀察圖片狀態的定時器執行次數
    totalTime = 0,         // 觀察圖片狀態的定時器累計執行時間
    errTimeout = 1000;     // 失敗重試的延時
複製代碼

而後給img.src賦值,此時圖片就開始加載了code

img.src = imageUrl;
  count++;
複製代碼

img.complete判斷圖片是否加載完畢,爲true,則表示圖片已加載成功,直接執行回調函將正確的圖片路徑返回cdn

if (img.complete) {
    callBack({
      imageUrl: imageUrl,
    });
  }
複製代碼

若是img.complete爲false,則須要分狀況處理。對象

狀況一:圖片加載中(pendding狀態),咱們須要定時去看圖片狀態,直到圖片加載成功或者失敗,用onload事件或onerror事件去捕獲。
然而這個地方有個大坑,什麼坑呢?
圖片有可能一直處於pendding狀態,此時是不會有任何返回值的,因此沒有辦法用onloadonerr事件捕獲,定時器一直重複執行,內存狂飆!blog

else {
        if(totalTime < 60000){
          timeId = setTimeout(function () {
            timer();
            timeCount++;
            // 每次執行間隔是上次執行間隔的兩倍
            time = 40*Math.pow(2,timeCount);
            totalTime += time;
          }, time);
        }else{
          clearTimeout(timeId);
        }
      }
    };
    // 執行觀察定時器
    timeId = setTimeout(function () {
      timer();
      totalTime += time;
    }, time);
複製代碼

此時若是圖片加載成功或失敗則會被onload捕獲或onerr捕獲。圖片加載成功進入到onload裏面,執行回調並將正確的圖片路徑返回;圖片加載失敗,進入到onerror裏面,並進行重試,直到成功或者重試次數大於maxCount結束。

img.onload = function () {
      clearTimeout(timeId);
      if (!finished) {
        return callBack({
          imageUrl: imageUrl
        });
      }
    };
    img.onerror = function () {
      if (count < maxCount) {
        clearTimeout(timeId);
        // 錯誤重試間隔每次以2s遞增
        errTimeout += (count-1) * 2000;
        // 遞歸
        timeId = setTimeout(function () {
          validateImage(imageUrl, callBack, maxCount, count);
        }, errTimeout);
      } else {
        clearTimeout(timeId);
        if (!finished) {
          callBack({
            imageUrl: ''
          });
        }
      }
    };
  }
複製代碼

ok,大功告成!

寫的很差,僅供參考。謝謝你們

源碼:

/** * 驗證圖片是否加載成功 * @param imageUrl * @param callBack * @param maxCount {string} 最大輪詢次數 * @param count {string} 從新加載次數 */
function validateImage(imageUrl, callBack, maxCount, count) {
  let img = new Image(), timeId, timer, finished, time = 40, timeCount = 0, totalTime = 0, errTimeout = 1000;
  img.src = imageUrl;
  count++;
  if (img.complete) {
    callBack({
      imageUrl: imageUrl,
    });
  } else {
    timer = function () {
      if (img.width > 0 || img.height > 0) {
        finished = true;
        callBack({
          imageUrl: imageUrl
        });
      } else {
        if(totalTime < 60000){
          timeId = setTimeout(function () {
            timer();
            timeCount++;
            time = 40*Math.pow(2,timeCount);
            totalTime += time;
          }, time);
        }else{
          clearTimeout(timeId);
        }
      }
    };
    timeId = setTimeout(function () {
      timer();
      totalTime += time;
    }, time);
    img.onload = function () {
      clearTimeout(timeId);
      if (!finished) {
        return callBack({
          imageUrl: imageUrl
        });
      }
    };
    img.onerror = function () {
      if (count < maxCount) {
        clearTimeout(timeId);
        errTimeout += (count-1) * 2000;
        timeId = setTimeout(function () {
          validateImage(imageUrl, callBack, maxCount, count);
        }, errTimeout);
      } else {
        clearTimeout(timeId);
        if (!finished) {
          callBack({
            imageUrl: ''
          });
        }
      }
    };
  }
}

/** * @param imageUrl {string} 圖片URL * @param callBack {function} 圖片加載成功後的回調(參數:{imageUrl}) * @returns {*} */
function loadImage(imageUrl, callBack) {
  if (!imageUrl) {
    return callBack({
      imageUrl: ''
    });
  }
  const maxCount = 5;
  let count = 0;

  validateImage(imageUrl, callBack, maxCount, count);
}
export default loadImage;

複製代碼
相關文章
相關標籤/搜索