CSS transition scale () - странная задержка - PullRequest
0 голосов
/ 03 марта 2019

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

Это достигается путем уменьшения дочернего компонента в том же соотношении, в котором его родительский масштаб увеличен (родительский масштаб уменьшен до4, потомок масштабируется до 0,25).

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

Это почти как если бы родительсначала масштабируется, а затем завершает дочерние масштабы.

Это какое-то ограничение браузера?Или я что-то не так делаю?

Спасибо!

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: 100px;
  height: 100px;
  overflow: hidden;
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transform-origin: top left;
  transition: transform 1s;
  cursor: pointer;
  border: 1px solid black;
}

.outer--active {
  transform: scale(4) translate(-50%, -50%);
}

.inner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 400px;
  height: 400px;
  background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center repeat;
  transform-origin: top left;
  transition: transform 1s;
}

.outer--active .inner {
  transform: scale(0.25) translate(-50%, -50%);
}
<div class="outer">
  <div class="inner"></div>
</div>

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Вот аналогичные идеи, как и в другом ответе, где вы можете сделать это только с одним элементом.

Увеличение ширины / высоты.

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: calc(100px * var(--s,1));
  height: calc(100px * var(--s,1));
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: 
    url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center ;
  transition: all 1.5s;
  cursor: pointer;
  border: 1px solid black;
}

.outer--active {
  --s:4;
}
<div class="outer">
</div>

Учитывая clip-path, где я добавляю radial-gradient для создания границы

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: 400px;
  height: 400px;
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: 
    radial-gradient(farthest-side,transparent calc(100% - 3px),#000 100%),
    url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg');
  background-size:40% 40%,auto;
  background-position:center;
  transition: all 1.5s;
  cursor: pointer;
  -webkit-clip-path: circle(20% at 50% 50%);
  clip-path: circle(20% at 50% 50%);
}

.outer--active {
  -webkit-clip-path: circle(50% at 50% 50%);
  clip-path: circle(50% at 50% 50%);
  background-size:100% 100%,auto;
}
<div class="outer">
</div>

Вы также можете рассмотреть только radial-gradient, но область щелчка будет больше, и у вас не будет прозрачности:

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: 400px;
  height: 400px;
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: 
    radial-gradient(farthest-side,transparent calc(40% - 3px),#000 40%,#fff calc(40% + 1px)),
    url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg');
  background-size:100% 100%,auto;
  background-position:center;
  transition: all 1.5s;
  cursor: pointer;
}

.outer--active {
  background-size:240% 240%,auto;
}
<div class="outer">
</div>
0 голосов
/ 03 марта 2019

Проблема

То, что вы видите, это не "ошибка браузера", а скорее недоразумение, как работает вычисление двух комбинированных масштабов.

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

Поскольку мы хотим, чтобы конечная шкала внутреннего элемента оставалась постоянной, то (масштабирование вверхфункция) × (функция уменьшения) = 1 для всех временных аргументов.К сожалению, если мы сделаем умножение, в результате мы получим квадратную функцию (в нашем случае это -¾x² + 3x + ¾).Это удар по окончательному масштабированию, который вы можете увидеть в середине перехода.Чтобы избежать этого, вместо перехода к значению scale(n) нам нужно было бы масштабировать правило m in scale(1/m) css.К сожалению, мы не можем этого сделать, даже если мы использовали переменные css, поскольку они (пока) не допускают переходы (см. this answer)

Чтобы смягчить это, мы могли бы создатьпользовательская cubic-bezier функция синхронизации, которая была бы обратной к квадратной функции, но я не смог сделать это вручную, и, вероятно, cubic-bezier не дал бы точную кривую для всех значений времени, особенно если бы мы хотели иметь основаниефункция синхронизации, отличная от linear.

Решение

Подход 1. Вместо масштабирования мы можем изменить размеры внешнего элемента, как показано ниже:

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: 100px;
  height: 100px;
  overflow: hidden;
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transform-origin: top left;
  transition: all 1s;
  cursor: pointer;
  border: 1px solid black;
}

.outer--active {
  width: 400px;
  height: 400px;
}

.inner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 400px;
  height: 400px;
  background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center repeat;
  transform-origin: top left;
  transition: transform 1s;
}
<div class="outer">
  <div class="inner"></div>
</div>

Засглаживание (например, отчет об ошибках Firefox )


Подход 2: Используйте обтравочную маску для эффекта круглого выреза, добавьте div для границы:

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: 100px;
  height: 100px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transform-origin: top left;
  cursor: pointer;
}

.rim {
    width: 100px;
    height: 100px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    border-radius: 100%;
    border: 1px solid black;
    transition: all 1s;
    transform-origin: top left;
}

.outer--active .rim {
    transform: scale(4)  translate(-50%, -50%);
}

.inner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 400px;
  height: 400px;
  background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center repeat;
  transform-origin: top left;
  transition: all 1s;
  clip-path: circle(50px at 200px 200px);
}

.outer--active .inner {
  clip-path: circle(200px at 200px 200px);
}
<div class="outer">
  <div class="inner"></div>
  <div class="rim"></div>
</div>

Плюсы: плавно масштабируется

Минусы: необходимо добавить еще один тег HTML для круглой границы / обода.Ободок иногда может выглядеть отделенным от внутреннего образа.

...