首页 > 解决方案 > 在 d3js styleTween 中使用 CSS calc 时出现意外的过渡

问题描述

我想使用 CSS calc 计算我想通过使用 d3js styleTween 平滑移动的 div 的 left 属性的开始和/或结束值。但在开始过渡到正确的终点之前,div 会朝相反的方向跳跃。

我使用 Chromium 版本 73.0.3683.75 和 Firefox 60.7.0esr(64 位)测试了代码。两者都会产生相同的意外行为。

在 d3.interpolate 中使用 this.style.left 作为第一个参数不会改变结果。

我没有在 d3js styleTween 文档中找到警告使用 calc 的注释,它确实适用于过渡结束。

我希望先计算 calc 值,然后将结果传递给插值函数,就可以解决问题。但我不确定这是否可能。

<!DOCTYPE html>
<html>
    <head>
        <title>Odd interpolation with CSS calc and d3 styleTween</title>

        <style>
        html {height: 100%;}
        body {height: 100%; margin:0; }

        div.button {
        position: absolute;
        left: 20%;
        width: 3rem;
        height: 100%;
        background-color: #0af;
        }
        </style>
    </head>

    <body>
        <div class="button" id="menu_toggle"></div>
    </body>

    <script src="https://d3js.org/d3.v4.min.js" />
    <script>
        menu = function()
        {
            var div = d3.select('div.button');
            var is_left = true;
            function toggle_menu()
        {
            var left = "20%"
            var right = "calc(80% - 3rem)"
            if (is_left)
            {
                is_left = false;
                div.transition().duration(2000)
                                .styleTween('left', function() { return d3.interpolate(left, right); });
            }
            else
            {
                is_left = true;
                div.transition().duration(2000)
                                .styleTween('left', function() { return d3.interpolate(right, left); });
            }
        }
        return {toggle_menu:toggle_menu}
        }()

        d3.select('#menu_toggle').on("click", menu.toggle_menu)
    </script>
</html>

我希望在 20% 和 80%-3rem 两个位置之间平稳过渡。

相反,当 div 从左侧开始时,它首先向左跳 3rem,然后向右移动。同样,如果 div 在右侧,它首先向右跳 3rem,然后再向左移动。

标签: javascriptcssd3.jscss-calc

解决方案


问题非常简单:您插入的是字符串,而不是它们的评估值。

当您在 CSS 中执行此操作时...

calc(80% - 3rem)

...它将被评估为像素值。到目前为止,一切都很好。但是,D3 插值器实际上是(双关语)从"20%"到插值"calc(80% - 3rem)"。当然,插值器不知道如何处理这个问题,它实际上是从"calc(20% - 3rem)"to插值"calc(80% - 3rem)"

让我们来看看:

const interpolator = d3.interpolate("20%", "calc(80% - 3rem)");
d3.range(0, 1.1, 0.1).forEach(d => {
  console.log(interpolator(d))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

最简单的解决方案是评估结果calc()并在插值器中使用该值。有几种方法可以做到这一点,比如使用这个库

所以,假设在评估之后我们有一些像150px和的值430px。现在插值很容易:

const interpolator = d3.interpolate("150px", "430px");
d3.range(0, 1.1, 0.1).forEach(d => {
  console.log(interpolator(d))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


推荐阅读