caiyunyi/utils/imagePreloader.js
ylj20011123 a289ea61b5 update
2025-09-12 18:08:40 +08:00

176 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 图片预加载工具类 - 针对微信小程序优化
*/
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