【问题标题】:Portions of SVG that were offscreen while zoomed in disappear when zooming back out放大时离开屏幕的 SVG 部分在缩小时消失
【发布时间】:2018-12-01 04:40:49
【问题描述】:

我正在使用 css 过渡来动画缩放到 SVG。唯一的问题是,当缩小时,部分 SVG 会丢失,直到动画完成然后全部弹出。

(目前只在 Mac 上的 Chrome 中测试过)

我根本没有更改 SVG,只是通过在 SVG 中的组上设置 scale transform 来放大然后退出。

如何让浏览器重新渲染这些屏幕外元素,以免像这样弹出?

const root = document.getElementById('root')

setTimeout(function() {
  root.setAttribute('transform', 'scale(10,10)')
}, 1)

setTimeout(function() {
  root.setAttribute('transform', 'scale(1,1)')
}, 4200)
#root {
  transition: 4s transform;
}

circle {
  stroke: white;
  stroke-width: 3px;
}
<svg viewbox="0 0 300 100">
  <g id="root">
    <circle cx="50" cy="50" r="50" />
    <circle cx="100" cy="50" r="50" />
    <circle cx="150" cy="50" r="50" />
    <circle cx="200" cy="50" r="50" />
    <circle cx="250" cy="50" r="50" />
  </g>
</svg>

【问题讨论】:

  • 我可能错了,但我认为 Chrome 在使用过渡时会创建光栅图像。然后,当转换完成时,更新 DOM。这是表演的事情。不知道如何解决,但也许知道这会让你朝着正确的方向前进。

标签: css svg css-transitions


【解决方案1】:

正如 cmets 中所说,这可能是因为 CSS 渲染器中的一些优化。
这是一个 Chrome 错误(他们的 CSS 绘画优化之一...),你应该let them know about it

目前,您是否考虑过使用 SMIL?

由于您在代码中使用了 javascript,我假设您在浏览器中从允许执行脚本的某个地方(即不在 &lt;img&gt; 标记中)运行它,因此您将能够使用像FakeSmile这样的polyfill。
因此,这实际上将为您提供比通过 CSS 转换更好的浏览器支持(IIRC IESVGTransformAttribute。 实际上,目前只有 Chrome 支持它(可能是因为虽然某些属性在 SVG1.1 中已经是 CSS 可转换的,transform 的语法与其等效的 CSS 不同,但算法应该不同)。

这是您的示例在 SMIL 中的样子:

// and if you need JS control
document.onclick = e => {
  document.getElementById('zoomin').beginElement();
};
circle {
  stroke: white;
  stroke-width: 3px;
  transform: translateZ(1);
}
<svg viewbox="0 0 300 100">
  <g id="root">
    <animateTransform attributeName="transform" type="scale" id="zoomin"
      from="1 1" to="10 10" dur="4s" begin="1s"/>
    <animateTransform attributeName="transform" type="scale" id="zoomout"
      from="10 10" to="1 1" dur="4s" begin="zoomin.end"/>    
    <circle cx="50" cy="50" r="50" />
    <circle cx="100" cy="50" r="50" />
    <circle cx="150" cy="50" r="50" />
    <circle cx="200" cy="50" r="50" />
    <circle cx="250" cy="50" r="50" />
  </g>
  <!-- for IE -->
  <script xlink:href="https://cdn.rawgit.com/FakeSmile/FakeSmile/master/smil.user.js"></script>
</svg>

【讨论】:

    【解决方案2】:

    怎么样,过渡每个圈子, 你可以吗?

    我实际上没有解释为什么它会这样工作

    const circles = document.getElementsByTagName('circle')
    
    setTimeout(function() {
      circles[0].setAttribute('transform', 'scale(10,10)');
      circles[1].setAttribute('transform', 'scale(10,10)');
      circles[2].setAttribute('transform', 'scale(10,10)');
      circles[3].setAttribute('transform', 'scale(10,10)');
      circles[4].setAttribute('transform', 'scale(10,10)');
    }, 1)
    
    setTimeout(function() {
       circles[0].setAttribute('transform', 'scale(1,1)');
       circles[1].setAttribute('transform', 'scale(1,1)');
       circles[2].setAttribute('transform', 'scale(1,1)');
       circles[3].setAttribute('transform', 'scale(1,1)');
       circles[4].setAttribute('transform', 'scale(1,1)');
    }, 4200)
    #root {
      transition: 4s transform;
    }
    
    circle {
     stroke: white;
     stroke-width: 3px;
     transition: 4s transform;
    }
    <svg viewbox="0 0 300 100">
     <g id="root">
       <circle cx="50" cy="50" r="50" />
       <circle cx="100" cy="50" r="50" />
       <circle cx="150" cy="50" r="50" />
       <circle cx="200" cy="50" r="50" />
       <circle cx="250" cy="50" r="50" />
     </g>
    </svg>

    【讨论】:

    • 嗯,这很有趣。我敢打赌,如果有很多元素,这会表现得更糟,但它似乎确实可以在没有视觉伪影的情况下完成工作。
    • 嗯,同样,这在应用于组时似乎不起作用,我真正的问题是一个更大的 SVG,将这种变换应用于每个可渲染元素是不切实际的。会有数千人。
    【解决方案3】:

    为什么不转换整个svg 元素?

    const root = document.getElementById('root')
    
    setTimeout(function() {
      root.setAttribute('transform', 'scale(10,10)')
    }, 1)
    
    setTimeout(function() {
      root.setAttribute('transform', 'scale(1,1)')
    }, 4200)
    #root {
      transition: 4s transform;
      transform-origin: top left;
    }
    
    circle {
      stroke: white;
      stroke-width: 3px;
    }
    <svg id="root" viewbox="0 0 300 100">
      <g>
        <circle cx="50" cy="50" r="50" />
        <circle cx="100" cy="50" r="50" />
        <circle cx="150" cy="50" r="50" />
        <circle cx="200" cy="50" r="50" />
        <circle cx="250" cy="50" r="50" />
      </g>
    </svg>

    【讨论】:

    • 这确实解决了我设计的示例中的问题,但我需要能够放大特定区域。如果我这样做,翻译就会在页面的坐标空间中,而不是 SVG,这使得很难弄清楚如何正确翻译。
    【解决方案4】:

    setAttribute 需要 style 属性。试试这个脚本:

    setTimeout(function() {
     root.setAttribute('style', 'transform: scale(10,10)')
    }, 1)
    
    setTimeout(function() { 
        root.setAttribute('style', 'transform: scale(1,1)')
    }, 4200)
    

    【讨论】:

    • 很遗憾,这也有同样的问题。
    猜你喜欢
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-30
    • 2019-05-28
    • 2023-03-22
    • 1970-01-01
    • 2014-06-18
    相关资源
    最近更新 更多