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

使用jQuery设置CSS变量实现关键帧动画:Safari及移动端失效问题

动态CSS变量在Safari关键帧动画中失效的问题

我开发了一个以CSS变量作为动画属性的CSS关键帧动画,变量通过jQuery定义。动画在Chrome和Firefox中运行正常,但在Safari及移动端浏览器中,仅未使用该变量的动画部分能正常工作。

我已完成以下调试:

  • 在所有浏览器的最新版本中测试
  • Safari开发者工具显示变量已正确定义(如1200px)
  • 在CSS中硬编码该变量为相同像素值时,动画运行正常
  • 在jQuery中硬编码该变量为相同像素值(非计算得出)时,动画仍失效
  • 将变量用于元素选择器属性(非关键帧)时,元素可正常定位
  • 尝试用transform: translateY(...)替代top:,结果一致
  • 偶尔长时间打开窗口后动画会突然正常,但无法手动复现,无规律可循

问题核心:通过JS(jQuery或原生)定义变量并在关键帧中使用的组合,在Safari中存在兼容性问题。


相关代码

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="main-content">
  <div class="item one">Animated Using jQuery Variable</div>
  <div class="item two">Positioned Using jQuery Variable</div>
  <div class="item three">Animated Using CSS Variable</div>
</div>

CSS

:root {
  --hardvarheight: 75vh;
}

#main-content {
  width: 100%;
  height: 75vh;
}

.item {
  width: 75px;
  height: 50px;
  font-size: 80%;
}

.one {
  animation: animOne 3s linear infinite alternate;
  position: absolute;
  background-color: pink;
}

.two {
  position: absolute;
  top: calc(var(--varheight) / 100 * 50);
  left: 50vw;
  background-color: lightblue;
}

.three {
  animation: animThree 3s linear infinite alternate;
  position: absolute;
  background-color: lightgreen;
}

@keyframes animOne {
  0% {top: calc(var(--varheight) / 100 * 10); left: 40vw;}
  50% {top: calc(var(--varheight) / 100 * 20); left: 50vw;}
  100% {top: calc(var(--varheight) / 100 * 10);left: 60vw;}
}

@keyframes animThree {
  0% {top: calc(var(--hardvarheight) / 100 * 80); left: 40vw;}
  50% {top: calc(var(--hardvarheight) / 100 * 90); left: 50vw;}
  100% {top: calc(var(--hardvarheight) / 100 * 80); left: 60vw;}
}

JavaScript

function refHeight() {
  if ($('#main-content').length != 0) {
    $(':root').css('--varheight', Math.round($('#main-content').outerHeight()) + 'px');
  }
}
window.addEventListener('load', refHeight);
window.addEventListener('resize', refHeight);

原因分析

Safari(包括移动端Safari)对CSS关键帧的解析机制与Chrome/Firefox不同:关键帧在页面首次加载时会被解析并固化其中的变量值,后续通过JS动态修改全局(:root)变量时,Safari不会重新计算关键帧内的calc表达式。即使变量在开发者工具中显示正确,关键帧仍会使用初始解析时的变量值(通常是未定义的默认值),导致动画失效。

而Chrome和Firefox会实时监听CSS变量的变化,并自动更新关键帧中的计算结果,因此动画能正常运行。


解决方法

方法1:将变量绑定到动画元素而非全局

把CSS变量直接定义在需要动画的元素上,而非:root,这样Safari会监听元素级变量的变化并更新动画:

修改JavaScript代码:

function refHeight() {
  if ($('#main-content').length != 0) {
    const heightVal = Math.round($('#main-content').outerHeight()) + 'px';
    // 给目标元素设置变量
    $('.one').css('--varheight', heightVal);
    $('.two').css('--varheight', heightVal);
  }
}
window.addEventListener('load', refHeight);
window.addEventListener('resize', refHeight);

CSS无需修改,元素级变量的作用域足够覆盖其关联的关键帧。

方法2:通过JS动态生成关键帧

每次高度变化时,用JS重新生成关键帧的CSS内容,强制Safari重新解析关键帧:

function refHeight() {
  if ($('#main-content').length != 0) {
    const height = Math.round($('#main-content').outerHeight());
    // 创建新的样式标签覆盖原有关键帧
    const styleEl = document.createElement('style');
    styleEl.textContent = `
      @keyframes animOne {
        0% {top: calc(${height}px / 100 * 10); left: 40vw;}
        50% {top: calc(${height}px / 100 * 20); left: 50vw;}
        100% {top: calc(${height}px / 100 * 10); left: 60vw;}
      }
    `;
    // 移除旧的关键帧样式(可选,避免重复)
    const oldStyle = document.querySelector('#dynamic-anim');
    if (oldStyle) oldStyle.remove();
    styleEl.id = 'dynamic-anim';
    document.head.appendChild(styleEl);
  }
}
window.addEventListener('load', refHeight);
window.addEventListener('resize', refHeight);

方法3:使用@property显式声明变量(Safari 15.4+)

如果目标浏览器版本不低于Safari 15.4,可通过@property显式声明变量的类型,让Safari监听变量变化并更新关键帧:

在CSS开头添加:

@property --varheight {
  syntax: '<length>';
  inherits: true;
  initial-value: 0px;
}

此方法无需修改JS代码,@property会告诉浏览器变量的类型和行为,使其正确响应变量的动态修改。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 10:44:58