Использование scaleX и translationX одновременно (VelocityJS) - PullRequest
0 голосов
/ 03 апреля 2019

У меня есть бар, который заполнен от определенного процента до другого процента (0% - 50%).Я хочу плавно оживить это к новому диапазону (25% - 55%).Это означает, что ширина и расположение панели должны быть изменены.У меня возникли проблемы с плавным выполнением обоих этих функций одновременно.

После некоторых исследований я обнаружил, что мне нужно использовать scaleX, чтобы плавно анимировать ширину, и translateX, чтобы плавно анимировать местоположение (гдеtranslateX также зависит от масштаба).Проблема, которая возникает сейчас, заключается в том, что полоса перешагнет желаемый процент (55%), а затем вернется назад, как показано в фрагменте ниже.

/* 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 для анимации в данный момент.

Есть идеи, что я делаю неправильно или как мне это сделать?Есть ли какие-то вычисления, которые мне нужно сделать для определения продолжительности / замедления, чтобы исправить все, что идет не так?

Ответы [ 2 ]

1 голос
/ 03 апреля 2019

Проблема связана с тем, как выполняется интерполяция значений. Было бы трудной работой по обеспечению глобального линейного преобразования с использованием 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>
0 голосов
/ 04 апреля 2019

Я начал с математики, пытаясь найти ответ, и получил его для упрощения перевода, что почти идеально: [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, чтобы вычислить кубические точки Безье, которые я использовал выше.Обратите внимание, что я делаю 1-C2y для 4-го пункта, не уверен, почему, но в противном случае это не сработало бы.

ПРИМЕЧАНИЕ.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>
...