Как оживить индикатор выполнения с негативами с помощью Element.animate () - PullRequest
3 голосов
/ 13 июля 2020

Я пытаюсь имитировать c следующий виджет с HTML / CSS / JavaScript: https://gyazo.com/76bee875d35b571bd08edbe73ead12cb

Я настроил его следующим образом :

  • У меня есть полоса с цветом фона, который имеет градиент от красного к зеленому, что составляет c.
  • Затем у меня есть две шоры, которые должны представлять отрицательное пространство, чтобы создать иллюзию анимации цветных полос (на самом деле шоры просто ускользают)

Я сделал это, потому что решил, что это может быть проще, чем пытаться оживить Бар идет в обоих направлениях, но теперь я не уверен, лол. Одно требование, которое я пытаюсь сохранить, заключается в том, что анимация имеет дело только с transform или opacity, чтобы воспользоваться преимуществами оптимизации, которую может сделать браузер (как описано здесь: https://hacks.mozilla.org/2016/08/animating-like-you-just-dont-care-with-element-animate/)

В примере есть несколько кнопок, которые помогают тестировать различные вещи. «Случайный положительный результат» отлично работает, и это именно то, что я хочу. Я еще не подключил отрицательное, потому что не знаю, как подойти к проблеме перехода от положительного к отрицательному и наоборот.

В идеале, при переходе от положительного к отрицательному, правая ослепляющая маска завершит sh в середине, а левая ослепляет sh анимацию и завершит go.

Так, например, если значения изначально установить на 40%, а затем установить на -30%, правая ослепляющая маска должна анимировать transform: translateX(40%) -> transform: translateX(0%), а затем левая козырёк должна анимироваться от transform: translateX(0%) -> transform: translateX(-30%), чтобы показать красный цвет.

Кроме того, плавность должна быть плавной.

Я не уверен, возможно ли это с настройкой (в частности, сохраняя плавность плавности, поскольку, я думаю, она будет зависеть от каждого элемента, и не может «перенести» на другой элемент?) Примечание: * 10 39 * Я использую jquery просто для удобства работы с событиями кликов и прочим, но в конечном итоге это будет в приложении, которое не знает jquery.

Вот моя текущая попытка: https://codepen.io/blitzmann/pen/vYLrqEW

let currentPercentageState = 0;

function animate(percentage) {
  var animation = [{
      transform: `translateX(${currentPercentageState}%)`,
      easing: "ease-out"
    },
    {
      transform: `translateX(${percentage}%)`
    }
  ];

  var timing = {
    fill: "forwards",
    duration: 1000
  };

  $(".blind.right")[0].animate(animation, timing);

  // save the new value so that the next iteration has a proper from keyframe
  currentPercentageState = percentage;
}

$(document).ready(function() {
  $(".apply").click(function() {
    animate($("#amount").val());
  });

  $(".reset").click(function() {
    animate(0);

  });

  $(".random").click(function() {
    var val = (Math.random() * 2 - 1) * 100;
    $("#amount").val(val);
    animate(val);

  });

  $(".randomPos").click(function() {
    var val = Math.random() * 100;
    $("#amount").val(val);
    animate(val);

  });

  $(".randomNeg").click(function() {
    var val = Math.random() * -100;
    $("#amount").val(val);
    animate(val);
  });

  $(".toggleBlinds").click(function() {
    $(".blind").toggle();
  });

  $(".toggleLeft").click(function() {
    $(".blind.left").toggle();
  });

  $(".toggleRight").click(function() {
    $(".blind.right").toggle();
  });
});

$(document).ready(function() {});
.wrapper {
  margin: 10px;
  height: 10px;
  width: 800px;
  background: linear-gradient(to right, red 50%, green 50%);
  border: 1px solid black;
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
}

.blind {
  height: 100%;
  position: absolute;
  top: 0;
  background-color: rgb(51, 51, 51);
  min-width: 50%;
}

.blind.right {
  left: 50%;
  border-left: 1px solid white;
  transform-origin: left top;
}

.blind.left {
  border-right: 1px solid white;
  transform-origin: left top;
}
<div class="wrapper">
  <div class='blind right'></div>
  <div class='blind left'></div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>

<input id="amount" type="number" placeholder="Enter percentage..." value='40' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>

<button class="reset" href="#">Reset</button>

Ответы [ 3 ]

1 голос
/ 21 июля 2020

Вам нужно анимировать вещи в два этапа. Первый шаг - сбросить предыдущее состояние в исходное состояние (которое должно быть установлено на 0), а на втором шаге вам нужно запустить другую анимацию, которая фактически переместит его в целевое состояние. Для этого вы можете сделать:

let currentPercentageState = 0;
const animationTiming = 300;

function animate(percentage) {
  let defaultTranformVal = [{
    transform: `translateX(${currentPercentageState}%)`,
    easing: "ease-out"
  }, {transform: `translateX(0%)`}];
  var animation = [{
      transform: `translateX(0%)`,
      easing: "ease-out"
    },{
      transform: `translateX(${percentage}%)`,
      easing: "ease-out"
    }];
  var timing = {
    fill: "forwards",
    duration: animationTiming
  };
  if (percentage < 0) {
    if(currentPercentageState > 0) {
      $(".blind.right")[0].animate(defaultTranformVal, timing); 
      setTimeout(() => {
        $(".blind.left")[0].animate(animation, timing);
      }, animationTiming); 
    } else {
      $(".blind.left")[0].animate(animation, timing);
    }
  }
  if(percentage > 0) {
   if(currentPercentageState < 0) {
    $(".blind.left")[0].animate(defaultTranformVal, timing);
     setTimeout(() => {
       $(".blind.right")[0].animate(animation, timing);
     }, animationTiming);
   } else {
     $(".blind.right")[0].animate(animation, timing);
   }
  }

  // save the new value so that the next iteration has a proper from keyframe
  currentPercentageState = percentage;
}

Здесь вы увидите, что у нас есть два преобразования. Первый defaultTranformVal переместит currentPercentageState в ноль, а затем другой, который переместится с 0 на процент.

Здесь вам нужно обработать пару условий . Первый - если вы запускаете его в первый раз (означает, что нет currentPercentageState ), вам не нужно запускать defaultTranformVal . Если у вас есть currentPercentageState , тогда вам нужно запустить defaultTranformVal , а затем запустить вторую анимацию.

Примечание: - Вам также необходимо очистить тайм-аут, чтобы предотвратить утечку памяти. С этим можно справиться, сохранив возвращаемое значение setTimout , а затем при следующем запуске очистите предыдущее с помощью clearTimeout .

Вот обновленный код пример: - https://codepen.io/gauravsoni119/pen/yLeZBmb?editors=0011

1 голос
/ 16 июля 2020

Я изменил ваш код. Взгляните на код.

let currentPercentageState = 0;

function animate(percentage) {

  var animation = [{
      transform: `translateX(${currentPercentageState}%)`,
      easing: "ease-out"
    },
    {
      transform: `translateX(${percentage}%)`
    }
  ];

  var timing = {
    fill: "forwards",
    duration: 1000
  };

  if (percentage < 0) {
    $(".blind.right")[0].animate(
      [{
          transform: `translateX(0%)`,
          easing: "ease-out"
        },
        {
          transform: `translateX(0%)`
        }
      ], timing);
    $(".blind.left")[0].animate(animation, timing);

  } else {
    $(".blind.left")[0].animate(
      [{
          transform: `translateX(0%)`,
          easing: "ease-out"
        },
        {
          transform: `translateX(0%)`
        }
      ], timing);
    $(".blind.right")[0].animate(animation, timing);
  }


  // save the new value so that the next iteration has a proper from keyframe
  //currentPercentageState = percentage;
}

$(document).ready(function() {
  $(".apply").click(function() {
    animate($("#amount").val());
  });

  $(".reset").click(function() {
    animate(0);

  });

  $(".random").click(function() {
    var val = (Math.random() * 2 - 1) * 100;
    $("#amount").val(val);
    animate(val);

  });

  $(".randomPos").click(function() {
    var val = Math.random() * 100;
    $("#amount").val(val);
    animate(val);

  });

  $(".randomNeg").click(function() {
    var val = Math.random() * -100;
    $("#amount").val(val);
    animate(val);
  });

  $(".toggleBlinds").click(function() {
    $(".blind").toggle();
  });

  $(".toggleLeft").click(function() {
    $(".blind.left").toggle();
  });

  $(".toggleRight").click(function() {
    $(".blind.right").toggle();
  });
});

$(document).ready(function() {});
.wrapper {
  margin: 10px;
  height: 10px;
  width: 800px;
  background: linear-gradient(to right, red 50%, green 50%);
  border: 1px solid black;
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
}

.blind {
  height: 100%;
  position: absolute;
  top: 0;
  background-color: rgb(51, 51, 51);
  min-width: 50%;
}

.blind.right {
  left: 50%;
  border-left: 1px solid white;
  transform-origin: left top;
}

.blind.left {
  border-right: 1px solid white;
  transform-origin: left top;
}
<div class="wrapper">
  <div class='blind right'></div>
  <div class='blind left'></div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>

<input id="amount" type="number" placeholder="Enter percentage..." value='40' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>

<button class="reset" href="#">Reset</button>
0 голосов
/ 29 июля 2020

РЕДАКТИРОВАТЬ: Мне действительно удалось это решить! для которого я знаю разворот. Пост ниже и мое объяснение основаны на функции ослабления с использованием sin (), которую нелегко отменить. Не только это, но и встроенная функция ослабления для ease-out не соответствует функции sin (), на которую я ссылался (я не совсем уверен, на чем основана эта сборка). Но я понял, что могу дать ему свою функцию, для которой я знал разворот, и бум работает как шарм!

Это был очень информативный опыт для меня, я рад, что у меня решение, которое работает. Я все еще думаю, что окунусь в другие идеи, которые мне нужно было посмотреть, какие из них будут лучше в долгосрочной перспективе.

Исторический пост:

Итак, после нескольких ночей, когда я ломал голову над этим, я пришел к выводу, что это либо невозможно, как я думал об этом, либо Если это возможно, то решение настолько надумано, что оно, вероятно, того не стоит, и мне лучше разработать новое решение (одно из которых я придумал, или несколько вещей, которые я хотел бы попробовать).

Пожалуйста, посмотрите этот jsfiddle для моего окончательного «решения» и вскрытия

https://jsfiddle.net/blitzmann/zc80p1n4/

let currentPercentageState = 0;
let easing = "linear";
let duration = 1000;

function animate(percentage) {
  percentage = parseFloat(percentage);

  // determine if we've crossed the 0 threshold, which would force us to do something else here
  let threshold = currentPercentageState / percentage < 0;
  console.log("Crosses 0: " + threshold);

  if (!threshold && percentage != 0) {
    // determine which blind we're animating
    let blind = percentage < 0 ? "left" : "right";

    $(`.blind.${blind}`)[0].animate(
      [
        {
          transform: `translateX(${currentPercentageState}%)`,
          easing: easing
        },
        {
          transform: `translateX(${percentage}%)`
        }
      ],
      {
        fill: "forwards",
        duration: duration
      }
    );
  } else {
    // this happens when we cross the 0 boundry
    // we'll have to create two animations - one for moving the currently offset blind back to 0, and then another to move the second blind
    let firstBlind = percentage < 0 ? "right" : "left";
    let secondBlind = percentage < 0 ? "left" : "right";
    
    // get total travel distance
    let delta = currentPercentageState - percentage;
    
    // find the percentage of that travel that the first blind is responsible for
    let firstTravel  = currentPercentageState / delta;
    let secondTravel = 1 - firstTravel;

    console.log("delta; total values to travel: ", delta);
    console.log(
      "firstTravel; percentage of the total travel that should be done by the first blind: ",
      firstTravel
    );
    console.log(
      "secondTravel; percentage of the total travel that should be done by the second blind: ",
      secondTravel
    );
    
    // animate the first blind.
    $(`.blind.${firstBlind}`)[0].animate(
      [
        {
          transform: `translateX(${currentPercentageState}%)`,
          easing: easing
        },
        {
          // we go towards the target value instead of 0 since we'll cut the animation short
          transform: `translateX(${percentage}%)`
        }
      ],
      {
        fill: "forwards",
        duration: duration,
        // cut the animation short, this should run the animation to this x value of the easing function
        iterations: firstTravel
      }
    );

    // animate the second blind
    $(`.blind.${secondBlind}`)[0].animate(
      [
        {
          transform: `translateX(${currentPercentageState}%)`,
          easing: easing
        },
        {
          transform: `translateX(${percentage}%)`
        }
      ],
      {
        fill: "forwards",
        duration: duration,
        // start the iteration where the first should have left off. This should put up where the easing function left off
        iterationStart: firstTravel,
        // we only need to carry this aniamtion the rest of the way
        iterations: 1-firstTravel,
        // delay this animation until the first "meets" it
        delay: duration * firstTravel
      }
    );
  }
  // save the new value so that the next iteration has a proper from keyframe
  currentPercentageState = percentage;
}

// the following are just binding set ups for the buttons

$(document).ready(function () {
  $(".apply").click(function () {
    animate($("#amount").val());
  });

  $(".reset").click(function () {
    animate(0);
  });

  $(".random").click(function () {
    var val = (Math.random() * 2 - 1) * 100;
    $("#amount").val(val);
    animate(val);
  });

  $(".randomPos").click(function () {
    var val = Math.random() * 100;
    $("#amount").val(val);
    animate(val);
  });

  $(".randomNeg").click(function () {
    var val = Math.random() * -100;
    $("#amount").val(val);
    animate(val);
  });

  $(".flipSign").click(function () {
    animate(currentPercentageState * -1);
  });

  $(".toggleBlinds").click(function () {
    $(".blind").toggle();
  });

  $(".toggleLeft").click(function () {
    $(".blind.left").toggle();
  });

  $(".toggleRight").click(function () {
    $(".blind.right").toggle();
  });
});

animate(50);
//setTimeout(()=>animate(-100), 1050)

$(function () {
  // Build "dynamic" rulers by adding items
  $(".ruler[data-items]").each(function () {
    var ruler = $(this).empty(),
      len = Number(ruler.attr("data-items")) || 0,
      item = $(document.createElement("li")),
      i;

    for (i = -11; i < len - 11; i++) {
      ruler.append(item.clone().text(i + 1));
    }
  });
  // Change the spacing programatically
  function changeRulerSpacing(spacing) {
    $(".ruler")
      .css("padding-right", spacing)
      .find("li")
      .css("padding-left", spacing);
  }

  changeRulerSpacing("30px");
});
.wrapper {
  margin: 10px auto 2px;
  height: 10px;
  width: 600px;
  background: linear-gradient(to right, red 50%, green 50%);
  border: 1px solid black;
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
}

.blind {
  height: 100%;
  position: absolute;
  top: 0;
  background-color: rgb(51, 51, 51);
  min-width: 50%;
}

.blind.right {
  left: 50%;
  border-left: 1px solid white;
  transform-origin: left top;  
}

.blind.left {
  border-right: 1px solid white;
  transform-origin: left top;
}

#buttons {
  text-align: center;
}

/* Ruler crap */

.ruler-container {
  text-align: center;
}
.ruler, .ruler li {
    margin: 0;
    padding: 0;
    list-style: none;
    display: inline-block;
}
/* IE6-7 Fix */
.ruler, .ruler li {
    *display: inline;
}
.ruler {
  display:inline-block;
    margin: 0 auto;https://jsfiddle.net/user/login/
    background: lightYellow;
    box-shadow: 0 -1px 1em hsl(60, 60%, 84%) inset;
    border-radius: 2px;
    border: 1px solid #ccc;
    color: #ccc;
    height: 3em;
    padding-right: 1cm;
    white-space: nowrap;
  margin-left: 1px;
}
.ruler li {
    padding-left: 1cm;
    width: 2em;
    margin: .64em -1em -.64em;
    text-align: center;
    position: relative;
    text-shadow: 1px 1px hsl(60, 60%, 84%);
}
.ruler li:before {
    content: '';
    position: absolute;
    border-left: 1px solid #ccc;
    height: .64em;
    top: -.64em;
    right: 1em;
}

Спасибо тем, кто пытался решить эту, по общему признанию, сложную задачу

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...