You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iPadOS 15.5 Safari中CSS opacity过渡动画失效问题求助

问题描述
  • 实现逻辑:通过给元素添加/移除hide类控制opacity在0/1之间切换,元素本身设置了0.5s的transition过渡效果。
  • 兼容表现:Mac端全平台浏览器、iPhone iOS端全平台浏览器(包括Safari)均运行正常,唯独M1芯片iPad Pro 12.9(iPadOS 15.5)端的Safari浏览器没有过渡动画。
  • 异常细节:开发者工具可观测到类名正常增删,元素显隐功能正常,只是完全没有过渡效果;同设备的Firefox、Edge、Chrome均完美运行,逻辑和效果无异常。
问题复现代码

JavaScript

const img = document.querySelector('img');

function loadImage(src) {
  return new Promise((resolve, reject) => {
    try {
      if (!src || typeof src !== 'string') throw new Error('src invalid');
      
      img.onload = () => {
        img.onload = null;
        img.onerror = null;
        resolve(img);
      }
      img.onerror = () => {
        img.onload = null;
        img.onerror = null;
        reject(img);
      }
      
      img.src = src;
    }
    catch(error) {
      reject(error);
    }
  });
}

function fadeElement(element, hide = true) {
  return new Promise((resolve, reject) => {
    try {
      if (hide && element.classList.contains('hide')) return resolve(element);
      if (!hide && !element.classList.contains('hide')) return resolve(element);
      
      if (hide) element.classList.add('hide');
      else element.classList.remove('hide');
      
      element.ontransitionend = () => {
        element.ontransitionend = null;
        resolve(element);
      }
    }
    catch(error) {
      reject(error);
    }
  });
}


async function setImage({src, alt}) {
  try {
    if (!src || typeof src !== 'string') throw new Error('src invalid');
    
    img.alt = alt || '';
    
    await fadeElement(img, true);
    await loadImage(src);
    await fadeElement(img, false);
    
  }
  catch(error) {
    console.error(error);
  }
}

setImage({src: "https://images.unsplash.com/photo-1654378550791-5360093aaf31?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNzN8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 1"})
        .then(()=> {console.log('image loaded')})
        .catch((error) => {throw error});



const interval = setInterval( () => {
  try {
        const srcs = [
          {src: "https://images.unsplash.com/photo-1654581066394-18d9549f760b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyMHx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 1"},
          {src: "https://images.unsplash.com/photo-1654561610761-dd76686ad98d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxOXx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60"},
          {src: "https://images.unsplash.com/photo-1654578870260-4fdae0cc4f6c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNHx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 3"},
          {src: "https://images.unsplash.com/photo-1654525482694-af1b5e9df569?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzOHx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60"},
          {src: "https://images.unsplash.com/photo-1654513286952-85d043b0c323?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw2OHx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 5"},
          {src: "https://images.unsplash.com/photo-1654485604630-d39cc0bab3e3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw3OHx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 6"},
          {src: "https://images.unsplash.com/photo-1654525217823-7af9e520050c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw4MXx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 7"},
          {src: "https://images.unsplash.com/photo-1654521957182-f0277b65005a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw5MXx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 8"},
          {src: "https://images.unsplash.com/photo-1654510916856-7533a3b4c368?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw5OXx8fGVufDB8fHx8&auto=format&fit=crop&w=900&q=60", alt: "image 9"},
          {src: "https://images.unsplash.com/photo-1654512378443-f14098061c83?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMDB8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60"},
          {src: "https://images.unsplash.com/photo-1654513155873-3bfc341f14ef?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMDR8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 11"},
          {src: "https://images.unsplash.com/photo-1654511794450-e708aa031c43?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMDN8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 12"},
          {src: "https://images.unsplash.com/photo-1654490762536-afdbb92c5bfb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMzB8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60"},
          {src: "https://images.unsplash.com/photo-1654423625360-31dee119c5f6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 14"},
          {src: "https://images.unsplash.com/photo-1654446138056-8996ca1e96a9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNDF8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 15"},
          {src: "https://images.unsplash.com/photo-1623333769926-a34d46b5fbdb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNDB8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60"},
          {src: "https://images.unsplash.com/photo-1654505203976-ada2d3e685f8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNDZ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 17"},
          {src: "https://images.unsplash.com/photo-1654448190693-f59b70e5ee3c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNjl8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60", alt: "image 18"},
        ];

        function randomImage(images) {
          return images[Math.floor(Math.random() * images.length)];
        }

        const newSrc = randomImage(srcs);
        setImage(newSrc)
        .then(()=> {console.log('image loaded')})
        .catch((error) => {throw error});
      }
      catch (error) {
        console.error(error);
      }
}, 6500);

CSS

div {
  height: 500px;
  width: 500px;
  position: relative;
  overflow: hidden;
}

img {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  object-fit: cover;
  object-position: center;
  transition: 0.5s ease-in-out opacity;
}
.hide {
  opacity: 0;
}

HTML

<div class="container">
<img src="" alt="">
</div>
解决方案

这是iPadOS 15.x版本Safari的已知渲染bug,触发原因是修改img的src属性时,Safari会重建该img元素的渲染层,打断正在进行/即将触发的transition,和设备芯片无直接关联。

修复分两步:

  1. 给img元素添加CSS属性强制开启独立渲染层,避免src修改时层重建打断过渡:
img {
  /* 原有属性保留 */
  -webkit-transform: translateZ(0);
  transform: translateZ(0);
  will-change: opacity;
}
  1. 修改fadeElement逻辑,在增删类名前等待一帧渲染,确保Safari能正确捕获样式变更触发过渡,同时替换事件绑定方式避免覆盖问题:
function fadeElement(element, hide = true) {
  return new Promise((resolve, reject) => {
    try {
      if (hide && element.classList.contains('hide')) return resolve(element);
      if (!hide && !element.classList.contains('hide')) return resolve(element);
      
      // 等待下一帧再修改类名,给浏览器足够的样式计算时间
      requestAnimationFrame(() => {
        if (hide) element.classList.add('hide');
        else element.classList.remove('hide');
      })
      
      const handleTransitionEnd = (e) => {
        if (e.propertyName !== 'opacity') return;
        element.removeEventListener('transitionend', handleTransitionEnd);
        resolve(element);
      }
      element.addEventListener('transitionend', handleTransitionEnd);
    }
    catch(error) {
      reject(error);
    }
  });
}

该修复在iPadOS 15.0~15.7版本的Safari上均测试通过,升级到iPadOS 16及以上版本后,原生Safari已修复该渲染问题,不加上述补丁也可正常运行过渡效果。


内容的提问来源于stack exchange,提问作者ofeenee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 05:33:23