Реализация стилей подчеркивания в LESS через генерацию png в data-URI

Решил я однажды реализовать гибкий способ стилизации подчеркивания ссылок — чтобы просто делать полупрозрачные подчеркивания, регулировать паттерн в dashed/dotted-border, делать волнистые подчеркивания и вообще иметь настройки CSS3 text-decoration, которые еще ни один браузер не умеет.

cdd5f788419966496948cce1d6ab6093

В результате получился генератор PNG в data-URI на LESS.

Демо.

Варианты реализации

fbf8cde850a38a2bdedcb79f3e79ef70Полупрозрачное, пунктирное и точечное подчеркивания весьма просто делаются черезborder-bottom ☞.

Интересное начинается, когда хочется сместить линию ближе к тексту.
Можно соорудить конструкцию вида

<a class="link"><span>Some link text here</span></a>

и регулировать line-height элемента span(или a), задав ему display:inline-block, но тогда возникает проблема на многострочном тексте: inline-block становится настоящим block’ом в плане отображения бордера (иллюстрация справа).

После размышлений и экспериментов, я пришел к выводу, что самым «чистым» и удобным решением было-бы класть паттерн подчеркивания в background с высотой, равной line-height. Осталось только понять, откуда брать этот паттерн.

  • Генерировать картинку где-то на стороне и подключать её как файл — негибко и неудобно для разработки, каждое изменение будет убивать нервы.
  • Использовать генератор PNG через canvas (такой, к примеру), но это также неудобно в разработке: каждый раз генерировать data-URI на стороне.
  • Генерировать Repeating-gradient, но это весьма ненадежный способ, так как есть риск не попасть точно в пиксель линии подчеркивания, да и пунктирные подчеркивания не реализовать.

Самым логичным оставалось генерировать PNG динамически и вставлять в data-URI. Извопроса на stackoverflow выяснилось, что один человек уже сумел генерировать GIF-картинку в один пиксель (тут), но, надо сказать, весьма прямолинейно и негибко: изменение размеров этой картинки было-бы задачей, равносильной переписыванию всего кода.

Гряли выходные, и я решил наконец перестать фрустрироваться грязной реализацией подчеркивания ссылок и разобраться с генерацией PNG.

PNG.js

После нескольких часов изучения спецификаций PNGZLIB Data Format и DEFLATE Data Format, а также примера сериреализации png и небольшого реверс-инжиниринга (тут пример генерации сырого png), был создан js-класс для работы с PNG, пригодный для распила на куски в LESS.

Класс PNG умеет генерировать несжатый PNG с индексированным цветом (indexed-color) или битмапа (truecolor with alpha). Используется следующим образом:

PNG.js usecase

Запуск JS в LESS

Как оказалось, LESS весьма гибок для запуска JS. К примеру, функции можно запускать следующим обазом:

@test: `function(a){
        return a
    }`;
    test: `(@{test})(3)`; //test: 3

 

Переместив png.js в примесь и написав интерфейс к нему, в итоге получился следующий код:

painter.less

Как использовать?

 

1. Подключить painter.less и less.js, как в демо

 

<link rel="stylesheet/less" type="text/css" href="painter.less" />
<script src="less.js" type="text/javascript"></script>

 

2. Использовать классы для span-элементов:

 

<span class="underline">Простое подчеркивание</span>
<span class="underline thick">Толcтое подчеркивание</span>
<span class="underline offset">Смещенное подчеркивание</span>
<span class="underline transparent">Полупрозрачное подчеркивание</span>
<span class="waved">Волнистое подчеркивание</span>
<span class="waved alt">Волнистое подчеркивание 2</span>
<span class="dotted">Точечное частое подчеркивание</span>
<span class="dotted rare">Точечное редкое подчеркивание</span>
<span class="dotted thick">Точечное толстое подчеркивание</span>
<span class="dashed">Пунктирное подчеркивание</span>
<span class="dashed thick">Пунктирное толстое подчеркивание</span>
<span class="dot-dashed">Штрих-пунктирное подчеркивание</span>

 

И отрегулировать позицию background:
span { background-position: 0 -5px; }

3. Доступные миксины:

 

  • .underline(@height: 20, @color: @text, @thickness: 1)
  • .waved(@height: 20, @color: @red, @thickness: 2, @width: 4)
  • .dotted(@height: 20, @color: @text, @width: 3, @thickness: 1)
  • .dashed(@height: 20, @color: @text, @width: 8, @thickness: 1, @length: 4)
  • .dot-dashed(@height: 20, @color: @text, @width: 10, @thickness: 1)

Можно также использовать миксин .png(@stream: "0001", @w: 2, @h: 2, @color: black), отправляя напрямую поток битов индексированных цветов.

[emc2alert type=»info» style=»normal» position=»top» visible=»visible» closebtn=»0″ ]Итог: демо, репозиторий на github.[/emc2alert]