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,和设备芯片无直接关联。
修复分两步:
- 给img元素添加CSS属性强制开启独立渲染层,避免src修改时层重建打断过渡:
img { /* 原有属性保留 */ -webkit-transform: translateZ(0); transform: translateZ(0); will-change: opacity; }
- 修改
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
相关产品推荐
相关产品推荐

