Кроссбраузерный zoom?

151
30 ноября 2019, 04:10

Почему к примеру на Firefox не работает zoom, и работает moz-transform: scale - криво? Zoom масштабирует целиком, если использовать в html, body, на весь экран... transform: scale - если уменьшаем, то уменьшает и оставляет черные полосы по бокам и сверху, как-бы экран в экране получается. Есть альтернатива зуму для Лисы?

Пример: Браузер Лиса со scale:

Пример: Браузер Хром с zomm:

Видите разницу?

Answer 1

Приниципиальное отличие transform от zoom в том, что zoom меняет реальный размер элемента, и это сказывается на положении соседних элементов и на всём размере страницы. transform же — чисто декоративная штука, она визуально меняет элемент, но в плане расчёта геометрии размеры элемента остаются стандартными и никак не влияют на основную страницу. В примере ниже можно наглядно увидеть разницу между transform: scale и zoom (запускать нужно в браузере Chrome, так как Firefox на момент написания данного ответа не поддерживает zoom):

.ramka { 
  display: inline-block; 
  vertical-align: top; 
  border: 1px solid black; 
  margin: 0 20px; 
  font-family: Arial, sans-serif; 
  font-size: 10px; 
} 
 
.demo-transform { 
  transform: scale(1.4); 
  transform-origin: left top; 
} 
 
.demo-zoom { 
  zoom: 1.4; 
} 
 
.img { 
  vertical-align: top; 
}
<div class="ramka"> 
 transform: scale(1.4)<br/> 
 <div class="demo-transform"> 
   <img class="img" src="https://i.stack.imgur.com/NyEgY.png" alt="" width="100" height="100" /> 
 </div> 
</div> 
 
<div class="ramka"> 
 zoom: 1.4<br/> 
 <div class="demo-zoom"> 
   <img class="img" src="https://i.stack.imgur.com/NyEgY.png" alt="" width="100" height="100" /> 
 </div> 
</div>

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

Уменьшившуюся ширину можно исправить с помощью transform-origin: left top и width: calc(1 / 0.6) (где 0.6 это величина scale), однако это не решит проблемы с высотой, потому что она, в отличие от ширины, заранее не известна. При scale<1 снизу будет пустота, а при scale>1 низ страницы наоборот обрежется. А всё потому что transform не меняет геометрию.

К сожалению, стандартного кроссбраузерного аналога zoom я не знаю (есть какой-то @viewport zoom, но с его поддержкой всё ещё хуже, и заставить его работать я не смог).

Один из вариантов, как с этим жить, вы описали сами: использовать transform для Firefox и zoom для остальных браузеров. Судя по тому, как сильно вы «любите» Лису, этот вариант вас вполне устроит :) Но о том, что zoom нестандартный и может быть в любой момент удалён из браузеров, я вас уже предупреждал. (Хотя было бы прекрасно, если бы его стандартизировали и реализовали в Firefox, но это дело далёкого будущего.)

Но можно в принципе пересчитывать всю геометрию самостоятельно с помощью JavaScript. Ниже я написал функцию, которая пересчитывает height для всех элементов с классом js-zoomed (при этом width прописывается в CSS). Вы можете добавить класс js-zoomed к элементу <html> на своём сайте, чтобы пересчитывать размеры всего сайта.

Проблемное ограничение данного способа в том, что этот скрипт нужно перезапускать при КАЖДОМ изменении размеров элементов. В примере я повесил перезапуск на браузерное событие resize (оно приводит к изменению размеров почти всегда), но если вы динамически меняете какие-то элементы какими-то скриптами, то вам надо перезапускать это вручную в коде.

Ну а ещё это просто дикий костыль с кучей ограничений. Например, у увеличеннного элемента есть торчащая вниз невидимая пустота (она образуется из-за того, height и transform действуют вместе), и эта пустота добавляет ненужную полосу прокрутки, так что пришлось исправить это с помощью overflow: hidden.

function recalcZoomedElements() { 
  var elems = document.getElementsByClassName('js-zoomed'); 
   
  // Обрабатываем элементы в обратном порядке, чтобы 
  // не было проблем с вложенными друг в друга элементами 
  for (var i = elems.length - 1; i >= 0; i--) { 
    var elem = elems[i]; 
     
    // Убираем старый расчёт height 
    elem.style.height = ''; 
     
    // Браузер посчитает высоту автоматически с учётом transform; 
    // забираем её значение себе 
    var rect = elem.getBoundingClientRect(); 
    var newHeight = rect.height; 
     
    // Ставим посчитанную браузером высоту, изменяя геомертию 
    elem.style.height = newHeight + 'px'; 
  } 
} 
 
// Перерасчёт при первой загрузке страницы 
recalcZoomedElements(); 
 
// Перерасчёт при каждом изменении размера окна браузера 
window.addEventListener('resize', function() {recalcZoomedElements()});
.ramka { 
  display: inline-block; 
  vertical-align: top; 
  border: 1px solid black; 
  margin: 0 10px; 
  font-family: Arial, sans-serif; 
  font-size: 10px; 
 
  /* Вот так имитируем окно браузера для данного примера: */ 
  /* его ширина заране известна и за пределы окна ничего не вылезает */ 
  width: 140px; 
  overflow: hidden; 
} 
 
.demo-transform { 
  transform: scale(1.4); 
  width: calc(100% / 1.4); 
  transform-origin: left top; 
} 
 
.demo-zoom { 
  zoom: 1.4; 
} 
 
.img { 
  vertical-align: top; 
}
<div class="ramka"> 
 transform: scale(1.4) no js<br/> 
 <div class="demo-transform"> 
   <img class="img" src="https://i.stack.imgur.com/NyEgY.png" alt="" width="100" height="100" /> 
 </div> 
</div> 
 
<div class="ramka"> 
 transform: scale(1.4) with js<br/> 
 <div class="demo-transform js-zoomed"> 
   <img class="img" src="https://i.stack.imgur.com/NyEgY.png" alt="" width="100" height="100" /> 
 </div> 
</div> 
 
<div class="ramka"> 
 zoom: 1.4<br/> 
 <div class="demo-zoom"> 
   <img class="img" src="https://i.stack.imgur.com/NyEgY.png" alt="" width="100" height="100" /> 
 </div> 
</div>

Answer 2

Почему бы не использовать transform: scale()?

.zoom { 
  display: block; 
  width: 100px; 
  height: 100px; 
} 
 
.zoom img { 
  width: 100%; 
  height: 100%; 
} 
 
.zoom:hover { 
  -webkit-transform: scale(2); 
  -moz-transform: scale(2); 
  -ms-transform: scale(2); 
  -o-transform: scale(2); 
  transform: scale(2); 
}
<div class="zoom"> 
  <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Firefox_Developer_Edition_logo.png"> 
</div>

Answer 3

Вот рабочее решение:

@media (max-width: 1680px) and (min-width: 1601px) {
 html {
    zoom: 87.5%;
 }
@-moz-document url-prefix() {
 html {
    transform: scale(0.875);
    transform-origin: left top;
    width: calc(100% / 0.875);
    height: calc(100% / 0.875);
    }
  }
}

На всякий случай немного объясню:

@media (max-width: 1680px) and (min-width: 1601px)

Промежуток в котором будет работать код исходя из ширины экрана.

Zoom - для современных браузеров, оставляем, ниже прописываем конкретно для Мозилы/Огненной Лисы (только они воспримут код):

@-moz-document url-prefix() {....}

У меня работает.

READ ALSO
Удалить свойство из обьекта?

Удалить свойство из обьекта?

У меня есть обьект такого вида:

132
Проблемы с подключением typed.js

Проблемы с подключением typed.js

Возникли проблемы с подключением библиотеки typedjs

123
Непонятный синтаксис в коде. @withRouter и тд?

Непонятный синтаксис в коде. @withRouter и тд?

Что значит @ в этом контексте кода? Не могу врубитсяСкрин ниже

109
Установить время конца суток JS

Установить время конца суток JS

Как определить время конца сегодняшних сутокЕсли сейчас дата например:

134