Адаптивный SVG viewBox

Я сделал «кнопку гамбургера» в SVG, как показано ниже.

body {
  margin: 0;
  text-align: center;
}

svg#ham-btn {
  margin: 2rem;
  border: 1px solid black;
  fill: #383838;
}
<svg id = "ham-btn" width = "107.5px" height = "80px" viewBox = "0 0 80 80" preserveAspectRatio = "xMidYMid meet" xmlns = "http://www.w3.org/2000/svg">
  <style>
    #ham-btn {
      cursor: pointer;
    }
    #ham-btn:hover #r1 {
      outline: 1px solid transparent;
      transition: transform 0.2s;
      transform-origin: 50% 50%;
      transform: rotate(37deg) translate(0%, 28.75%) scaleX(1.1);
    }
    #ham-btn:hover #r2 {
      transition: transform 0.2s;
      transform: translate(120%, 0);
    }
    #ham-btn:hover #r3 {
      outline: 1px solid transparent;
      transition: transform 0.2s;
      transform-origin: 50% 50%;
      transform: rotate(-37deg) translate(0%, -28.75%) scaleX(1.1);
    }
    
  </style>
  <rect id = "r3" x = "0" y = "71.25%" width = "100%" height = "15%" rx = "5%" />
  <rect id = "r2" x = "0" y = "42.5%" width = "100%" height = "15%" rx = "5%"/>
  <rect id = "r1" x = "0" y = "13.75%" width = "100%" height = "15%" rx = "5%" />
</svg>

Вопрос

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

Есть ли способ сделать это более отзывчивым? Например, сделав viewBox процентом от области просмотра?

Согласно спецификации, кажется, что viewBox должен быть <number> и, следовательно, не может быть процентным.

Есть предположения?

Спасибо.


Примечание 1

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


Заметка 2

Я знаю, что могу сделать область просмотра SVG процентом от ее контейнера / области просмотра браузера.

К сожалению, это не решает мою проблему, поскольку для того, чтобы эта кнопка работала, мое соотношение viewBox-to-viewport должно оставаться неизменным (в противном случае кнопка теряет свою форму).

Таким образом, я хотел, чтобы viewBox находился в процентах от области просмотра.

В случае, если это интересно, в моем решении используются следующие преимущества:

  • viewBox шире окна просмотра, но имеет такую ​​же высоту
  • Таким образом, preserveAspectRatio используется для горизонтального центрирования rect.
  • Размеры rect указаны в процентах от viewBox.

outline предназначен для исправления проблемы Firefox с неровными линиями после преобразования.


Редактировать:

Если это будет полезно для будущих посетителей, последнюю кнопку, включая полную доступность, можно найти здесь: https://codepen.io/magnusriga/pen/KrrJKa

Я бы удалил ширину и высоту элемента svg. Тогда в CSS я бы сделал SVG width:50%. Вам не нужно декларировать высоту, если вам не нужно сжать / растянуть изображение.

enxaneta 10.12.2018 09:09

не заинтересованы в чистом решении CSS? как этот: stackoverflow.com/questions/53555133/… или этот: stackoverflow.com/questions/53270861/…

Temani Afif 10.12.2018 11:44

Привет @TemaniAfif. Спасибо за ссылки. Я также ранее реализовал меню в HTML + CSS, но недавно погрузился в SVG (как вы знаете) и нашел его невероятно эффективным для графики и анимации. На мой взгляд, для гамбургер-меню SVG кажется более простым и менее хакерским подходом по сравнению с HTML + CSS с абсолютными элементами.

Magnus 10.12.2018 14:26

Теперь мне просто нужно понять, почему установка ширины / высоты SVG в CSS - это не то же самое, что указание этого в самих атрибутах представления svg (как я думал, это сказано в спецификации).

Magnus 10.12.2018 14:27

@enxaneta Начальное значение атрибутов представления ширины / высоты в элементе SVG составляет 100% (родительского контейнера), если я правильно понимаю. Удаление высоты нарушит разницу в соотношении ширины / высоты между окном просмотра SVG и окном просмотра, от которого зависит центрирование окна просмотра (он использует preserveAspectRatio).

Magnus 10.12.2018 14:31
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
1
5
8 626
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Добавьте свойства ширины и высоты в свой svg css. С установленной шириной и автоматической высотой.

body {
  margin: 0;
  text-align: center;
}

svg#ham-btn {
  margin: 2rem;
  border: 1px solid black;
  fill: #383838;
  /* percentage of viewport - height will autocalculate */
  width: 7vw;
  height: auto;
}
<svg id = "ham-btn" width = "107.5px" height = "80px" viewBox = "0 0 80 80" preserveAspectRatio = "xMidYMid meet" xmlns = "http://www.w3.org/2000/svg">
  <style>
    #ham-btn {
      cursor: pointer;
    }
    #ham-btn:hover #r1 {
      transition: transform 0.2s;
      transform-origin: 50% 50%;
      transform: rotate(37deg) translate(0%, 28.75%) scaleX(1.1);
    }
    #ham-btn:hover #r2 {
      transition: x 0.2s;
      x: 120%;
    }
    #ham-btn:hover #r3 {
      transition: transform 0.2s;
      transform-origin: 50% 50%;
      transform: rotate(-37deg) translate(0%, -28.75%) scaleX(1.1);
    }
    
  </style>
  <rect id = "r3" x = "0" y = "71.25%" width = "100%" height = "15%" rx = "5%" />
  <rect id = "r2" x = "0" y = "42.5%" width = "100%" height = "15%" rx = "5%"/>
  <rect id = "r1" x = "0" y = "13.75%" width = "100%" height = "15%" rx = "5%" />
</svg>

Привет. Я знаю, что могу сделать окно просмотра svg процентным от его контейнера. Я думал о viewBox.

Magnus 10.12.2018 04:35

@Magnus какая-либо конкретная причина изменения атрибута viewBox в HTML / SVG? Afaik, нет возможности изменить это через CSS, но JS может помочь, хотя и не обязательно. Добиться желаемого эффекта (если я правильно понял) можно, задав ширину / высоту.

Ludovit Mydla 10.12.2018 04:41

Если вы измените ширину / высоту области просмотра без изменения viewBox, кнопка потеряет свою форму. Я просто добавил несколько примечаний к OP, в которых объясняется, как работает мое решение.

Magnus 10.12.2018 04:45

PS: Черный ящик вокруг гамбургера (область просмотра) должен находиться на точно таком же расстоянии% от прямоугольников при любом размере контейнера. Итак, если вы сделаете его тоньше, как вы это делали, весь svg потеряет свою форму. Черный ящик (окно просмотра) и гамбургер принадлежат друг другу. При наведении курсора крестик аккуратно указывает на углы этого черного ящика (область просмотра).

Magnus 10.12.2018 04:48

@Magnus Я обновил ответ - добавил height: auto Это должно сохранить соотношение сторон.

Ludovit Mydla 10.12.2018 04:52

Я думал, что установка ширины / высоты svg в CSS перезапишет собственную ширину / высоту окна просмотра SVG (107,5 пикселей и 80 пикселей). Разве это не так?

Magnus 10.12.2018 04:54

Кроме того, есть идеи, почему края выглядят неровными после преобразования в течение примерно 0,5 секунды в Firefox? То же самое не происходит в хроме.

Magnus 10.12.2018 05:00

@ Магнус, да. Свойства CSS "перезаписывают" атрибуты HTML по умолчанию.

Ludovit Mydla 10.12.2018 05:00

Я не думаю, что он перезаписывает размеры области просмотра svg 107,5 пикселей и 80 пикселей? Если вы попытаетесь изменить их вручную, не меняя viewBox, вы увидите, что форма теряет свою форму.

Magnus 10.12.2018 05:03

Я исправил проблемы с Firefox.

Magnus 10.12.2018 05:24

Думаю, я понимаю, почему ваше решение работает. Это связано с тем, что явно заданные ширина и высота области просмотра используются только для определения внутреннего соотношения сторон, отличного от того, что установил бы viewBox (если ширина / высота не были явно указаны). Фактическая ширина и высота области просмотра затем перезаписываются CSS, но внутреннее соотношение сторон svg сохраняется (107,5 / 80). Это соотношение сторон затем используется при указании height: auto в CSS. Если бы мы не указали auto в CSS, высота упала бы до 80 пикселей, как установлено svg. Если это так, не могли бы вы добавить это объяснение к своему ответу?

Magnus 10.12.2018 19:54

Как я уже сказал в своем комментарии: я бы удалил ширину и высоту элемента svg. Затем в CSS я бы сделал ширину SVG: 20%. Я делаю ширину SVG 50% ширины окна.

Чтобы сохранить пропорции, я положил «гамбургер» внутрь <symbol viewBox = "0 0 80 80">. Грубо говоря, в этом случае preserveAspectRatio = "xMidYMid meet" вам больше не нужен.

body {
  margin: 0;
  text-align: center;
}

svg#ham-btn {
  width: 20%;
  margin:auto;
  top:0;bottom:0;left:0;right:0;
  border: 1px solid black;
  fill: #383838;
  position:absolute; 
}
<svg id = "ham-btn" viewBox = "0 0 107.5 80" preserveAspectRatio = "xMidYMid meet" xmlns = "http://www.w3.org/2000/svg">
  <style>
    #ham-btn {
      cursor: pointer;
    }
    #ham-btn:hover #r1 {
      outline: 1px solid transparent;
      transition: transform 0.2s;
      transform-origin: 50% 50%;
      transform: rotate(37deg) translate(0%, 28.75%) scaleX(1.1);
    }
    #ham-btn:hover #r2 {
      transition: transform 0.2s;
      transform: translate(120%, 0);
    }
    #ham-btn:hover #r3 {
      outline: 1px solid transparent;
      transition: transform 0.2s;
      transform-origin: 50% 50%;
      transform: rotate(-37deg) translate(0%, -28.75%) scaleX(1.1);
    }
    
  </style>
  <symbol id = "s" viewBox = "0 0 80 80"> 
  <rect id = "r3" x = "0" y = "71.25%" width = "100%" height = "15%" rx = "5%" />
  <rect id = "r2" x = "0" y = "42.5%" width = "100%" height = "15%" rx = "5%"/>
  <rect id = "r1" x = "0" y = "13.75%" width = "100%" height = "15%" rx = "5%" />
  </symbol>  
  
  <use xlink:href = "#s"  width = "80" x = "13.75"/>
</svg>

Интересное решение, создание другого окна просмотра во внешнем окне просмотра. Я предполагаю, что другим вариантом было бы использовать вложенный svg вместо символа, поскольку они оба устанавливают новое окно просмотра. В любом случае, не могли бы вы добавить объяснение того, как все измерения связаны вместе, то есть каковы каждое начальное и вычисленное значения. Я предполагаю, что ширина / высота внутреннего окна просмотра составляет 100% (начальное) от размеров внешней ширины, что, в свою очередь, составляет 100% от родительского контейнера. Однако мы устанавливаем ширину в CSS, поэтому внешний svg получает ширину области просмотра 20%, а затем использует внутреннее измерение SVG для заполнения высоты. 1/2

Magnus 10.12.2018 18:40

2/3 .... Внутренние размеры (согласно спецификации: w3.org/TR/SVG2/coords.html#SizingSVGInCSS) равны viewBox.width / viewBox.height (поскольку мы не указали и ширину области просмотра, и высоту области просмотра как абсолютные значения). Наконец, это означает, что внутренний и внешний видовой экран равны по высоте / ширине, и оба имеют внутреннее соотношение сторон 107,5 / 80, которое используется CSS для вычисления высоты (начальное значение которого, если не указано, равно auto).

Magnus 10.12.2018 18:45

3/3 ... Все это означает, что нам фактически не нужно созданное вами внутреннее окно просмотра. У нас может быть только одно окно просмотра (как в OP), сохранить ширину и высоту области просмотра, чтобы сделать внутренние размеры 107/80, затем изменить ширину в CSS по желанию и установить высоту на auto (чтобы она не уменьшалась до 80 пикселей. ). В этом решении явно заданные ширина и высота области просмотра используются только для установки внутреннего измерения, отличного от того, что указывает viewBox. Фактическая ширина и высота области просмотра перезаписываются CSS.

Magnus 10.12.2018 18:48

Другие вопросы по теме