Можно ли использовать функцию генератора для замены управления обещаниями? - PullRequest
1 голос
/ 30 июня 2019

В этом примере кода вы должны представить серию анимаций при перемещении робота (двигаться влево / вправо, идти вперед)

На самом деле это сайт с более сложной анимацией (загрузка ajax, загрузка изображений, несколько анимаций и т. Д.) что я в настоящее время справляюсь с обещаниями, но по мере развития сайта код этой части становится блюдом из спагетти.

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

Этот пример кода выглядит правильно?
Должен ли я что-то там изменить?

const Root   = document.documentElement
  ,   gRoot  = getComputedStyle(Root)
  ,   moving = [ {T:-30,L:0},  {T:0,L:+30}, {T:+30,L:0}, {T:0,L:-30} ]
  ;
var RotateDeg = 0
  , RotateMov = 0
  , posT      = parseInt(gRoot.getPropertyValue('--PosT'))
  , posL      = parseInt(gRoot.getPropertyValue('--PosL'))
  ;
function F_1() // move forward
  {
  posT += moving[RotateMov].T
  posL += moving[RotateMov].L

  Root.style.setProperty('--PosT', posT + "px")
  Root.style.setProperty('--PosL', posL + "px")
  }
function T_L() // turn Left
  {
  RotateMov = (RotateMov +3) %4
  RotateDeg -=90
  Root.style.setProperty('--turn', RotateDeg + "deg")
  }
function T_R() // turn Right
  {
  RotateMov = (RotateMov +1) %4
  RotateDeg +=90
  Root.style.setProperty('--turn', RotateDeg + "deg")
  }
function R_0() // rotate to zero
  {
  RotateMov = 0
  RotateDeg = 0
  Root.style.setProperty('--turn', RotateDeg + "deg")
  }
function disableButtons(OnOff)
  {
  Bt_Tab_A.disabled = OnOff
  Bt_Tab_B.disabled = OnOff
  }
function* Sequence(Tab_fct)
  {
  for( let fct of Tab_fct) yield fct
  }

var iterator = Sequence([])

function nextSequence()
  {
  let command = iterator.next()
  if (!command.done) command.value()
  else disableButtons(false)
  }

Bt_Tab_A.onclick=_=>
  { 
  disableButtons(true)
  iterator = Sequence( [ F_1, T_L, F_1, T_R, F_1, T_R, F_1, F_1, T_R, F_1, F_1, T_R, F_1, R_0  ] )
  nextSequence()
  }
Bt_Tab_B.onclick=_=>
  { 
  disableButtons(true)
  iterator = Sequence( [ T_L, F_1, T_R, F_1, T_R, F_1, T_R, F_1, R_0 ] )
  nextSequence()
  }
robot.addEventListener('transitionend',  nextSequence )
:root {
  --turn  : 0deg;
  --PosT  : 110px;
  --PosL  : 90px;
}
#robot {
  font-size   : 16px;
  width       : 30px;
  height      : 30px;
  background-color: aqua;
  text-align  : center;
  line-height : 1.8em;
  transition  : all .5s linear;
  transform   : rotate( var(--turn) );
  position:fixed;
  top : var(--PosT);
  left: var(--PosL);
}
<div id="robot">R</div>

<button id="Bt_Tab_A"> Sequence A</button>
<button id="Bt_Tab_B"> Sequence B</button>

Советы и советы приветствуются;)

1 Ответ

2 голосов
/ 01 июля 2019

В вашем случае я чувствую, что Promise - это путь.

По моему собственному эмпирическому правилу (будьте осторожны, он самоуверенный):

  • Используйте обещание, когда вам нужнодля выполнения асинхронных операций последовательно .
  • Используйте функцию генератора, когда вам нужно сгенерировать результат , когда вам это нужно.

В вашем фрагментевсе, что вам нужно сделать, это запустить анимацию последовательно , а не , когда вам это нужно.

Кроме того, вы заметите, что у вас есть несколько функций, которые зависят от текущего состояния (результата) одной переменной (т. Е. iterator).Плохо в этом то, что когда между вашей последовательностью возникает ошибка, вам потребуется больше времени и усилий для отладки ситуации, потому что одно изменение в одной функции может повлиять на другие функции.У вас также есть глобальный transitionend прослушиватель событий, который с первого взгляда весьма потребляет.

Короче говоря, используя функции генератора, последовательность последовательных операций трудно понять.

Подходниже используется async / await.Изменяются только методы Sequence и nextSequence (поясните комментарии внутри).Каждая операция содержится в своей области видимости.Уменьшается зависимость от глобальных переменных:

(Извините, я отформатировал код в соответствии со своим стилем кода, когда писал их)

const Root = document.documentElement;
const gRoot = window.getComputedStyle(Root);
const moving = [
  {
    T: -30,
    L: 0
  },
  {
    T: 0,
    L: +30
  },
  {
    T: +30,
    L: 0
  },
  {
    T: 0,
    L: -30
  }
];

let RotateDeg = 0;
let RotateMov = 0;
let posT = parseInt(gRoot.getPropertyValue('--PosT'));
let posL = parseInt(gRoot.getPropertyValue('--PosL'));

function F_1(){
  posT += moving[RotateMov].T;
  posL += moving[RotateMov].L;

  Root.style.setProperty('--PosT', posT + 'px');
  Root.style.setProperty('--PosL', posL + 'px');
}

function T_L(){
  RotateMov = (RotateMov + 3) % 4;
  RotateDeg -= 90;
  Root.style.setProperty('--turn', RotateDeg + 'deg');
}

function T_R(){
  RotateMov = (RotateMov + 1) % 4;
  RotateDeg += 90;
  Root.style.setProperty('--turn', RotateDeg + 'deg');
}

function R_0(){
  RotateMov = 0;
  RotateDeg = 0;
  Root.style.setProperty('--turn', RotateDeg + 'deg');
}

function disableButtons(OnOff){
  Bt_Tab_A.disabled = OnOff
  Bt_Tab_B.disabled = OnOff
}

async function Sequence(Tab_fct){
  // Disable buttons before start
  disableButtons(true);
  
  for (let fct of Tab_fct)
    // Run the animation one by one
    await nextSequence(fct);

  // Reenable buttons before end
  disableButtons(false);
}

function nextSequence(fct){
  return new Promise(res => {
    // Move event listener here so that they dont depend on a global one.
    // Use { once: true } to run this callback only once
    window.addEventListener('transitionend', res, { once: true });
    
    // Run the animation
    fct();
  })
}


Bt_Tab_A.onclick = () => {
  Sequence([F_1, T_L, F_1, T_R, F_1, T_R, F_1, F_1, T_R, F_1, F_1, T_R, F_1, R_0]);
}

Bt_Tab_B.onclick = () => {
  Sequence([ T_L, F_1, T_R, F_1, T_R, F_1, T_R, F_1, R_0 ]);
}
:root {
  --turn  : 0deg;
  --PosT  : 110px;
  --PosL  : 90px;
}
#robot {
  font-size   : 16px;
  width       : 30px;
  height      : 30px;
  background-color: aqua;
  text-align  : center;
  line-height : 1.8em;
  transition  : all .5s linear;
  transform   : rotate( var(--turn) );
  position:fixed;
  top : var(--PosT);
  left: var(--PosL);
}
<div id="robot">R</div>

<button id="Bt_Tab_A">Sequence A</button>
<button id="Bt_Tab_B">Sequence B</button>
...