This commit is contained in:
ylj20011123 2025-09-15 09:27:13 +08:00
parent a289ea61b5
commit 178c3a8179
56 changed files with 185 additions and 112 deletions

View File

@ -1,59 +1,127 @@
<template>
<view class="lazy-image-wrapper" :style="{ width: width, height: height }">
<image :src="optimizedSrc" :style="{ width: width, height: height, opacity: imageLoaded ? 1 : 0 }" :mode="mode"
@load="onImageLoad" @error="onImageError" :lazy-load="false" :show-menu-by-longpress="false" />
<view class="lazy-image-wrapper" :style="wrapStyle">
<!-- 仅在可视区才真正挂载 image避免无意义的下载+解码 -->
<image v-if="visible" class="img" :src="realSrc" :mode="mode" :lazy-load="true" webp="true"
show-menu-by-longpress="false" :style="{ opacity: imageLoaded ? 1 : 0 }" @load="onImageLoad"
@error="onImageError" />
<view v-if="!imageLoaded && !showError" class="placeholder">
<view class="loading-bg"></view>
</view>
<view v-if="showError" class="error-placeholder">
<image src="/static/images/home/defaultIcon.png" :style="{ width: width, height: height }" :mode="mode" />
<image class="fallback" src="/static/images/home/defaultIcon.png" :mode="mode" />
</view>
</view>
</template>
<script>
export default {
props: {
src: {
type: String,
default: ''
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '100%'
},
mode: {
type: String,
default: 'aspectFill'
/** ===== 全局简单并发池(同域连接数受限时很有用) ===== */
const POOL_MAX = 6;
const queue = [];
let loadingCount = 0;
function schedule(task) {
if (loadingCount < POOL_MAX) {
loadingCount++;
task(() => {
loadingCount--;
const next = queue.shift();
if (next) schedule(next);
});
} else {
queue.push(task);
}
}
export default {
name: 'LazyImage',
props: {
src: { type: String, default: '' },
width: { type: String, default: '100%' },
height: { type: String, default: '100%' },
mode: { type: String, default: 'aspectFill' },
/** 后端/CDN若支持按需裁剪可传如 'w=360' 自动拼接到 url 上 */
query: { type: String, default: '' },
},
data() {
return {
imageLoaded: false,
showError: false
}
showError: false,
visible: false,
retry: 0,
io: null,
release: null,
};
},
computed: {
wrapStyle() {
return `width:${this.width};height:${this.height};`;
},
optimizedSrc() {
//
return this.src || ''
if (!this.src) return '';
if (!this.query) return this.src;
const joiner = this.src.includes('?') ? '&' : '?';
return `${this.src}${joiner}${this.query}`;
},
// <image> src
realSrc() {
return this.visible ? this.optimizedSrc : '';
},
},
mounted() {
// visible=true
// #ifdef MP-WEIXIN
this.io = this.createIntersectionObserver();
this.io.relativeToViewport({ top: 200, bottom: 200 }).observe('.lazy-image-wrapper', (res) => {
const inView = res?.intersectionRatio > 0;
if (inView && !this.visible) {
this.visible = true;
//
schedule((done) => {
this.release = done;
// realSrc
});
} else if (!inView && this.visible && this.imageLoaded) {
//
this.visible = false;
this.imageLoaded = false;
this.showError = false;
this.retry = 0;
}
});
// #endif
},
beforeDestroy() {
if (this.io) this.io.disconnect();
},
methods: {
onImageLoad() {
this.imageLoaded = true
this.showError = false
this.imageLoaded = true;
this.showError = false;
if (this.release) this.release();
},
onImageError() {
this.showError = true
this.imageLoaded = false
}
}
if (this.release) this.release();
if (this.retry < 2) {
// 退/CDN 499/
this.retry++;
const delay = 200 * Math.pow(2, this.retry); // 200ms, 400ms
setTimeout(() => {
//
this.imageLoaded = false;
this.showError = false;
schedule((done) => {
this.release = done;
// realSrc
// this.visible = false;
// this.$nextTick(() => (this.visible = true));
});
}, delay);
} else {
this.showError = true;
}
},
},
};
</script>
<style lang="less" scoped>
@ -61,42 +129,47 @@ export default {
position: relative;
overflow: hidden;
.placeholder {
position: absolute;
top: 0;
left: 0;
.img,
.fallback {
display: block;
width: 100%;
height: 100%;
transition: opacity .25s ease;
will-change: opacity;
}
.placeholder,
.error-placeholder {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
background: #f5f5f5;
z-index: 1;
}
.error-placeholder {
z-index: 2;
}
.loading-bg {
width: 100%;
height: 100%;
background-color: #f0f0f0;
background: linear-gradient(90deg, rgba(0, 0, 0, .06), rgba(0, 0, 0, .12), rgba(0, 0, 0, .06));
background-size: 200% 100%;
animation: shimmer 1.2s infinite linear;
border-radius: 4rpx;
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
.error-placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
z-index: 2;
100% {
background-position: 200% 0;
}
image {
display: block;
transition: opacity 0.2s;
}
}
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long