上一篇写的本来测试好多型号都无事, 今天下午公司的战斗机vivo横空冒出…
晕 弹框直接显示都出不来了,现在还有用这种老的机型的,但是没办法咯~ 前端遇到这种兼容性的问题就要勇于解决
主要解决了这几点:
// 添加图片加载事件
<img
v-if="imageUrl"
:src="imageUrl"
@load="handleImageLoad"
@error="handleImageError"
@click="handleImgClick"
/>
// 图片加载完成后再显示
const handleImageLoad = () => {
console.log('图片加载完成');
imageLoaded.value = true;
showPopup();
};
这确保了只有在图片真正加载完成后才显示弹框,避免了空白或闪烁。
多重渲染保护:
const showPopup = () => {
requestAnimationFrame(() => {
setTimeout(() => {
isVisible.value = true;
if (popupRef.value) {
popupRef.value.style.transform = 'translateZ(0)';
}
}, 100);
});
};
使用 requestAnimationFrame 和 setTimeout 的组合,确保在浏览器的下一帧渲染时才显示,这解决了 vivo 手机上的渲染时机问题。
显示状态控制:
const isVisible = ref(false);
const imageLoaded = ref(false);
const containerStyle = computed(() => ({
visibility: imageLoaded.value ? ‘visible’ : ‘hidden’
}));
使用独立的状态控制显示,而不是直接依赖数据加载。
硬件加速和性能优化:
.popup-container {
-webkit-transform: translateZ(0);
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000;
will-change: transform;
}
这些 CSS 属性强制启用硬件加速,提高渲染性能。
成功的关键在于:
正确的渲染时机:等待图片加载完成
可靠的显示控制:使用多重状态检查
性能优化:启用硬件加速
渲染队列:使用 requestAnimationFrame 确保正确的渲染顺序
这解决了 vivo 手机上的几个典型问题:
渲染时机不对导致的白屏
硬件加速缺失导致的显示问题
图片加载时机导致的布局问题
建议在类似场景下:
总是等待资源加载完成
使用多重渲染保护
添加必要的性能优化属性
保持良好的状态管理
<template>
<Teleport to="body">
<div v-show="isVisible" class="popup-container" :style="containerStyle">
<div class="popup-content" ref="popupRef">
<img
v-if="imageUrl"
:src="imageUrl"
@load="handleImageLoad"
@error="handleImageError"
@click="handleImgClick"
/>
<div class="close-btn" @click="handleClose">×</div>
</div>
</div>
</Teleport>
</template>
<script setup>
import { ref, getCurrentInstance, onMounted, computed } from "vue";
import { getAdList, getConfing } from "@/api/base";
const app = getCurrentInstance();
const proxy = app?.appContext.config.globalProperties;
const adPosition = proxy?.$global.AD_POSITION_HOME_POPUP;
const diaData = ref({});
const currentPopupIndex = ref(0);
const isVisible = ref(false);
const popupRef = ref(null);
const imageLoaded = ref(false);
const maxShowCount = ref(0);
const imageUrl = computed(() => {
return diaData.value[adPosition]?.[currentPopupIndex.value]?.pic || "";
});
const containerStyle = computed(() => ({
visibility: imageLoaded.value ? "visible" : "hidden",
}));
const handleImageLoad = () => {
console.log("图片加载完成===");
imageLoaded.value = true;
if (checkCanShow()) {
showPopup();
} else {
isVisible.value = false;
currentPopupIndex.value = -1;
}
};
const handleImageError = (error) => {
console.error("图片加载失败===", error);
imageLoaded.value = false;
};
const showPopup = () => {
if (!checkCanShow()) {
isVisible.value = false;
currentPopupIndex.value = -1;
return;
}
requestAnimationFrame(() => {
setTimeout(() => {
isVisible.value = true;
if (popupRef.value) {
popupRef.value.style.transform = "translateZ(0)";
}
}, 100);
});
};
// 处理图片点击
const handleImgClick = () => {
if (diaData.value?.[adPosition]?.[currentPopupIndex.value]) {
proxy?.$adRouter(diaData.value[adPosition][currentPopupIndex.value]);
}
};
// 处理关闭按钮点击
const handleClose = () => {
incrementShowCount();
// 检查是否达到最大显示次数
if (!checkCanShow()) {
isVisible.value = false; // 隐藏整个弹框(包括遮罩)
currentPopupIndex.value = -1;
return;
}
if (currentPopupIndex.value < diaData.value[adPosition]?.length - 1) {
// 切换到下一张前重置状态
imageLoaded.value = false;
currentPopupIndex.value++;
} else {
isVisible.value = false;
currentPopupIndex.value = -1;
}
};
const getTodayShowCount = () => {
const today = new Date().toDateString();
const storageKey = "popupShowCount_" + today;
return parseInt(localStorage.getItem(storageKey) || "0");
};
const incrementShowCount = () => {
const today = new Date().toDateString();
const storageKey = "popupShowCount_" + today;
const currentCount = getTodayShowCount();
localStorage.setItem(storageKey, (currentCount + 1).toString());
};
const checkCanShow = () => {
const currentCount = getTodayShowCount();
return currentCount < maxShowCount.value;
};
onMounted(async () => {
try {
// 先获取配置的最大显示次数
const configRes = await getConfing({ key: proxy.$global.ImageDialogCount });
maxShowCount.value = parseInt(configRes.data[0].configValue || "0");
console.log("最大显示次数:", maxShowCount.value);
// 检查是否可以显示
if (checkCanShow()) {
// 获取广告数据
const res = await getAdList({
regionType: [adPosition],
});
if (res.data) {
diaData.value = res.data;
console.log("广告数据获取成功:", diaData.value);
// 确保数据存在
if (diaData.value[adPosition]?.length > 0) {
currentPopupIndex.value = 0;
}
}
} else {
console.log("已达到最大显示次数");
}
} catch (error) {
cconsole.log("err的信息", error);
}
});
</script>
<style lang="scss" scoped>
.popup-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
margin: 0;
padding: 0;
-webkit-transform: translateZ(0);
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000;
will-change: transform;
}
.popup-content {
position: relative;
width: fit-content;
margin: auto;
-webkit-transform: translateZ(0);
transform: translateZ(0);
img {
display: block;
width: 80vw;
max-height: 80vh;
object-fit: contain;
-webkit-touch-callout: none;
user-select: none;
-webkit-user-select: none;
pointer-events: auto;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
}
.close-btn {
position: absolute;
top: -30px;
right: 0;
width: 30px;
height: 30px;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 20px;
color: #333;
z-index: 1000;
-webkit-tap-highlight-color: transparent;
&:active {
background: #fff;
}
}
</style>
完整代码 解决~