Bootstrap

继上一篇,设置弹框次数以及自适应图片弹框,部分机型(vivo)老手机不显示的问题

上一篇写的本来测试好多型号都无事, 今天下午公司的战斗机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>

完整代码 解决~

;