176 lines
5.1 KiB
JavaScript
176 lines
5.1 KiB
JavaScript
/**
|
||
* 图片预加载工具类 - 针对微信小程序优化
|
||
*/
|
||
|
||
class ImagePreloader {
|
||
constructor() {
|
||
this.cache = new Map() // 图片缓存
|
||
this.loadingQueue = new Map() // 正在加载的队列
|
||
this.maxConcurrent = 3 // 最大并发加载数
|
||
this.currentLoading = 0 // 当前正在加载的数量
|
||
}
|
||
|
||
/**
|
||
* 预加载图片
|
||
* @param {string} src 图片地址
|
||
* @param {Object} options 选项
|
||
* @returns {Promise}
|
||
*/
|
||
preload(src, options = {}) {
|
||
if (!src) return Promise.reject(new Error('Image src is required'))
|
||
|
||
// 如果已经缓存,直接返回
|
||
if (this.cache.has(src)) {
|
||
return Promise.resolve(src)
|
||
}
|
||
|
||
// 如果正在加载,返回现有Promise
|
||
if (this.loadingQueue.has(src)) {
|
||
return this.loadingQueue.get(src)
|
||
}
|
||
|
||
// 创建加载Promise
|
||
const loadPromise = this._loadImage(src, options)
|
||
this.loadingQueue.set(src, loadPromise)
|
||
|
||
loadPromise.finally(() => {
|
||
this.loadingQueue.delete(src)
|
||
this.currentLoading--
|
||
this._processQueue() // 处理等待队列
|
||
})
|
||
|
||
return loadPromise
|
||
}
|
||
|
||
/**
|
||
* 批量预加载图片
|
||
* @param {Array} srcList 图片地址数组
|
||
* @param {Object} options 选项
|
||
* @returns {Promise}
|
||
*/
|
||
preloadBatch(srcList, options = {}) {
|
||
if (!Array.isArray(srcList)) return Promise.reject(new Error('srcList must be an array'))
|
||
|
||
return Promise.allSettled(
|
||
srcList.map(src => this.preload(src, options))
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 实际加载图片的方法
|
||
* @private
|
||
*/
|
||
_loadImage(src, options = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
// 控制并发数量
|
||
if (this.currentLoading >= this.maxConcurrent) {
|
||
setTimeout(() => {
|
||
this._loadImage(src, options).then(resolve).catch(reject)
|
||
}, 100)
|
||
return
|
||
}
|
||
|
||
this.currentLoading++
|
||
|
||
// 优化图片URL
|
||
const optimizedSrc = this._optimizeImageUrl(src, options)
|
||
|
||
// 使用微信小程序的图片预加载API
|
||
uni.preloadPage({
|
||
url: optimizedSrc,
|
||
success: () => {
|
||
this.cache.set(src, optimizedSrc)
|
||
resolve(optimizedSrc)
|
||
},
|
||
fail: (error) => {
|
||
// 如果预加载失败,尝试直接加载
|
||
this._fallbackLoad(optimizedSrc).then(() => {
|
||
this.cache.set(src, optimizedSrc)
|
||
resolve(optimizedSrc)
|
||
}).catch(reject)
|
||
}
|
||
})
|
||
|
||
// 设置超时
|
||
setTimeout(() => {
|
||
if (!this.cache.has(src)) {
|
||
reject(new Error('Image load timeout'))
|
||
}
|
||
}, options.timeout || 8000)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 备用加载方法
|
||
* @private
|
||
*/
|
||
_fallbackLoad(src) {
|
||
return new Promise((resolve, reject) => {
|
||
const img = uni.createImage ? uni.createImage() : new Image()
|
||
img.onload = () => resolve(src)
|
||
img.onerror = reject
|
||
img.src = src
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 优化图片URL
|
||
* @private
|
||
*/
|
||
_optimizeImageUrl(src, options = {}) {
|
||
if (!src || typeof src !== 'string') return src
|
||
|
||
// 如果是本地图片或base64,直接返回
|
||
if (src.startsWith('/') || src.startsWith('data:')) return src
|
||
|
||
// 如果包含已知的压缩参数,直接返回
|
||
if (src.includes('x-oss-process') || src.includes('imageView2')) return src
|
||
|
||
const { width = 300, height = 300, quality = 80 } = options
|
||
|
||
// 针对不同CDN服务商添加压缩参数
|
||
if (src.includes('eshangtech.com')) {
|
||
// 阿里云OSS
|
||
return `${src}${src.includes('?') ? '&' : '?'}x-oss-process=image/resize,w_${width},h_${height},m_fill/quality,q_${quality}`
|
||
} else if (src.includes('qcloud.com') || src.includes('myqcloud.com')) {
|
||
// 腾讯云COS
|
||
return `${src}${src.includes('?') ? '&' : '?'}imageView2/1/w/${width}/h/${height}/q/${quality}`
|
||
} else if (src.includes('baidubce.com')) {
|
||
// 百度云BOS
|
||
return `${src}${src.includes('?') ? '&' : '?'}x-bce-process=image/resize,w_${width},h_${height}/quality,q_${quality}`
|
||
}
|
||
|
||
return src
|
||
}
|
||
|
||
/**
|
||
* 处理等待队列
|
||
* @private
|
||
*/
|
||
_processQueue() {
|
||
// 这里可以添加队列处理逻辑,目前保持简单
|
||
}
|
||
|
||
/**
|
||
* 清除缓存
|
||
*/
|
||
clearCache() {
|
||
this.cache.clear()
|
||
}
|
||
|
||
/**
|
||
* 获取缓存状态
|
||
*/
|
||
getCacheInfo() {
|
||
return {
|
||
cacheSize: this.cache.size,
|
||
loadingCount: this.loadingQueue.size,
|
||
currentLoading: this.currentLoading
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建单例
|
||
const imagePreloader = new ImagePreloader()
|
||
|
||
export default imagePreloader |