Проблема
То, что вы видите, это не "ошибка браузера", а скорее недоразумение, как работает вычисление двух комбинированных масштабов.
Для простоты, давайте предположим, что функция переходаравно linear
(а не ease
, что является функцией синхронизации по умолчанию).В этом случае графики обеих шкал будут следующими:
Поскольку мы хотим, чтобы конечная шкала внутреннего элемента оставалась постоянной, то (масштабирование вверхфункция) × (функция уменьшения) = 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 для круглой границы / обода.Ободок иногда может выглядеть отделенным от внутреннего образа.