Как перезапустить обновленную анимацию после динамического изменения ключевых кадров? - PullRequest
0 голосов
/ 28 мая 2018

Я пытаюсь создать выделение (да, я сначала сделал МНОГО поиска по этой теме), используя анимированный text-indent.Я предпочитаю это решение другим, которые я пробовал, например, использовать перевод на 100%, что приводит к утечке текста за границы моего выделения.

Я пытался следовать этому примеру здесь: https://www.jonathan -petitcolas.com / 2013/05/06 / simulate-marquee-tag-in-css-and-javascript.html

... которую я немного обновил, выполняяэто в TypeScript с использованием обновлений API (appendRule вместо insertRule) и отбрасыванием опасений по поводу поддержки старого браузера.

Проблема в том, что анимация перезапускается с использованием старых правил ключевых кадров - шаг, описанныйкомментарий «переназначить анимацию (чтобы она запускалась)» не работает.

Я посмотрел, что происходит в отладчике, и правила определенно меняются - старые правила удалены,добавлены новые правила.Но как будто старые правила где-то кешируются, и они не очищаются.

Вот мой текущий CSS:

#marquee {
  position: fixed;
  left: 0;
  right: 170px;
  bottom: 0;
  background-color: midnightblue;
  font-size: 14px;
  padding: 2px 1em;
  overflow: hidden;
  white-space: nowrap;
  animation: none;
}

#marquee:hover {
  animation-play-state: paused;
}

@keyframes marquee-0 {
  0% {
    text-indent: 450px;
  }

  100% {
    text-indent: -500px;
  }
}

И соответствующий раздел моего TypeScript:

function updateMarqueeAnimation() {
  const marqueeRule = getKeyframesRule('marquee-0');

  if (!marqueeRule)
    return;

  marquee.css('animation', 'unset');

  const element = marquee[0];
  const textWidth = getTextWidth(marquee.text(), element);
  const padding = Number(window.getComputedStyle(element).getPropertyValue('padding-left').replace('px', '')) +
                  Number(window.getComputedStyle(element).getPropertyValue('padding-right').replace('px', ''));
  const offsetWidth = element.offsetWidth;

  if (textWidth + padding <= offsetWidth)
    return;

  marqueeRule.deleteRule('0%');
  marqueeRule.deleteRule('100%');
  marqueeRule.appendRule('0% { text-indent: ' + offsetWidth + 'px; }');
  marqueeRule.appendRule('100% { text-indent: -' + textWidth + 'px; }');

  setTimeout(() => marquee.css('animation', 'marquee-0 15s linear infinite'));
}

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

Следующее, что я, вероятно, попробую, - это динамическое создание новых объектов ключевых кадров вместо редактированияправила внутри существующего объекта ключевых кадров, но это грязное решение, которого я бы предпочел избегать, если у кого-то есть лучшее решение.

1 Ответ

0 голосов
/ 28 мая 2018

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

let animationStyleSheet: CSSStyleSheet;
let keyframesIndex = 0;
let lastMarqueeText = '';

function updateMarqueeAnimation(event?: Event) {
  const newText = marquee.text();

  if (event === null && lastMarqueeText === newText)
    return;

  lastMarqueeText = newText;
  marquee.css('animation', 'none');

  const element = marquee[0];
  const textWidth = getTextWidth(newText, element);
  const padding = Number(window.getComputedStyle(element).getPropertyValue('padding-left').replace('px', '')) +
                  Number(window.getComputedStyle(element).getPropertyValue('padding-right').replace('px', ''));
  const offsetWidth = element.offsetWidth;

  if (textWidth + padding <= offsetWidth)
    return;

  if (!animationStyleSheet) {
    $('head').append('<style id="marquee-animations" type="text/css"></style>');
    animationStyleSheet = ($('#marquee-animations').get(0) as HTMLStyleElement).sheet as CSSStyleSheet;
  }

  if (animationStyleSheet.cssRules.length > 0)
    animationStyleSheet.deleteRule(0);

  const keyframesName = 'marquee-' + keyframesIndex++;
  const keyframesRule = `@keyframes ${keyframesName} { 0% { text-indent: ${offsetWidth}px } 100% { text-indent: -${textWidth}px; } }`;
  const seconds = (textWidth + offsetWidth) / 100;

  animationStyleSheet.insertRule(keyframesRule, 0);
  marquee.css('animation', `${keyframesName} ${seconds}s linear infinite`);
}

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

Другое дело, что я вообще не хочу, чтобы текст прокручивался, если он хорошо помещается без прокрутки.

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