【问题标题】:Using scaleX and translationX at the same time (VelocityJS)同时使用 scaleX 和 translationX (VelocityJS)
【发布时间】:2019-08-25 03:46:45
【问题描述】:

我有一个从某个百分比填充到另一个百分比 (0% - 50%) 的条形图。我想将其平滑地设置为一个新的范围(25% - 55%)。这意味着栏的宽度和位置都需要更改。我在同时顺利完成这两项工作时遇到了一些麻烦。

经过一些研究,我发现我需要使用scaleX 来平滑地为宽度设置动画,并使用translateX 来平滑地为位置设置动画(其中translateX 也受比例影响)。现在出现的问题是,柱将超出其所需的百分比 (55%),然后向后移动,如下面的 sn-p 所示。

/* Button function to restart. */
const restart = () => {
  animate();
}

const animate = () => {
  /* Reset the bar to its starting position. (from 0% - 50%) */
  Velocity(document.getElementById('movingBar'), {
    scaleX: 0.5,
    translateX: 0
  },
  {
    duration: 0,
    easing: [0, 0, 1, 1]
  });
  
  /* Move the bar to its final position. (from 25% - 55%). */
  /* Split into two velocity calls so that they can have a seperate duration/easing if needed. */
  Velocity(document.getElementById('movingBar'), {
    scaleX: 0.30
  },
  {
    duration: 1000,
    easing: [0, 0, 1, 1],
    queue: false
  });
  
  Velocity(document.getElementById('movingBar'), {
    translateX: (25 / 0.30) + '%'
  },
  {
    duration: 1000,
    easing: [0, 0, 1, 1],
    queue: false
  });
};

/* Start animation on run. */
animate();
#root {
  width: 700px;
  height: 100%;
}

#container {
  width: 100%;
  height: 90px;
  background-color: #000000;
}

.bar {
  width: 100%;
  height: 30px;
  transform: scaleX(0.5);
  transform-origin: left;
  background-color: #FF0000;
}

#description {
  display: flex;
}

.percentage {
  display: flex;
  justify-content: flex-end;
  width: 10%;
  height: 20px;
  text-align: right;
}

.odd {
  background-color: #DDDDDD;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
  <div id='container'>
    <div class="bar" style="background-color: #00FF00;"></div>
    <div id='movingBar' class="bar"></div>
    <div class="bar" style="background-color: #00FF00; transform: scaleX(0.30) translateX(calc(25 / 0.30 * 1%))"></div>
  </div>
  <div id='description'>
    <div class="percentage even">10%</div>
    <div class="percentage odd">20%</div>
    <div class="percentage even">30%</div>
    <div class="percentage odd">40%</div>
    <div class="percentage even">50%</div>
    <div class="percentage odd">60%</div>
    <div class="percentage even">70%</div>
    <div class="percentage odd">80%</div>
    <div class="percentage even">90%</div>
    <div class="percentage odd">100%</div>
  </div>
  <button onClick="restart()">restart</button>
</div>

该示例在顶部绿色条中显示起始位置,并在底部绿色条中显示动画到的所需位置。中间的红色条是应该从起始位置动画到所需位置的条。如您所见,红色条最终达到了预期的结果,但在超过 55% 之前没有达到。我目前正在使用 VelocityJS 制作动画。

知道我做错了什么或者我将如何制作动画吗?我需要对持续时间/缓动进行一些计算以纠正出现的任何问题吗?

【问题讨论】:

    标签: css css-transforms velocity.js


    【解决方案1】:

    这个问题与如何完成值的插值有关。确保使用 scaleX 和 translateX 进行全局线性变换将是一项困难工作,因为您无法控制值的插值,而浏览器将为您执行此操作。因此,您要么对持续时间/缓动进行复杂计算以找到完美的结果,要么考虑另一种动画。

    对于这种情况,我会考虑clip-path,即使支持不是最佳的,但它会更容易处理,因为你只有一个属性要动画,你不需要任何复杂的计算,因为你只需要使用图表的百分比。

    这是一个简化的例子:

    body {
      background:#000;
    }
    .box {
      height:50px;
      background:red;
      clip-path:polygon(10% 100%,10% 0,     40% 0,40% 100%); /*from [10% 40%]*/
      transition:1s all;
    }
    
    body:hover .box {
      clip-path:polygon(50% 100%,50% 0,     60% 0,60% 100%); /*to [50% 60%]*/
    }
    <div class="box">
    
    </div>

    【讨论】:

      【解决方案2】:

      我从数学开始,试图找到答案,并得到了这个几乎完美的翻译缓和: [1/3, 0.2, 2/3, 1 - (8/15)];

      我通过创建一个公式来计算这些转换路径应该是什么样子25x / (-0.2x + 0.5)。然后我将它除以 83.333333 以在 [0, 1] 窗口中获得所需的翻译公式。然后我使用(25x / (-0.2x + 0.5)) / 83.333333 的公式来计算我上面使用的三次贝塞尔点。请注意,我在第 4 点执行 1-C2y,不知道为什么,否则它不会起作用。

      注意:这适用于上面的原始问题,但当比例从 0.5 - 0.3 以外的任何值变化时,它就无法工作。我还在想为什么。

      /* Button function to restart. */
      const restart = () => {
        animate();
      }
      
      const desiredX = 0.5;
      let desiredYScale = 0;
      let desiredYTranslation = 0;
      
      const animate = () => {
        const barMax = 100;
        
        
        const scaleStart = 0.5;
        const scaleEnd = 0.4;
        const offsetStart = 0;
        const offsetEnd = 10;
        const translationStart = 100 / barMax * offsetStart / scaleStart;
        const translationEnd = 100 / barMax * offsetEnd / scaleEnd;
        const dataS = {};
        const dataT = {};
        
        const F = 0.5;
        const scaleFormula = ((-scaleStart + scaleEnd) * F + scaleStart);
        //console.log("scale formula: ", scaleFormula);
        const translationPath = (offsetEnd * F) / scaleFormula;
        const diffPath = translationPath - (offsetEnd / scaleEnd * F);
        const diffFormulaA = - (diffPath / (F * F));
        const diffFormula = (diffFormulaA/diffPath) * (F - 0.5) * (F - 0.5) + 1
        const diffA = diffFormulaA / diffPath;
        const cX = 0.5;
        const cY = 0.5 * Math.abs(diffA);
        const cAx = 2/3 * cX;
        const cAy = 2/3 * cY;
        const cBx = 2/3 * cX + 1/3 * 1;
        const cBy = 2/3 * cY + 1/3 * 1;
        const multiplicant = 0.5;
        
        const realCAX = cAx / cBy * multiplicant;
        const realCAY = cAy / cBy * multiplicant;
        const realCBX = 1 - (cBx / cBy * multiplicant);
        const realCBY = cBy / cBy * multiplicant;
        console.log("realCAX: ", realCAX);
        console.log("realCAY: ", realCAY);
        console.log("realCBX: ", realCBX);
        console.log("realCBY: ", realCBY);
        
        
        const linearEasing = [0, 0, 1, 1];
        //const one = 0.5 + (scaleEnd / 4);
        //const two = 0.525 - (scaleStart - scaleEnd);
        //const one = 0.40 + 0.025 / (0.5 - (scaleStart - scaleEnd));
        //console.log("One: ", one, (scaleStart - scaleEnd));
        //const one = 0.5;
        //const one = 0.535;
        //const one = 0.5;
        const one = 0.5;
        const two = 0.1;
        
        //const two = 0.125;
        //const translationEasing = [0.33, 10, 0.66, 16];
        //const translationEasing = [1/3, 0.2, 2/3, 1-(8/15)];
        //const translationEasing = [1/3, 1/15, 2/3, 1-0.4];
        //const translationEasing = [0.24, 0.06666, 0.85, 0.1];
        //const translationEasing = [0.33, 1.33, 0.66, 1.66];
        //const translationEasing = [0.2, 0.8, 0.4, 1];
        //const translationEasing = [0.1, 0.4, 1-0.2, 0.5];
        //const translationEasing = [realCAX, realCAY, realCBX, realCBY];
        //const translationEasing = [1/3, 0.0833333, 2/3, 0.42];
        const translationEasing = [one, two, 1-two, 1-one];
        //const translationEasing = [0, 0, 1, 1];5
        
        /* Reset the bar to its starting position. (from 0% - 50%) */
        Velocity(document.getElementById('movingBar'), {
          scaleX: scaleStart,
          translateX: translationStart  + '%'
        },
        {
          duration: 0,
          easing: linearEasing
        });
        
        /* Move the bar to its final position. (from 25% - 55%). */
        /* Split into two velocity calls so that they can have a seperate duration/easing if needed. */
        Velocity(document.getElementById('movingBar'), {
          scaleX: scaleEnd
        },
        {
          duration: 1000,
          easing: linearEasing,
          queue: false,
          progress: function(elements, complete, remaining, start, tweenValue) {
            dataS[complete] = scaleStart + ((scaleEnd - scaleStart) * complete);
          }
        });
        
        Velocity(document.getElementById('movingBar'), {
          translateX: translationEnd + '%',
          tween: translationEnd
        },
        {
          duration: 1000,
          easing: translationEasing,
          queue: false,
          progress: function(elements, complete, remaining, start, tweenValue) {
             dataT[complete] = translationStart + ((translationEnd - translationStart) * complete);
             console.log("TWEEN", complete, tweenValue);
          },
          complete: function() {
            //console.log("DONE!");
            //console.log("SCALE:");
            //if (desiredX in dataS) {
              //console.log('Scale[0.5]: ', dataS[desiredX], ', Translation[0.5]: ', dataT[desiredX]);
              //desiredYScale = dataS[desiredX];
              //desiredYTranslation = dataT[desiredX];
            //} else {
              //animate();
            //}
            for (const key in dataS) {
              if (dataS.hasOwnProperty(key)) {
                //console.log('', key, ': ', dataS[key]);
              } 
            }
            
            //console.log("TRANSLATION:");
            for (const key in dataT) {
              if (dataT.hasOwnProperty(key)) {
                //console.log('', key, ': ', dataT[key]);
              } 
            }
          }
        });
      };
      
      /* Start animation on run. */
      animate();
      #root {
        width: 700px;
        height: 100%;
      }
      
      #container {
        width: 100%;
        height: 90px;
        background-color: #000000;
      }
      
      .bar {
        width: 100%;
        height: 30px;
        transform: scaleX(0.5) transform (calc(20 / 0.5 * 1%));
        transform-origin: left;
        background-color: #FF0000;
      }
      
      #description {
        display: flex;
      }
      
      .percentage {
        display: flex;
        justify-content: flex-end;
        width: 10%;
        height: 20px;
        text-align: right;
      }
      
      .odd {
        background-color: #DDDDDD;
      }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
      
      <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
      <div id='root' style='width: 100%; height: 100%'>
        <div id='container'>
          <div class="bar" style="background-color: #00FF00; transform: scaleX(0.5) translateX(calc(0 / 0.5 * 1%))"></div>
          <div id='movingBar' class="bar"></div>
          <div class="bar" style="background-color: #00FF00; transform: scaleX(0.4) translateX(calc(10 / 0.4 * 1%))"></div>
        </div>
        <div id='description'>
          <div class="percentage even">10%</div>
          <div class="percentage odd">20%</div>
          <div class="percentage even">30%</div>
          <div class="percentage odd">40%</div>
          <div class="percentage even">50%</div>
          <div class="percentage odd">60%</div>
          <div class="percentage even">70%</div>
          <div class="percentage odd">80%</div>
          <div class="percentage even">90%</div>
          <div class="percentage odd">100%</div>
        </div>
        <button onClick="restart()">restart</button>
      </div>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-10-09
        • 2015-10-07
        • 2018-11-29
        • 2015-05-14
        • 2015-11-12
        • 2018-10-20
        • 1970-01-01
        相关资源
        最近更新 更多