Как исчезнуть в движущемся элементе в определенном месте на странице? - PullRequest
0 голосов
/ 25 января 2019

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

Вот пример, который работает в Chromium, но не в Firefox:

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
}
@keyframes mover {
  from {
    left: 0vmin;
    opacity: -3;
  }
  to {
    left: 90vmin;
    opacity: 6;
  }
}
.mover {
  background: red;
  animation: mover 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}
<div class="mover" style="animation-timing-function: linear;"></div>
<div class="mover" style="animation-timing-function: ease-in;"></div>
<div class="mover" style="animation-timing-function: ease-out;"></div>
<div class="marker"></div>

В Chromium все красные блоки постепенно исчезают точно над синим блоком, благодаря интерполяции opacity вне его предполагаемого диапазона [0, 1]. Похоже, что Chromium сначала интерполирует непрозрачность, а затем ограничивает ее до диапазона [0, 1]. Это грязный трюк, но он вроде работает.

Похоже, что Firefox делает обратное: сначала он привязывается к [0, 1], а затем применяет интерполяцию. Результатом является плавное постепенное увеличение от дальнего левого до дальнего правого пути.

Я могу заставить его сломаться и в Chromium, анимировав transform вместо left, что, по всей логике, не должно иметь значения:

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
}
@keyframes mover {
  from {
    transform: translate(0vmin, 0);
    opacity: -3;
  }
  to {
    transform: translate(90vmin, 0);
    opacity: 6;
  }
}
.mover {
  background: red;
  animation: mover 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}
<div class="mover" style="animation-timing-function: linear;"></div>
<div class="mover" style="animation-timing-function: ease-in;"></div>
<div class="mover" style="animation-timing-function: ease-out;"></div>
<div class="marker"></div>

Какой браузер здесь правильный? И что еще более важно, как мне добиться этого эффекта, не прибегая к JavaScript?

Точные версии браузера:

  • Chromium 71.0.3578.98 (официальная сборка) Arch Linux (64-битная версия)
  • Firefox 64.0 (64-разрядная версия)

1 Ответ

0 голосов
/ 25 января 2019

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

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

Для этого я рассмотрю псевдоэлемент, ширина которого будет равна ширине пути (90vmin - 0vmin = 90vmin), который будет анимирован в противоположном направлении. этот элемент будет иметь градиентную окраску, и эта окраска создаст magic .

Вот пример:

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
  z-index:0;
  overflow:hidden;
  color:#fff;
}
.mover:before {
  content:"";
  position:absolute;
  z-index:99;
  top:-1px;
  bottom:-1px;
  left:0;
  width:90vmin;
  background:linear-gradient(to right,#fff 33%,transparent);
  animation: fader 10s infinite;
  animation-timing-function:inherit;
}

.mover {
  background: red;
  animation: mover 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}

@keyframes mover {
  from {
    left: 0vmin;
  }
  to {
    left: 90vmin;
  }
}
@keyframes fader {
  to {
    transform:translateX(-100%);
  }
}
<div class="mover" style="animation-timing-function: linear;">1</div>
<div class="mover" style="animation-timing-function: ease-in;">2</div>
<div class="mover" style="animation-timing-function: ease-out;">3</div>
<div class="marker"></div>

Чтобы лучше понять, что происходит, давайте изменим окраску и уберем лишний поток:

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
  z-index:0;
  color:#fff;
  /*overflow:hidden;*/
}
.mover:before {
  content:"";
  position:absolute;
  z-index:99;
  top:-1px;
  bottom:-1px;
  left:0;
  width:90vmin;
  background:linear-gradient(to right,green 33%,transparent);
  animation: fader 10s infinite;
  animation-timing-function:inherit;
}

.mover {
  background: red;
  animation: mover 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}

@keyframes mover {
  from {
    left: 0vmin;
  }
  to {
    left: 90vmin;
  }
}
@keyframes fader {
  to {
    transform:translateX(-100%);
  }
}
<div class="mover" style="animation-timing-function: linear;">1</div>
<div class="mover" style="animation-timing-function: ease-in;">2</div>
<div class="mover" style="animation-timing-function: ease-out;">3</div>
<div class="marker"></div>

Мы ясно видим, что псевдоэлемент не движется, потому что он просто движется в противоположном направлении, как основной элемент, и с той же скоростью. С нашей точки зрения это исправлено. Затем мы окрашиваем элемент до 33%, то есть 30vmin, поэтому наш элемент начнет отображаться в этот момент.

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


Другая идея состоит в том, чтобы полагаться только на фон, настраивая и анимируя background-size / background-position, таким образом, псевдоэлемент будет просто иметь тот же размер, что и основной элемент:

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
  z-index:0;
  overflow:hidden;
  color:#fff;
}
.mover:before {
  content:"";
  position:absolute;
  z-index:99;
  top:-1px;
  bottom:-1px;
  left:0;
  right:0;
  background-image:linear-gradient(to right,#fff 33%,transparent);
  background-size:90vmin 100%;
  background-position:left;
  animation: fader 10s infinite;
  animation-timing-function:inherit;
}

.mover {
  background: red;
  animation: mover 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}

@keyframes mover {
  from {
    left: 0vmin;
  }
  to {
    left: 90vmin;
  }
}
@keyframes fader {
  to {
    background-position:right;
  }
}
<div class="mover" style="animation-timing-function: linear;">1</div>
<div class="mover" style="animation-timing-function: ease-in;">2</div>
<div class="mover" style="animation-timing-function: ease-out;">3</div>
<div class="marker"></div>

И чтобы контролировать эффект затухания, мы увеличиваем background-size на любой множитель 90vmin

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
  z-index:0;
  overflow:hidden;
  color:#fff;
}
.mover:before {
  content:"";
  position:absolute;
  z-index:99;
  top:-1px;
  bottom:-1px;
  left:0;
  right:0;
  background-image:linear-gradient(to right,#fff 33%,transparent);
  background-size:calc(var(--m,1)*90vmin) 100%;
  background-position:left;
  animation: fader 10s infinite;
  animation-timing-function:inherit;
}

.mover {
  background: red;
  animation: mover 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}

@keyframes mover {
  from {
    left: 0vmin;
  }
  to {
    left: 90vmin;
  }
}
@keyframes fader {
  to {
    background-position:right;
  }
}
<div class="mover" style="animation-timing-function: linear;">1</div>
<div class="mover" style="animation-timing-function: ease-in;--m:5">2</div>
<div class="mover" style="animation-timing-function: ease-out;--m:20">3</div>
<div class="marker"></div>

Чем больше размер, тем ближе мы к эффекту непрозрачности.

UPDATE

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

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
  color:#fff;
}

.mover {
  background: red;
  animation: mover 10s infinite,
             fader 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}

@keyframes mover {
  from {
    left: 0vmin;
  }
  to {
    left: 90vmin;
  }
}
@keyframes fader {
  from {
    clip-path:polygon(30vmin 0%,  100vmin 0%,
                                  100vmin 100%,
                      30vmin 100% );
  }
  to {
    clip-path:polygon(-60vmin 0%,  10vmin 0%,
                                   10vmin 100%,
                      -60vmin 100% );
  }
}

html {
  background:radial-gradient(pink,yellow);
}
<div class="mover" style="animation-timing-function: linear;">1</div>
<div class="mover" style="animation-timing-function: ease-in;">2</div>
<div class="mover" style="animation-timing-function: ease-out;">3</div>
<div class="marker"></div>

Вот иллюстрация, чтобы лучше понять, что происходит:

enter image description here

Оранжевый прямоугольник - это область обрезки. Он должен начинаться с маркера (30vmin) и заканчиваться на end с учетом ширины элемента (90vmin + 10vmin = 100vmin). Это означает, что изначально мы не увидим элемент.

Эта область должна оставаться фиксированной для нашей перспективы, чтобы видеть элемент только тогда, когда он достигает маркера, поэтому мы должны переместить его в противоположном направлении, и для этого мы просто изменим clip-path.

После завершения анимации мы получим следующее:

enter image description here

Обрезанная область должна заканчиваться в конце элемента (10vmin), и она должна сохранять тот же размер, поэтому она должна начинаться с (-60vmin).


Теперь мы можем увеличить область clip-path, как мы сделали с background-size, чтобы управлять эффектом fading , и мы можем добавить анимацию непрозрачности и приблизиться к начальному требованию.

.mover, .marker {
  position: relative;
  width: 10vmin;
  height: 10vmin;
  color:#fff;
}

.mover {
  background: red;
  animation: mover 10s infinite,
             fader 10s infinite;
}
.marker {
  background: blue;
  left: 30vmin;
}

@keyframes mover {
  from {
    left: 0vmin;
  }
  to {
    left: 90vmin;
  }
}
@keyframes fader {
  from {
    clip-path:polygon(300vmin 0%,  1000vmin 0%, /* x10*/
                                  1000vmin 100%,
                      300vmin 100% );
    opacity:0;
  }
  to {
    clip-path:polygon(-600vmin 0%,  100vmin 0%,
                                   100vmin 100%,
                      -600vmin 100% );
    opacity:1;
  }
}

html {
  background:radial-gradient(pink,yellow);
}
<div class="mover" style="animation-timing-function: linear;">1</div>
<div class="mover" style="animation-timing-function: ease-in;">2</div>
<div class="mover" style="animation-timing-function: ease-out;">3</div>
<div class="marker"></div>


Стоит отметить, что путь клипа все еще не поддерживается широко (https://caniuse.com/#search=clip-path) ивам нужно добавить префикс -webkit-, чтобы закрыть браузер Safari.

...