【问题标题】:SVG transition is not workingSVG 过渡不起作用
【发布时间】:2017-10-29 03:05:55
【问题描述】:

只要 SVG 没有 z-index,我就可以通过 DOM 操作使我的矩形改变 z-position。但是,我有一个新问题。由于某种原因,过渡停止工作。我怀疑这是因为 DOM 重新排序,但我必须这样使用它。

function redZ() {
  document.getElementById('svgField').appendChild(document.getElementById('redRect'));
  document.getElementById('redRect').style.transition = "2s linear";
  document.getElementById('redRect').style.fill = "black";
}

function blueZ() {
  document.getElementById('svgField').appendChild(document.getElementById('blueRect'));
  document.getElementById('blueRect').style.transition = "2s linear";
  document.getElementById('blueRect').style.fill = "yellow";
}
<svg width="180" height="150" id="svgField">
   <rect onclick="redZ()" id="redRect" x="0" y="0" width="100" height="100" fill="red" />
   <rect onclick="blueZ()" id="blueRect" x="60" y="40" width="100" height="100" fill="blue" />
</svg>

【问题讨论】:

  • 根据实际 SVG 的整体结构,您可以选择将 SVG 中的元素拆分为单独的 &lt;svg&gt; 元素,然后在这些元素上应用 z-index

标签: javascript svg


【解决方案1】:

这似乎是由 DOM 操作引起的重绘问题。以下不是很优雅,但设置一个低毫秒值的超时(在某些情况下,您可能需要稍高的值 50/100 毫秒)通常可以作为最后的手段来解决与重绘相关的问题。

此外,您应该避免在每次点击时更改过渡值,这根本不需要。

document.getElementById('redRect').style.transition = "2s linear";
document.getElementById('blueRect').style.transition = "2s linear";

function redZ() {
  document.getElementById('svgField').appendChild(document.getElementById('redRect'));
  window.setTimeout(function() {
    document.getElementById('redRect').style.fill = "black";
  }, 10);
}

function blueZ() {
  document.getElementById('svgField').appendChild(document.getElementById('blueRect'));
  window.setTimeout(function() {
    document.getElementById('blueRect').style.fill = "yellow";
  }, 10);
}
<svg width="180" height="150" id="svgField">
   <rect onclick="redZ()" id="redRect" x="0" y="0" width="100" height="100" fill="red" />
   <rect onclick="blueZ()" id="blueRect" x="60" y="40" width="100" height="100" fill="blue" />
</svg>

【讨论】:

  • “这似乎是由DOM操作引起的重绘问题。” - 更确切地说,这是“控制权”交还给渲染引擎。 所有 DOM 和样式操作都是作为一项不间断的任务执行的。元素被附加到指定的 DOM 位置,并设置 CSS 过渡和填充属性。只有这样才会将其移交给渲染引擎,渲染引擎(对于原始的redZ 函数示例)查看它并运行,好吧,所以你给了我一个黑色填充颜色的元素,[...]跨度>
  • [...] 当某些属性发生变化时,我应该转换所有内容。知道了!让我检查一下......不,填充颜色仍然回来,其他一切都一样 - 我想我们已经完成了?轻微的超时(更“现代”的方法通常使用requestAnimationFrame)解决了这个问题,因为它也将控制权交给了两个步骤之间的渲染,将元素附加到 DOM,以及填充颜色的变化 - 所以当在填充颜色发生变化后,RE 再次查看它,它现在具有识别该变化的变化。
  • 感谢@CBroe 提供了比我更详细的信息。快速说明一下,requestAnimationFrame 是我首先尝试的,但似乎没有足够的延迟来处理这个问题。
  • 你是对的,显然浏览器使用 rAF (stackoverflow.com/a/40617982/1427878) 优化 反对,因此 setTimeout 是更安全的方法。好点子。
  • 正如stackoverflow.com/q/24148403/1427878 的其他答案中所提到的,有时在 JS 中明确地做一些事情,迫使它明确地“询问”渲染引擎的东西(比如元素的 offsetWidth、边界客户矩形等.) 也可以做到这一点。
【解决方案2】:

不要在 DOM 顺序中将活动 rect 移动到另一个 rect 上方,而是将 other rect 移动到 活动 rect 下方。这允许转换按预期工作。

换句话说,当单击redRect 时,不是将redRect 移动到DOM 顺序的底部(因此它在视觉上位于顶部),而是将blueRect 移动到DOM 顺序的顶部(所以它在视觉上位于底部)。与:

document.getElementById('svgField').prepend(document.getElementById('blueRect'));

像这样:

function redZ() {
  document.getElementById('svgField').prepend(document.getElementById('blueRect'));
  document.getElementById('redRect').style.transition = "2s linear";
  document.getElementById('redRect').style.fill = "black";
}

function blueZ() {
  document.getElementById('svgField').prepend(document.getElementById('redRect'));
  document.getElementById('blueRect').style.transition = "2s linear";
  document.getElementById('blueRect').style.fill = "yellow";
}
<svg width="180" height="150" id="svgField">
   <rect onclick="redZ()" id="redRect" x="0" y="0" width="100" height="100" fill="red" />
   <rect onclick="blueZ()" id="blueRect" x="60" y="40" width="100" height="100" fill="blue" />
   
</svg>

【讨论】:

    猜你喜欢
    • 2018-01-19
    • 2012-08-09
    • 2013-07-24
    • 2018-06-27
    • 2015-01-16
    • 2015-12-01
    • 2012-09-27
    相关资源
    最近更新 更多