Очередной проект который мне приходится делать, требует этих самых круглых углов. Поговорив с заказчиком и дизайнером пришли к выводу что в Internet Explorer 8 нужны эти самые круглые углы. Что собственно и привело меня к тому что бы реализовать простейший и более быстрый способ организации таких рамочек, кнопочек и прочих фишечек на сайте. Без использования картинок и скриптов. При реализации данного метода, пришлось столкнутся с некоторыми подводными камнями и искать обходные пути. Что из себя представляет мой метод реализации и с чем мне пришлось столкнутся, читайте ниже.
Немного истории
Как мы все помним и знаем, способов реализации круглых углов в Internet Explorer, довольно много разбросано по всему интернету. Один из популярных способов является JavaScript библиотека CSS3PIE. Так же есть уйма других библиотек с использованием JavaScript. Есть простейшие способы реализации на VML с элементом v:roundrect
. А кто-то и вовсе делает углы картинками.
Каждый из способов реализации привносит свои достоинства и недостатки. Например JavaScript библиотеки очень заметно загружают браузер и производительность сайта оставляет желать лучшего, особенно это заметно в случаях когда на странице размещено огромное количество элементов требующих скругления углов. Иногда JavaScript библиотеки не правильно отрисовывают элементы или вовсе не отрисовывают.
Способ с картинками снижает нагрузку на браузер и страница как правило работает шустро, но этот способ не может претендовать в случаях когда огромная часть сайта содержит разнородные виды рамок и т.д. Этот способ просто автоматически отбрасывается в сторону и ищется альтернативный вариант, например оставить рамки с прямыми углами или вовсе отказаться от части элементов которые не портят вид сайта.
Способ с использованием VML на мой взгляд самый оптимальный. VML довольно старый и многими малоизученный язык для рисования векторной графики, он является предком развивающего SVG. По интернету разбросана информация по нему обрывками. Спецификация оставляет желать лучшего. А в будущем и вовсе про него забудут. Но на данный момент именно с помощью него мы и будет реализовывать наши круглые уголки.
Подготовка
Для отображения VML в Internet Explorer нужно проделать некоторые действия, в первую очередь добавить специальный тег на страницу в секцию HEAD
<!--[if VML&(lt IE 9)]>
<?import namespace="v" urn="urn:schemas-microsoft-com:vml" implementation="#default#VML" declareNamespace ?>
<![endif]-->
Данный тег говорит браузеру о том какой тип элементов нужно отдавать движку VML, в нашем случае элементы имеющие префикс (пространство имен) `v`
будут обрабатываться движком VML, так же тег внесен в условные комментарии, что бы другие браузеры не обрабатывали этот не нужный для них тег.
Далее надо указать, чтобы элементы вели себя как VML. Это делает CSS-свойство behavior:url(#default#VML)
. Для этого мы с вами добавим стиль так же в условных коментариях:
<!--[if VML&(lt IE 9)]>
<style type="text/css">
v:shape, v:shapetype, v:formulas, v:group, v:f, v:path, v:fill {
behavior:url(#default#VML);
}
v:group, v:shape {
display:inline-block;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
</style>
<![endif]-->
Стили вы можете вынести в отдельный файл стилей, дабы не засорять разметку. Собственно на этом подготовка заканчивается, тут мы с вами дали понять какие теги браузер должен отправлять движку VML.
Описываем формулы
Вот тут мы с вами и начнем реализовывать многофункциональный border-radius
на VML с использованием формул. Формулы это некие правила указывающие движку VML то, как нужно вести себя в тех или иных условиях. Именно они нам и дают возможность отказаться от JavaScript, дабы увеличить скорость прорисовки векторной графики. Я не буду описывать функционал формул, то как с ними работать, как использовать, так как мы с вами не изучаем VML а делаем круглые уголки. VML-формулы это довольно мощный способ реализовать то что многие реализуют на JavaScript не догадываясь о том что это можно сделать и без него.
Итак, я реализовал некий шаблон реализации скругленных рамок с помощью формул. Этот шаблон мы с вами будем использовать для прорисовки наших нужных рамочек:
<v:shapetype id="vml-radius" adj="0,0,0,0" coordorigin="-1 -1" coordsize="1000000 1000000"
path="m,@9qy@8,l@15,qx@0,@16l@0,@24qy@22,@1l@29,@1qx,@31l,@9xm@44,@54qy@49,@36
l@60,@36qx@39,@65l@39,@77qy@71,@42l@82,@42qx@44,@88l@44,@54xe" stroked="false">
<v:formulas>
<v:f eqn="val width"/><v:f eqn="val height"/><v:f eqn="prod @0 1 pixelwidth"/>
<v:f eqn="prod @1 1 pixelheight"/><v:f eqn="prod #0 1 10000"/><v:f eqn="prod @4 10000 1"/>
<v:f eqn="sum #0 0 @5"/><v:f eqn="if @4 @4 @6"/><v:f eqn="prod @7 @2 1"/>
<v:f eqn="prod @6 @3 1"/><v:f eqn="prod #1 1 10000"/><v:f eqn="prod @10 10000 1"/>
<v:f eqn="sum #1 0 @11"/><v:f eqn="if @10 @10 @12"/><v:f eqn="sum pixelwidth 0 @13"/>
<v:f eqn="prod @14 @2 1"/><v:f eqn="prod @12 @3 1"/><v:f eqn="prod #2 1 10000"/>
<v:f eqn="prod @17 10000 1"/><v:f eqn="sum #2 0 @18"/><v:f eqn="if @17 @17 @19"/>
<v:f eqn="sum pixelwidth 0 @20"/><v:f eqn="prod @21 @2 1"/><v:f eqn="sum pixelheight 0 @19"/>
<v:f eqn="prod @23 @3 1"/><v:f eqn="prod #3 1 10000"/><v:f eqn="prod @25 10000 1"/>
<v:f eqn="sum #3 0 @26"/><v:f eqn="if @25 @25 @27"/><v:f eqn="prod @28 @2 1"/>
<v:f eqn="sum pixelheight 0 @27"/><v:f eqn="prod @30 @3 1"/><v:f eqn="sum #4 #6 0"/>
<v:f eqn="sum #5 #7 0"/><v:f eqn="sum @32 @33 0"/><v:f eqn="prod #4 @3 1"/>
<v:f eqn="if @34 @35 @9"/><v:f eqn="prod #5 @2 1"/><v:f eqn="sum @0 0 @37"/>
<v:f eqn="if @34 @38 0"/><v:f eqn="prod #6 @3 1"/><v:f eqn="sum @1 0 @40"/>
<v:f eqn="if @34 @41 @9"/><v:f eqn="prod #7 @2 1"/><v:f eqn="if @34 @43 0"/>
<v:f eqn="sum @7 0 #7"/><v:f eqn="if @45 @45 0"/><v:f eqn="prod @46 @2 1"/>
<v:f eqn="sum @47 @43 0"/><v:f eqn="if @34 @48 0"/><v:f eqn="sum @6 0 #4"/>
<v:f eqn="if @50 @50 0"/><v:f eqn="prod @51 @3 1"/><v:f eqn="sum @52 @35 0"/>
<v:f eqn="if @34 @53 @9"/><v:f eqn="sum @13 0 #5"/><v:f eqn="if @55 @55 0"/>
<v:f eqn="sum pixelwidth 0 @56"/><v:f eqn="prod @57 @2 1"/><v:f eqn="sum @58 0 @37"/>
<v:f eqn="if @34 @59 0"/><v:f eqn="sum @12 0 #4"/><v:f eqn="if @61 @61 0"/>
<v:f eqn="prod @62 @3 1"/><v:f eqn="sum @63 @35 0"/><v:f eqn="if @34 @64 @9"/>
<v:f eqn="sum @20 0 #5"/><v:f eqn="if @66 @66 0"/><v:f eqn="sum pixelwidth 0 @67"/>
<v:f eqn="prod @68 @2 1"/><v:f eqn="sum @69 0 @37"/><v:f eqn="if @34 @70 0"/>
<v:f eqn="sum @19 0 #6"/><v:f eqn="if @72 @72 0"/><v:f eqn="sum pixelheight 0 @73"/>
<v:f eqn="prod @74 @3 1"/><v:f eqn="sum @75 0 @40"/><v:f eqn="if @34 @76 @9"/>
<v:f eqn="sum @28 0 #7"/><v:f eqn="if @78 @78 0"/><v:f eqn="prod @79 @2 1"/>
<v:f eqn="sum @80 @43 0"/><v:f eqn="if @34 @81 0"/><v:f eqn="sum @27 0 #6"/>
<v:f eqn="if @83 @83 0"/><v:f eqn="sum pixelheight 0 @84"/><v:f eqn="prod @85 @3 1"/>
<v:f eqn="sum @86 0 @40"/><v:f eqn="if @34 @87 @9"/>
</v:formulas>
</v:shapetype>
Огромный шаблон не правда ли? Но это не страшно, ведь JavaScript как правило занимает куда больше места, шаблон размещается лишь один раз на странице и используется по ключу vml-radius
указанный в атрибуте ID
в теге v:shapetype
. Этот шаблон мы также можем разместить в условных комментариях <!--[if VML&(lt IE 9)]>...<![endif]-->
где то в теге HEAD.
Рисуем круглые углы
Теперь мы с вами можем легко рисовать рамочки с круглыми углами:
<v:shape adj="16,16,16,16" fillcolor="#00ff00" style="width: 100px; height: 100px;" type="#vml-radius" />
тут мы с вами нарисовали простой зеленый квадрат с круглыми углами, радиус которых составляет 16 пикселей. Как вы заметили радиус углов мы указываем в атрибуте adj="16,16,16,16"
. А теперь давайте добавим бордюры для этого квадрата, например красного цвета:
<v:group style="width: 100px; height: 100px;">
<v:shape adj="16,16,16,16" fillcolor="#00ff00" type="#vml-radius" />
<v:shape adj="16,16,16,16,10,10,10,10" fillcolor="#ff0000" type="#vml-radius" />
</v:group>
Тут мы с вами создали группу слоев, то есть на зеленый квадрат наложили красные бордюры и как вы заметили у атрибута adj
появились дополнительные параметры указывающие толщину бордюра.
Иногда бывает нужно сделать углы не равномерные, а овальные. То есть размер угла по вертикали один, а по горизонтали другой, например такой квадрат:
для этого мы немного подкорректируем параметр adj:
<v:group style="width: 100px; height: 100px;">
<v:shape adj="00320064,16,00320064,16" fillcolor="#00ff00" type="#vml-radius" />
<v:shape adj="00320064,16,00320064,16,10,10,10,10" fillcolor="#ff0000" type="#vml-radius" />
</v:group>
Добавим задний фон:
<v:group style="width: 100px; height: 100px;">
<v:shape adj="00320064,16,00320064,16" type="#vml-radius">
<v:fill type="tile" src="http://img-fotki.yandex.ru/get/4424/104967700.7f/0_7a378_1c3ad751_S.jpg" />
</v:shape>
<v:shape adj="00320064,16,00320064,16,10,0,10,0" fillcolor="#ff0000" type="#vml-radius" />
</v:group>
или зальем градиентом:
<v:group style="width: 100px; height: 100px;">
<v:shape adj="00320064,16,00320064,16" type="#vml-radius">
<v:fill type="gradient" angle="0" color="#D0E805" color2="#E88A05" />
</v:shape>
<v:shape adj="00320064,16,00320064,16,10,0,10,0" fillcolor="#ff0000" type="#vml-radius" />
</v:group>
Рисуем пейзаж с помощью градиента:
<v:group style="width: 100px; height: 100px;">
<v:shape adj="00320064,16,00320064,16" type="#vml-radius">
<v:fill type="gradient" method="sigma" angle="0" color="#ff0000" colors="40% #0fff00,60% #00fff0" color2="#0000ff" />
</v:shape>
<v:shape adj="00320064,16,00320064,16,10,0,10,0" fillcolor="#ff0000" type="#vml-radius" />
</v:group>
Параметры
Описание стандартных для элементов параметров вы можете прочесть документацию например у Microsoft, я лишь опишу параметр adj
, параметр adj
имеет всего восемь значений разделенных запятыми, первые четыре из них указывают на радиус углов, вторая четверка указывает на толщину бордюра.
Первые четыре параметра, имеют хитрую специфику. Если число указано менее 10000
то углы будут одинаковыми как по горизонтали, так и по вертикали. Например запись 00320064
говорит о том, что нужно отрисовать угол в 32 пикселя по горизонтали и 64 пикселя по вертикали. Грубо говоря число делится на две части: 0032/0064
. Такой специфический способ реализации пришлось сделать по причине того что параметр adj
может максимум иметь 8-м параметров, остальные просто игнорируются. Об этом вы можете почитать документацию в официальных источниках.
первые четыре параметра при значениях ниже 10000 указывают на:
LeftTopXY, RightTopXY, RightBottomXY, LeftBottomXY
при значении 10000 и выше указывают на:
LeftTopX/LeftTopY, RightTopX/RightTopY, RightBottomX/RightBottomY, LeftBottomX/LeftBottomY
Вторая четверка указывает на толщину бордюров:
Top, Right, Bottom, Left
Как видите ничего тут сложного нет, единственное если не указать вторую четверку параметров, то заливка цветом будет полностью заливать всю рамочку, если все же указан хоть один параметр, то заливка будет действовать на бордюры, а задний фон будет прозрачным.
Проблемные части
Проблемной частью в данном решении является резиновые блоки, суть проблемы в том что Microsoft выпустил Internet Explorer 8 вырезав в нем возможность подгонки VML элементов под блоки HTML, в более ранних версиях браузера работает данный метод нормально:
<span style="position: relative; display: inline-block; padding: 20px;">
<v:shape adj="16,16,16,16" fillcolor="#00ff00" style="z-index: -1; position: absolute;" type="#vml-radius" />
test text
</span>
То есть к примеру IE7 вполне нормально подгонит наш квадратик по родительский блок. А вот в IE8 такой подход уже не прокатит.
Что же делать спросите вы!? Конечно есть способ сделать это на JavaScript, повесить на родительский блок событие onresize
и менять размеры нашего квадратика при изменении этого блока. Но это приведет к тому что при первой загрузке страницы нам нужно будет пробежаться по всем элементам, установить начальные размеры, повесить событие. Это все муторно и это JavaScript, а ведь мы с вами делаем все что бы не использовать для этих целей JavaScript.
К нашей радости Microsoft не полностью нас порезал в данной возможности и оставил один VML элемент позиционирующий от родительского блока HTML. Хотя этот элемент не совсем уж удобный, но дает возможность выйти из проблемной ситуации. Этот элемент называется V:VMLFRAME
. Это что-то подобие IFRAME но для вектора.
VMLFRAME или очередной костыль
Что бы использовать данный тег, нужно вынести нашу формулу в отдельный файл, создать там наш квадратик. То есть правило для него, а потом использовать его. И так приступим, создадим файл с названием допустим internalvml.vml
с содержимым:
<xml xmlns:v="urn:schemas-microsoft-com:vml">
<v:shapetype id="vml-radius" adj="0,0,0,0" coordorigin="-1 -1" coordsize="1000000 1000000"
path="m,@9qy@8,l@15,qx@0,@16l@0,@24qy@22,@1l@29,@1qx,@31l,@9xm@44,@54qy@49,@36
l@60,@36qx@39,@65l@39,@77qy@71,@42l@82,@42qx@44,@88l@44,@54xe" stroked="false">
<v:formulas>
<v:f eqn="val width"/><v:f eqn="val height"/><v:f eqn="prod @0 1 pixelwidth"/>
<v:f eqn="prod @1 1 pixelheight"/><v:f eqn="prod #0 1 10000"/><v:f eqn="prod @4 10000 1"/>
<v:f eqn="sum #0 0 @5"/><v:f eqn="if @4 @4 @6"/><v:f eqn="prod @7 @2 1"/>
<v:f eqn="prod @6 @3 1"/><v:f eqn="prod #1 1 10000"/><v:f eqn="prod @10 10000 1"/>
<v:f eqn="sum #1 0 @11"/><v:f eqn="if @10 @10 @12"/><v:f eqn="sum pixelwidth 0 @13"/>
<v:f eqn="prod @14 @2 1"/><v:f eqn="prod @12 @3 1"/><v:f eqn="prod #2 1 10000"/>
<v:f eqn="prod @17 10000 1"/><v:f eqn="sum #2 0 @18"/><v:f eqn="if @17 @17 @19"/>
<v:f eqn="sum pixelwidth 0 @20"/><v:f eqn="prod @21 @2 1"/><v:f eqn="sum pixelheight 0 @19"/>
<v:f eqn="prod @23 @3 1"/><v:f eqn="prod #3 1 10000"/><v:f eqn="prod @25 10000 1"/>
<v:f eqn="sum #3 0 @26"/><v:f eqn="if @25 @25 @27"/><v:f eqn="prod @28 @2 1"/>
<v:f eqn="sum pixelheight 0 @27"/><v:f eqn="prod @30 @3 1"/><v:f eqn="sum #4 #6 0"/>
<v:f eqn="sum #5 #7 0"/><v:f eqn="sum @32 @33 0"/><v:f eqn="prod #4 @3 1"/>
<v:f eqn="if @34 @35 @9"/><v:f eqn="prod #5 @2 1"/><v:f eqn="sum @0 0 @37"/>
<v:f eqn="if @34 @38 0"/><v:f eqn="prod #6 @3 1"/><v:f eqn="sum @1 0 @40"/>
<v:f eqn="if @34 @41 @9"/><v:f eqn="prod #7 @2 1"/><v:f eqn="if @34 @43 0"/>
<v:f eqn="sum @7 0 #7"/><v:f eqn="if @45 @45 0"/><v:f eqn="prod @46 @2 1"/>
<v:f eqn="sum @47 @43 0"/><v:f eqn="if @34 @48 0"/><v:f eqn="sum @6 0 #4"/>
<v:f eqn="if @50 @50 0"/><v:f eqn="prod @51 @3 1"/><v:f eqn="sum @52 @35 0"/>
<v:f eqn="if @34 @53 @9"/><v:f eqn="sum @13 0 #5"/><v:f eqn="if @55 @55 0"/>
<v:f eqn="sum pixelwidth 0 @56"/><v:f eqn="prod @57 @2 1"/><v:f eqn="sum @58 0 @37"/>
<v:f eqn="if @34 @59 0"/><v:f eqn="sum @12 0 #4"/><v:f eqn="if @61 @61 0"/>
<v:f eqn="prod @62 @3 1"/><v:f eqn="sum @63 @35 0"/><v:f eqn="if @34 @64 @9"/>
<v:f eqn="sum @20 0 #5"/><v:f eqn="if @66 @66 0"/><v:f eqn="sum pixelwidth 0 @67"/>
<v:f eqn="prod @68 @2 1"/><v:f eqn="sum @69 0 @37"/><v:f eqn="if @34 @70 0"/>
<v:f eqn="sum @19 0 #6"/><v:f eqn="if @72 @72 0"/><v:f eqn="sum pixelheight 0 @73"/>
<v:f eqn="prod @74 @3 1"/><v:f eqn="sum @75 0 @40"/><v:f eqn="if @34 @76 @9"/>
<v:f eqn="sum @28 0 #7"/><v:f eqn="if @78 @78 0"/><v:f eqn="prod @79 @2 1"/>
<v:f eqn="sum @80 @43 0"/><v:f eqn="if @34 @81 0"/><v:f eqn="sum @27 0 #6"/>
<v:f eqn="if @83 @83 0"/><v:f eqn="sum pixelheight 0 @84"/><v:f eqn="prod @85 @3 1"/>
<v:f eqn="sum @86 0 @40"/><v:f eqn="if @34 @87 @9"/>
</v:formulas>
</v:shapetype>
<v:shape id="my-round" adj="16,16,16,16" fillcolor="#00ff00" style="width: 100%; height: 100%;" type="#vml-radius" />
</xml>
В файл мы поместили наш квадратик:
<v:shape id="my-round" adj="16,16,16,16" fillcolor="#00ff00" style="width: 100%; height: 100%;" type="#vml-radius" />
Добавив ему ID
с именем my-round
, что бы потом по этому имени IE определил какой квадратик мы хотим использовать.
А теперь можно смело делать наш блок с нашим квадратиком через элемент v:vmlframe
:
<span style="position: relative; display: inline-block; padding: 20px;">
<v:vmlframe style="z-index: -1; position: absolute;" src="internalvml.vml#my-round" />
test text
</span>
Теперь Internet Explorer 8 нормально будет подгонять наш квадратик к размеру родительского блока.
Заключение
В заключении хочу сказать, что если вам что-то не ясно или просто хотите что-то добавить, пишите в комментариях и я дополню/исправлю статью. Писать статьи я не мастак, кодить это мое а вот писать статьи у меня не совсем хорошо выходит. Поэтому уж не обессудьте, я старался)))
Всем удачи и приятно скругления!
Автор: devote