css

«Магический экран» на CSS

У всех в детстве была игрушка «магический экран»? Такая прямоугольная штуковина, обычно из красного пластика с двумя крутилками, которые надо было поворачивать, чтобы что-нибудь нарисовать?

А все знают, что браузеры на основе «вебкита» («Хром», «Сафари» и им подобные) некорректно отрабатывают некоторые комбинации CSS3-селекторов? Они «забывают» их перерисовывать, если меняется состояние, зависящее от псевдо-селекторов «:hover», «:target» и другим, которые активно взаимодействуют с внешним окружением.

Меня раздрадает этот баг, ему много лет, его не правят и из-за него невозможно сделать некоторые интересные вещи так, чтобы они работали в «вебките» тоже.

Магический экран на CSS (33.62КиБ)

Вчера ночью я про него думал и меня осенило каким образом баг можно использовать, чтобы создать кое-что интересное. Ведь чем этот баг характеризуется? «Залипанием» состояния. Отлично, это можно использовать!

На основе этого бага я наколдовал «магический экран» без использования Джаваскрипта (не забудьте, работает в «Сафари» или «Хроме»). Если вести справа налево кружки́ рисуются, если слева направо — стираются. «Крутилки» только для декора, они не работают.

Шаманство и контакт с космосом происходит вот в этом участке CSS:

.outer>i:hover+i+i {
        width: 10px;
        height: 10px;
        background: gray;
        border-radius: 50%;
        z-index: 2;

        margin: -2px 0 0 -2px;
    }

Поле для рисования состоит из кучи элементов размером 3×3, когда я наезжаю курсором на один из них, срабатывает «:hover» и меняет стиль кружка́, который находится сильно после того, на который я наехал курсором. Это нужно, чтобы «вебкит» не увидел момента, когда курсор уходит с элемента. «Вебкит» хоть и неправильно работает с селекторами, тем не менее содержит кучу «хаков», чтобы в распространённых случаях всё выглядело как надо.

Таким образом, браузер не получает события, что курсор с элемента ушёл и оставляет стиль как есть — я увеличиваю размер элемента до 10×10 и меняю цвет на серый, на экране за курсором остаётся шлейф из таких элементов справа от курсора. Если же двигаться слева направо, наезжая на кружки, то браузер видит нужные события и очищает стиль, кружки пропадают.

В самых свежих «Сафари» 5.1 и «Хроме» 24 мой «магический экран» работает.

9 комментариев

Отгадка: CSS не видно, картинка спрятана

«Уровень один» вчерашней головоломки прошли многие, этот ларчик открывается просто — есть RFC 5988, где описан способ подключать некоторые ресурсы через HTTP-заголовок. Этот RFC поддерживают «Опера» и «Файерфокс». На скриншоте, в средстве для разработчиков «Оперы» заголовок хорошо видно.

Где спряталось содержимое (20.32КиБ)

Дальше дело техники, пользуясь тем, что BODY в ДОМе есть всегда, вне зависимости от того указан ли он реально в документе, я накладываю на него стиль, выставляю размеры, обвожу бордюром и аутлайном (из-за чего получается такая хитрая рамка, я её вчера придумал) и ставлю картинку на задний фон.

Теперь главное. А картинка-то откуда взялась?

Есть такой стааааренький формат графики XMB, тестовый формат и по виду явно происходит от заголовочных файлов Си. Про него и не помнит уже никто, важно же в нём то, что когда-то его поддерживали все браузеры. Я даже когда-то библиотеку Image_XBM писал для работы с ним из ПХП.

Вот начало одного из файлов, содержащих картинку в формате XBM:

#define _width 300
#define _height 225
static char _bits[] = {
0xbd, 0xf7, 0xde, 0x7b, 0xef, 0xbd, 0xf7, 0xde, 0x7b, 0xef, 0xbd, 0xf7, 0xde, 0x7b, 0xef, 0xbd, 0xf7, 0xde, 0x7b, 0xef, 0xbd, 0xf7, 0xde, 0x7b, 0xef,
0xbd, 0xf7, 0xde, 0x7b, 0xef, 0xbd, 0xf7, 0xde, 0x7b, 0xef, 0xbd, 0xf7, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00…
}

Он тут, кстати, попроще, чем обычно используется, я выкинул название картинки, которое идёт перед подчёркиваниями. Браузеры, видимо, проще относятся к этому формату, работает и так.

У формата тяжёлая судьба, сначала его похоронил «Эксплорер», убрав в версии 6.0SP1 его поддержку из-за ошибки в кодеке, потом «Файрфокс» выбросил его за ненабодностью, а в «Хроме» его не было с рождения. Остался он только в «Сафари» и в «Опере».

Мне давно хотелось стереть с него пыль и куда-нибудь его пристроить повеселее, вот я и решил вчера к ночи попробовать вставить его в CSS. Удалось сделать это быстро, почти без труда, но стало интересно — сколько можно выкинуть из формата, чтобы браузер его по-прежнему понимал.

Вот часть CSS из вчерашнего примера:

  background: 20px 20px url(bolk.css#0x5F0xBF0xFF0xDC0x530xF60x870…) no-repeat;
  width: 80px; /*_width 80px*/
  height: 80px; /*_height 80px*/
  border: 7px dashed #259;
  outline: #259 dashed 6px;
  margin: 10px; padding: 3px;
}

Читатели правильно догадались, что после хеша в свойстве «background» записана картинка, но как она оттуда попадает в HTML не догадался никто.

В гонке выкидывания лишнего из картинки в формате XBM победила «Опера», ей нужно было только оставить первую строку «#define», которую она использует в качестве «магического» значения для определения формата, где-то раскидать «_width» и «_height» (я их засунул в коментарии CSS) и оставить строку шестнадцатеричных чисел, причём, как оказалось, «Опере» и запятые не нужны!

Итак, откуда взялась картинка?

«Опера» смотрит в свойство «background» и видит там урл с картинкой, часть с хешем просто отбрасывается, в данном случае она не используется и браузер грузит картинку с адреса «bolk.css». «Опера» пытается определить в каком формате пришла картинка, тут нам помогает «магическая» строка «#define», раз она есть, стало быть это XBM.

Дальше браузер ищет в полученном файле «_width» и «_height» с цифрами и находит, считая цифры размерами картинки, после чего ищет первое вхождение «0x…» и двигается по ним, пока не находит их все, это и есть тело картинки, которую надо отобразить.

Оставить комментарий

Головоломка для верстальщика: CSS не видно, картинка спрятана

Хотите голову поломать? Вот вам тест на знание всякого странного, откройте следующий урл в «Опере»: /files/opera-mystery/ (именно в «Опере») и попробуйте догадаться как это сделано.

Ответ завтра. Кажется, во всём интернете так ещё никто не делал.

Загадка (25.18КиБ)

Выше снимок того что вы должны увидеть на экране, я испытывал под «Оперой» последней версии (12.12) для «Мака», не факт, что под другими ОС всё так же будет.

Вот что видно в консоли, если попытаться скачать этот файл:

bolk@Bolk ~$ wget -O- --user-agent="Opera/9.80 (Macintosh; Intel Mac OS X 10.8.2) Presto/2.12.388 Version/12.12" /files/opera-mystery/
--2013-01-22 00:24:09--  /files/opera-mystery/
Resolving bolknote.ru... 91.230.61.15
Connecting to bolknote.ru|91.230.61.15|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 0 [text/html]
Saving to: ‘STDOUT’

    [ <=>                                                                                                                                                   ] 0           --.-K/s   in 0s      

2013-01-22 00:24:09 (0.00 B/s) - written to stdout [0/0]

Когда догадаетесь где тело страницы, переходите на уровень два — догадайтесь откуда берётся картинка.

18 комментариев

Генерируемый задний фон

Я как-то пропустил знаменитую (оказывается) программу для генерации фона, похожего на лабиринт из двух слешей. Несложная программа на Бейсике:

10 PRINT CHR$(205.5+RND(1));: GOTO 10

Выводит вот такой фон (скриншот с «Командора 64», поэтому цвета такие):

Фон из слешей (27.66КиБ)

Для тех, кто не понимает этот архаичный язык, перепишу на Пайтоне:

import random; while 1: print random.choice(u'╱╲'),

и ПХП:

for(;;) flush(print '/\\'[mt_rand(0, 1)+.5]);

На «Хабре» статья пробегала, где автор пытался сделать то же при помощи CSS, не справился и сделал генерацию на серверной стороне — на PHP. Он высказывал мысль использовать для этого JS, но то ли не смог, то ли отбросил её по каким-то иным причинам.

В общем, решил я попробовать сделать это на стороне клиента, на «канвасе», заодно посмотреть конструкции, которые позволяют ставить «канвас» в качестве задника, давно руки чесались.

Сначала результат (в левом верхнем углу фрагмент нормального размера):

Фон в виде лабиринта (31.46КиБ)

Теперь посмотрим код. Я убрал всё лишнее, подробности можно посмотреть в разделе «Храню»:

!function(c) {
    var ctx = document.getCSSCanvasContext ? 
        document.getCSSCanvasContext("2d", "maze", c.width, c.height):
        c.getContext("2d");

    ctx.font      = "10px monospace";
    ctx.fillStyle = '#36f';
    ctx.fillRect(0, 0, c.width, c.height);

    ctx.fillStyle = '#068';

    for (var y = 7, ly = c.height; y < ly; y += 8) {
        for (var x = -1, lx = c.width; x < lx; x+= 4) {
            ctx.fillText("/\\".charAt(Math.random() + .5), x, y);
        }
    }

    if (!document.mozSetImageElement && !document.getCSSCanvasContext) {
        document.getElementsByTagName('body')[0].style.backgroundImage='url(' + c.toDataURL("image/png") + ')';
    }

}(document.getElementById('maze'));

Внутрь функции у меня передаётся объект «канваса» (размером 99×96, подогнан под шрифт).

В первой строке проверяется наличие метода «getCSSCanvasContext», он работает только в браузерах, основанных на Вебките (Хром, Сафари) и позволяет напрямую использовать «канвас» для задника, нужно только в CSS указать идентификатор:

body { background: -webkit-canvas(maze); }

Если этот метод недоступен, то я рисую в скрытый тег «канваса», который есть у меня на странице. Вывожу те же самые символы, которые использовались в Бейсике.

Связь «канваса» и задника для браузеров на «Геко» (ФайрФокса и менее известных) в Джаваскрипте не видна, она полностью на стороне CSS:

body { background: -moz-element(#maze); }

Эти браузеры позволяют указать в качестве задника что угодно — хоть IFRAME, был бы у него идентификатор. Соответственно, «maze» — идентификатор моего «канваса». Делать больше ничего не нужно, любой элемент с этим «айди» размножится в качестве фона.

В последних строках я проверяют доступен ли метод для указания «-moz-element», нет ли метода для рисования «канвасом» прямо в задник, если нет, то перевожу картинку из «канваса» в dataURI и добавляю её в таком виде на фон тега BODY. Таким образом, этот код работает и для «Оперы» с «Эксплорером» (начиная с девятого).

7 комментариев

Масштабирование тега CANVAS

Думаю, многие знают, что у тега CANVAS можно задать размер, а потом смасштабировать его до нужных размеров при помощи CSS.

<canvas width="10" height="10" style="width:100px; height:100px"></canvas>

В этом случае, он не меняет свой размер, доступный из кода, но на экране растягивается или сжимается до указанных величин. Иногда это бывает полезно, но есть недостаток — все браузеры, в которых я это пробовал, сглаживают («размывают») содержимое этого тега, что иногда нежелательно.

У меня нет возможности посмотреть под всеми распространёнными версиями браузеров, но, как выяснилось в эксперименте, с современными работает такой трюк, можно указать алгоритм масштабирования для изображений, который для «канваса» тоже работает:

canvas {
    image-rendering: optimizeSpeed; /* old WebKit, Safari, FireFox 3.6+ */
    image-rendering: -webkit-optimize-contrast; /* newer WebKit (CSS3 proposal) */
    image-rendering: -o-crisp-edges; /* Opera 11.6+ (CSS4 prefixed) */
    image-rendering: -moz-crisp-edges; /* FF 6+ (CSS4 prefixed) */
    image-rendering: crisp-edges; /* CSS4 */
}
Сравнение пиксельной и сглаженной диагональной линии: слева — ступенчатый край, справа — размытый, демонстрирующий эффект антиалиасинга.

Чтобы была понятно о чём вообще речь — слева на скриншоте версия без сглаживания (с применёнными стилями), справа — со сглаживанием, это я прочертил одну линию:

with (canvas.getContext('2d')) {
        moveTo(0, 0);
        lineTo(10, 10);
        stroke();
    }

Я снабдил код комментариями, чтобы было видно где это работает. К сожалению, в Эксплорере ничего не получится, когда-то у него было свойство -ms-interpolation-mode, но с девятой версии (где и появился «канвас») оно уже не работает, придётся решать проблему через JS.

Добавлено позднее: похоже, у Хрома тоже проблемы. На разных ОС может работать или нет.

Оставить комментарий

Теннис на CSS-анимациях

CSS-теннис (26.61КиБ)

Я тут решил прикинуть для себя недостатки CSS-анимации, поэтому написал «теннис» на HTML и CSS. В кавычках, потому что играть в него невозможно: можно лишь управлять при помощи мышки «ракеткой», а шарик в этот момент будет летать из угла в угол, ни на что не реагируя.

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

Префиксы для всех браузеров в коде ставить не стал, оставил только для «вебкита» (будет работать в «Сафари», «Хроме» и «Яндекс.Браузере»), всё равно все браузеры собираются начать их поддерживать, так зачем же стараться?

Так вот, анимации в ЦСС — интересная штука, позволяющая простыми средствами плавно менять свойства объектов на странице, мне понравилось. Но у неё есть недостаток, на мой взгляд — анимируется ровно один элемент и у неё нет состояний.

Т. е. я не могу посмотреть в каком состоянии сейчас находится анимированный элемент, чтобы привязать к этому событию что-то ещё. Не знаю почему это не добавили в синтаксис, на мой взгляд, это совершенно не противоречит декларативности языка ЦСС.

По этой причине я не смог написать полноценный теннис — у меня просто нет средств, чтобы связать между собой положение мяча и ракетки.

Из других найденных недостатков — далеко не все свойства нормально анимируются, например, с фоновыми картинками какая-то беда, но я это списываю на сырость технологии.

Кстати, гениальные ребята, которые для Интернет Эксплорера когда-то придумали большую часть веб-технологий, которые мы под другими именами только недавно получили в виде стандартов W3C, первыми задумались и об анимации в вебе без ДжаваСкрипта. Сначала это был примитивный тег MARQUEE, появившийся аж во второй версии браузера, позже — технология HTML+TIME, позволявшая создавать очень сложную анимацию при помощи специальной разметки, это было позднее — в 5.5, но это было 12 лет назад!

2 комментария

Firefox 16 CSS hack

.selector, _:-moz-meter-optimum {
    background: red;
}

ЦСС-хак для Файрфокса 16, который выйдет сегодня вечером. Вместо «.selector» надо поставить нужный вам селектор.

2 комментария

Интерактив и CSS

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

Например, если анимировать градус поворота, то получится забавный червяк, закреплённый с одного конца. Легко можно сделать, чтобы он ползал и извивался буквой «S», если бы целую кучу префиксов не приходилось писать.

Червяк (8.38КиБ)

Но самое потрясающее — возможность добавлять интерактив, жаль только возможности браузеров в этом пока очень ограничены. Попробуйте поводить курсором мыши по червяку и закон его движения изменится, причём совершенно без JavaScript! Изменится случайным порядком — всё зависит от того как вы проведёте мышкой.

Устройство простое (если убрать лишнее):

@keyframes wag {
    50% {
         transform: rotate(10deg);
    }
}

сегментхвоста {
       /* остальные стили */
        transform: rotate(-10deg);
        animation-name: wag;
        animation-duration: 3s;
        animation-iteration-count: infinite;
        animation-timing-function: ease-in-out;
}

сегментхвоста:hover {
        animation-name: none; /* отключаем анимацию, если навели мышкой */
}

На наведение мышкой анимация отключается, от начала червяка до места наведения (теги-то вложенные) причём остальные сегменты всё ещё анимируются. Если убрать мышку, то анимация начинается с начала, поэтому, поводив мышкой, можно заставить червяка причудливо извиваться — анимация разных сегментов будет стартовать в разные моменты времени!

17 комментариев

Шрифт на чистом CSS

Десять лет назад я пытался решить проблему отсутствия у пользователей татарского шрифта (с Юникодом тогда было плохо, да и татарский туда не входил по-моему) через CSS — комбинируя стилем буквы со знаками препинания, чтобы получить татарские «җ», «ө», «ң» и так далее. Т. е. я брал «ж» и запятую, сдвигал их относительно друг друга и получал «җ».

Мощность компьютеров и возможности браузеров тогда были таковы, что такой HTML выводился буквально по слогам — компьютер нагружался сильно.

Сейчас такой проблемы нет, зато мощность компьютеров и браузеров возросли многократно. Я попробовал для интереса создать шрифт на чистом ЦСС. Там где можно пользовался только стилем бордюра, где нельзя — использовал CSS-transformation.

CSS-шрифт (18.88КиБ)

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

Слова пишутся внутри тега TEXT, буквы повешены на однобуквенные теги, им соответствующие. Для наглядности, вот код, который напишет HELLO с моим стилем CSS:

<text><h></h><e></e><l></l><l></l><o></o></text>

Цифр я изготовил всего две, они на тегах D1 и D8 (т. е. работают только однёрка и восьмёрка), кроме всего есть два знака препинания — точка (на теге DOT) и запятая (COMMA).

Из-за использования трансформаций, часть букв будет работать только в определённых версиях браузеров: ФФ 3.5+, ИЕ 9+, Опере 10.5, Сафари 3.1. По поводу «Хрома» ничего сказать не могу, когда он начал поддерживать трансформации мне неизвестно.

Обратите внимание, кстати, что в ИЕ можно такой шрифт создать уже с версии 5.5 — там есть фильтр matrix, при помощи которого можно эмулировать всё, что я использовал.

В ужатом состоянии (через оптимизатор CSSO) шрифт занимает всего 3,8КБ.

9 комментариев

Вложено и повёрнуто

Вложено и повёрнуто (85.42КиБ)

С тех пор как в браузерах появилась возможность свободно вращать любые объекты, мне нравится смотреть что из этого можно сделать, причём нравится экспериментировать с одинаковыми многократно вложенными объектами.

Т. е. поворот в CSS я задаю на один и тот же градус, но поскольку объекты вложены друг в друга, родительский угол тоже учитывается и получаются разнообразные кольца с регулярным рисунком. Если подключить к этому графику (тот же SVG), то всякие рождественские венки и прочие кольца со сложными, но повторяющимся рисунком, можно делать очень естественными образом.

Кстати, а если наложить несколько изображений с разным шагом и z-index, вообще страшно подумать какие рисунки можно выводить!

15 комментариев

Opera 12.50 CSS hack

Ребята из «Оперы» уже трудятся над весьма интересной версией 12.50 и у меня уже готов ЦСС-хак для неё:

@media (-o-min-device-pixel-ratio: 1/100) {
    @media all {
         body {
            background: red; /* Opera 12.50+ */
        }
    }
}

И ещё вариант:

_:-o-prefocus:fullscreen, body {
    background: red; /* Opera 12.50+ */
}

Вместо body надо подставить нужный вам селектор.

11 комментариев

Opera 12 CSS hack

Вышла «Опера» 12, многое поменялось, на некоторых моих сайтах едут стили. В частности, на «Маке» «Опера» поменяла поведение стиля «font-size: 0» — теперь он отображается шрифтом какого-то минимального размера.

Пришлось придумывать ЦСС-хак для этой версии. Я не могу себе позволить искать новое решение и заново тестировать его во всех браузерах, мне проще оставить всё как есть, сделав исключение.

Хак получился вот такой:

@media (min-resolution: .001dpcm) {
    _:-o-prefocus, body { /* стиль только для Opera 12.00+ */
        background: red;
    };
}

Соответственно, на место «body» надо поставить требуемый тег или селектор.

36 комментариев

Перевод картинки в серый средствами CSS

Серое в CSS (92.76КиБ)

Экспериментировал в рамках прототипа для рабочего проекта с преобразованием изображения в серую гамму. Вообще, конечно, есть тег CANVAS, там это запросто, но интересно посмотреть появилось ли что-то в CSS со времён фильтров «Эксплорера». В IE с незапамятных времён (ещё с пятой версии) есть фильтр «gray», который даёт нужный эффект. Сейчас фильтры обзавелись префиксами и их рекомендуемый синтаксис иной, но суть та же:

.gray {
        filter: gray; /* IE 5+ */
        -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)"; /* new IE */
}

Я всё ждал когда же W3C «переизобретёт» микрософтские фильтры. Свершилось. Недавно появилась спецификация Filter Effects, которая, конечно, гораздо шире того, что когда-то предложили в «Микрософте», но основной смысл тот же, и даже некоторые фильтры похожи.

«Хром» включил в готовящуяся 18-ю версию эксперементальную поддержку этих фильтров:

.gray {
        -webkit-filter: grayscale(1); /* Chrome 18+ */
}

В «Опере» и «Файрфоксе» поддержки этих фильтров пока нет, но есть поддержка других — фильтров SVG. Можно, например, засунуть растровое изображение внутрь SVG и применить к нему нужный фильтр:

<svg style="width: 0; height: 0">
<filter id="gray">
<feColorMatrix type="saturate" values="0"/>
</filter>
</svg>

<!-- тут какой-то HTML код и вдруг понадобилась серая картика -->
<svg style="width:400px; height: 431px"><image xlink:href="/imgs/2008.11.27.jpg" width="100%" height="100%" filter="gray" /></svg>

Хорошие новости — SVG можно мешать с HTML. С версии 3.5 в «Файерфоксе» есть способ лучше — можно применять фильры SVG прямо к тегам HTML (и делать всякие другие чудесные штуки), правда все описания в документации почему-то относятся к XHTML или внешним ресурсам, но я обнаружил, что уже с четвёртой версии этот же трюк работает и с обычным HTML:

<style>
    .gray {
        filter: url(#gray);  /* Firefox 4+ */
    }
</style>

<svg style="width: 0; height: 0">
<filter id="gray">
<feColorMatrix type="saturate" values="0"/>
</filter>
</svg>

<body><img src="/imgs/2008.11.27.jpg" class="gray" /></body>

Добавлено позднее: в ходе дальнейших экспериментов оказалось, что в более ранних «Хромах» (нижнюю границу я пока не выяснил, но в моём 17-м работает), можно через CSS задавать фильтры для SVG, вот таким образом:

<style>
    .gray {
        -webkit-filter: url(#gray); /* Chrome ?? */
        -webkit-filter: grayscale(1); /* Chrome 18+ 
    }
</style>

<svg style="width: 0; height: 0">
<filter id="gray">
<feColorMatrix type="saturate" values="0"/>
</filter>
</svg>

<!-- тут HTML и внезапно понадобилась серая картинка -->

<svg style="width:400px; height: 431px"><image xlink:href="/imgs/2008.11.27.jpg" width="400" height="431" class="gray" /></svg>

При этом «Хром» требует, чтобы ширина и высота картинки были заданы в абсолютных единицах (остальные браузеры прекрасно понимают проценты).

Если собрать всё вместе, получается нижеприведённый код. Я поставил рядом два изображения: слева обычный элемент HTML img, справа — растр в SVG, по ссылке можно протестировать что поддерживает ваш браузер.

Посмотреть код.

<html>
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<style>
    .gray {
        cursor: pointer;

        filter: url(#gray);  /* Firefox 4+ */
        filter: gray; /* IE 5+ */
        -webkit-filter: url(#gray); /* Chrome ?? */
        -webkit-filter: grayscale(1); /* Chrome 18+ */
        -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)"; /* new IE */
    }

    .gray:hover {
        filter: none;
        -webkit-filter: none;
        -ms-filter: none;
    }

</style>

<svg style="width: 0; height: 0">
<filter id="gray">
<feColorMatrix type="saturate" values="0"/>
</filter>
</svg>

<body>
<img src="/imgs/2008.11.27.jpg" class="gray" />
<svg style="width:400px; height: 431px"><image xlink:href="/imgs/2008.11.27.jpg" width="400" height="431" class="gray" /></svg>
</body>

</html>

Мне негде потестировать IE и тем более десятую версию, но я где-то читал, что из «десятки» убрали фильтры, поэтому я, на всякий случай, поставил режим совместимости с девятой версией в коде.

27 комментариев

Как обойти баг со сложными селекторами CSS3 в «Вебките»

В «Вебките» (а значит в «Хроме» и «Сафари») есть баг с поддержкой сложных селекторов CSS3. Насколько мне рассказывали, это даже не баг, а сознательное решение. Относится он к сложным селекторам, связанным с «:target», «:checked» и ещё некоторым. Смысл в том, что у браузера как будто нет внутренних проверок на то, что условия поменялись (изменился элемент, на который должен указывать «:target», например) и надо переприменить селекторы к элементам.

Оказывается есть способ «подтолкнуть» браузер к этой проверке. Достаточно сделать пустую анимацию на нужном элементе. Вероятно это снижает производительность, так что осторожно, но зато работает:

section {
    -webkit-animation: 0.1s hack infinite;
}
            
@-webkit-keyframes hack {
    from {margin: 0; padding: 0;}
    to {margin: 0; padding: 0;}
}

Теперь в теге «section» эти сложные селекторы будут срабатывать.

Оставить комментарий

CSS hack: firefox 3.5+

Для «Файерфокса» есть простой, не то чтобы даже CSS-хак, а просто метод «обнулить» свойство специальным значением, если именно для этого браузера это и требуется. В CSS3 для этого придумано значение «initial», которое «Файерфокс» поддерживает должным образом с версии 3.5 (для некоторых свойств это значение поддерживается аж с первой версии). Значение поддерживается с префиксом, что и позволяет его использовать как своеобразный CSS-хак:

body {
    background-color: red;
    background-color: -moz-initial; /* для FF — значение свойства по-умолчанию */
}

.blk {
    display: none; /* для всех браузеров блок скрыт, */
    display: -moz-initial; /* кроме FF */
}

Браузеры на основе «Вебкит» так же поддерживают это значение (со 125 сборки), правда безо всякого префикса.

2 комментария

Будущее формата CSS image

Лого в CSS (38.79КиБ)

Мои эксперименты с картинками на чистом CSS внезапно дали следующий результат. Я взял для экспериментов логотип студии Лебедева в первом попавшемся цвете и перегнал его в CSS image при помощи своего конвертора, используя все префиксы браузеров: «webkit», «moz», «o» и «ms» («Конкерор» включать не стал, доля этого браузера исчезающе мала).

Оказалось, что если сжать получившийся файл (браузеры же понимают сжатый CSS), то получившийся файл будет размером меньше, чем та же картинка в формате PNG: 2,5КБ против 6,7. Файл с лого Студии я пропускал через онлайновый оптимизатор PNG (punypng), так что он вряд ли неоптимален.

Чем объяснить такой разврыв между форматом, предназначенном для эффективного сжатия графики, и неэффективным текстовым, с огромной избыточностью, сжатым обычным гзипом я не знаю.

Добавлено позднее: в комментариях картинку «утоптали» в 448 байт. Непонятно почему у меня такой плохой PNG получился.

23 комментария

Картинка на чистом CSS-2

Ещё картинка на чистом CSS (82.31КиБ)

Я тут всё это время думал как дёшево улучшить свой конвертор картинок в чистый CSS и придумал.

Выкинул «radial-gradient» и стал использовать «linear-gradient». Картинка, которая в прошлой версии занимала 164КБ, сейчас занимает 89 79. Это уже неплохо, в сжатом (gzip) виде этот код занимает 19КБ, что всего в два с половиной раза больше, чем исходное изображение в PNG. Результат можно сильно улучшить, если отказаться от повтора цвета, но изображения с мелкими деталями (с текстом, например) пострадают.

Кстати говоря, логотип студии Лебедева при конвертации из PNG (6,7КБ) уменьшился до 1,6КБ 1,3КБ. Это уже реальная экономия, если бы не префиксы браузеров.

Отказ от радиального градиента в пользу линейного позволил мне кодировать целую строку изображения одной конструкцией, что, в свою очередь, позволило сильно экономить на повторяющихся байтах (кстати, можно попробовать группировать пиксели по вертикали и горизонтали, а потом выбирать лучший вариант).

Кстати этот раз «Опера», увы, не смогла показать картинку, так как, похоже, не поддерживает позиционирование для линейных градиентов.

Добавлено позднее: сделал оптимизацию, теперь конвертор делает два прогона: один просматривает пиксели по горизонтали, второй по вертикали. Выбирается тот, который даёт наименьший результат.

Ключи запуска те же.

<?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
    static $shortnames = array(
        "c0c0c0" => "silver",
        "808080" => "gray",
        "800000" => "maroon",
        "ff0000" => "red",
        "800080" => "purple",
        "008000" => "green",
        "808000" => "olive",
        "000080" => "navy",
        "008080" => "teal",
        "f0ffff" => "azure",
        "f5f5dc" => "beige",
        "ffe4c4" => "bisque",
        "a52a2a" => "brown",
        "ff7f50" => "coral",
        "ffd700" => "gold",
        "808080" => "gray",
        "008000" => "green",
        "808080" => "grey",
        "4b0082" => "indigo",
        "fffff0" => "ivory",
        "f0e68c" => "khaki",
        "faf0e6" => "linen",
        "800000" => "maroon",
        "000080" => "navy",
        "808000" => "olive",
        "ffa500" => "orange",
        "da70d6" => "orchid",
        "cd853f" => "peru",
        "ffc0cb" => "pink",
        "dda0dd" => "plum",
        "800080" => "purple",
        "ff0000" => "red",
        "fa8072" => "salmon",
        "a0522d" => "sienna",
        "c0c0c0" => "silver",
        "fffafa" => "snow",
        "d2b48c" => "tan",
        "008080" => "teal",
        "ff6347" => "tomato",
        "ee82ee" => "violet",
        "f5deb3" => "wheat",
    );

    $c = strtolower($color);
    if (isset($shortnames[$c])) {
        return $shortnames[$c];
    }

    if ($c[0] == $c[1] && $c[2] == $c[3] && $c[4] == $c[5]) {
        return '#' . $c[1] . $c[3] . $c[5];
    }

    return '#' . $c;
}

class Bo {
    public function __get($num) {
        return $num ? $num . 'px' : 0;
    }
}

function Bo_convert_helper($im, $lim1, $lim2, $getpix, $dotmask, $z) {
    $end  = $lim1 - 1;
    $dots = array();

    for ($o1 = 0; $o1<$lim2; $o1++) {
        $coll = array();
        $prev    = null;
        $start   = 0;

        for ($o2 = 0; $o2<$lim1; $o2++) {
            $pixel = $getpix($im, $o2, $o1);

            if ($prev !== null && $prev !== $pixel || $o2 == $end) {
                if ($prev !== null) {
                    $color = Bo_shortcolor(vsprintf('%02x%02x%02x', $prev));

                    $coll[] = "$color {$z->$start},$color {$z->$o2}";
                }

                if ($o2 == $end && $prev != $pixel) {
                    $color = Bo_shortcolor(vsprintf('%02x%02x%02x', $pixel));
                    $cell[] = "$color {$z->$o2}";
                }

                $start = $o2;
            }

            $prev = $pixel;
        }

        $dots[] = sprintf($dotmask, implode(',', $coll), $z->$o1);
    }

    return $dots;
}


function Bo_convert($filename, $prefix = '') {
    $z = new Bo();

    if (!file_exists($filename) && !is_readable($filename)) {
        throw new Exception('File not found.');
    }

    $types = array(
        IMAGETYPE_JPG => 'jpeg',
        IMAGETYPE_PNG => 'png',
        IMAGETYPE_GIF => 'gif',
    );

    $imagedata = @getimagesize($filename);
    if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
        throw new Exception('Unknown image format');
    }

    $im = call_user_func("imagecreatefrom" . $types[$imagedata[2]], $filename);
    $sw = $w = imagesx($im);
    $sh = $h = imagesy($im);

    $hfunc = create_function('$im, $x, $y', 'return imagecolorsforindex($im, imagecolorat($im, $x, $y));');
    $hmask = "{$prefix}linear-gradient(0,%s) 0 %s";

    $hdots = join(',', Bo_convert_helper($im, $w, $h, $hfunc, $hmask, $z));

    $vfunc = create_function('$im, $y, $x', 'return imagecolorsforindex($im, imagecolorat($im, $x, $y));');
    $vmask = "{$prefix}linear-gradient(90,%s) %s 0";

    $vdots = join(',', Bo_convert_helper($im, $h, $w, $vfunc, $vmask, $z));

    if (strlen($hdots) > strlen($vdots)) {
        $dots = $vdots;
        $sw = 1;
    } else {
        $dots = $hdots;
        $sh = 1;
    }

    return <<<HTML
<head>
<title>$filename converted in background by Evgeny Stepanischev //bolknote.ru</title>
<style text="text/css">
    div {
        background: $dots;
        background-repeat: no-repeat;
        {$prefix}background-size: {$sw}px {$sh}px;
        background-size: {$sw}px {$sh}px;
        width: {$w}px; height: {$h}px;
    }
</style>
<body>
<div></div>
</body>
HTML;
}

if ($_SERVER['argc'] < 2) {
    echo <<<HELP
Image to background CSS convertor v2 by Evgeny Stepanischev. //bolknote.ru Nov 2011

Usage: {$_SERVER['argv'][0]} filename [prefix]

filename  - image file name (PNG, JPEG or GIF)
prefix    - your browser name (Opera, IE, FF, Safari, Chrome)

HELP;
exit;
}

$browsers = array(
    'ie'     => '-ms-',
    'opera'  => '-o-',
    'safari' => '-webkit-',
    'chrome' => '-webkit-',
    'chromium' => '-webkit-',
    'ff' => '-moz-',
    'firefox' => '-moz-',
);

if (isset($_SERVER['argv'][2])) {
    $type = strtolower($_SERVER['argv'][2]);
    $prefix = isset($browsers[$type]) ? $browsers[$type] : '-' . $type . '-';
} else {
    $prefix = '';
}

try {
    echo Bo_convert($_SERVER['argv'][1], $prefix);
} catch (Exception $e) {
    echo "Error: ", $e->getMessage(), "\n";
    exit(1);
};

Оставить комментарий

Цвета из CSS, названия которых короче их номеров

Кстати, если вдруг кому-нибудь для оптимизации CSS нужна таблица цветов, названия которых короче их номеров, то я вчера для конвертора сделал скрипт, который все такие цвета нашёл. Наверняка её кто-то составлял и раньше, но мне было быстрее программу написать. Список в виде массива есть в конверторе, а вот список их названий строкой: navy, green, teal, indigo, maroon, purple, olive, gray, grey, sienna, brown, silver, peru, tan, orchid, plum, violet, khaki, azure, wheat, beige, salmon, linen, red, tomato, coral, orange, pink, gold, bisque, snow, ivory.

Например, «silver» короче своего числового эквивалента «#c0c0c0» на один символ, а «tan» короче «#d2b48c» на четыре.

8 комментариев

Картинка на чистом CSS

Картинка на чистом CSS (130.69КиБ)

Сейчас CSS позволяет делать всё более интересные вещи. Раз уж сегодня короткий день, за 20 минут из «сэкономленного» часа накидал программу, которая конвертирует любую картинку (в форматах PNG, JPEG или GIF) в чистый CSS (без использования dataURI). Каждая точка кодируется через одну конструкцию radial-gradient. Очень расточительно, сделал для иллюстрации принципа.

Если заморачиваться серьёзно, то можно выявлять в картинке области с градиентом (так делается в формате PNG для упаковки) и покрывать их целиком одной конструкцией, а так же оптимизировать наложением линейных градиентных областей с прозрачностью (через rgba или hsla). Достаточно интересная программисткая задача, кстати.

Самую очевидную оптимизацию я сделал в пробке по работе домой — выделил самый частый цвет и пустил его на фон, выиграл примерно 11,5КБ на картинке в примере.

За 20 минут я только успел сделать небольшой конвертор (результат работы можно посмотреть в разделе «Храню», только для «Хрома»). Так как синтаксис завязан на префиксы, то я в генераторе сделал указание браузера под который нужно сгенерировать код (поддерживаются: IE, Opera, Firefox и Chrome). Всё это весьма расточительно, картинка 75×75 занимает 164КБ.

У меня заработало для «Хрома» и «Файэрфокса», под «Оперой» не завелось, лень проверять что она не поддерживает, IE попробовать не могу — его не бывает на «Маках».

Код конвертора

// Конвертор картинки в чистый CSS, / ноябрь 2011 

<?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
    static $shortnames = array(
        "#000080" => "navy",
        "#008000" => "green",
        "#008080" => "teal",
        "#4b0082" => "indigo",
        "#800000" => "maroon",
        "#800080" => "purple",
        "#808000" => "olive",
        //"#808080" => "gray",
        "#808080" => "grey",
        "#a0522d" => "sienna",
        "#a52a2a" => "brown",
        "#c0c0c0" => "silver",
        "#cd853f" => "peru",
        "#d2b48c" => "tan",
        "#da70d6" => "orchid",
        "#dda0dd" => "plum",
        "#ee82ee" => "violet",
        "#f0e68c" => "khaki",
        "#f0ffff" => "azure",
        "#f5deb3" => "wheat",
        "#f5f5dc" => "beige",
        "#fa8072" => "salmon",
        "#faf0e6" => "linen",
        "#ff0000" => "red",
        "#ff6347" => "tomato",
        "#ff7f50" => "coral",
        "#ffa500" => "orange",
        "#ffc0cb" => "pink",
        "#ffd700" => "gold",
        "#ffe4c4" => "bisque",
        "#fffafa" => "snow",
        "#fffff0" => "ivory",
    );

    $c = strtolower($color);
    if (isset($shortnames[$c])) {
        return $shortnames[$c];
    }

    if ($c[0] == $c[1] && $c[2] == $c[3] && $c[4] == $c[5]) {
        return '#' . $c[1] . $c[3] . $c[5];
    }

    return '#' . $c;
}

function Bo_convert($filename, $prefix = '') {
    if (!file_exists($filename) && !is_readable($filename)) {
        throw new Exception('File not found.');
    }

    $types = array(
        IMAGETYPE_JPG => 'jpeg',
        IMAGETYPE_PNG => 'png',
        IMAGETYPE_GIF => 'gif',
    );

    $imagedata = @getimagesize($filename);
    if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
        throw new Exception('Unknown image format');
    }

    $im = call_user_func("imagecreatefrom" . $types[$imagedata[2]], $filename);
    $w  = imagesx($im);
    $h  = imagesy($im);

    // Самый частый цвет идёт на background-color
    $colorfreq = array();
    for ($x = 0; $x<$w; $x++) for ($y = 0; $y<$h; $y++) @$colorfreq[imagecolorat($im, $x, $y)]++;

    $bg = array_search(max($colorfreq), $colorfreq);

    for ($x = 0; $x<$w; $x++)
    for ($y = 0; $y<$h; $y++) {
        $pixel = imagecolorat($im, $x, $y);
        if ($pixel != $bg) {
            $color = Bo_shortcolor(sprintf('%06x', $pixel));

            $dots[] = "{$prefix}radial-gradient($color,$color) {$x}px {$y}px";
        }
    }

    $dots = join(',', $dots);
    $bg   = Bo_shortcolor(sprintf('%06x', $bg));

    return <<<HTML
<head>
<title>$filename converted in background by Evgeny Stepanischev //bolknote.ru</title>
<style text="text/css">
    div {
        background: $dots;
        background-repeat: no-repeat;
        {$prefix}background-size: 1px 1px;
        background-color: $bg;
        background-size: 1px 1px;
        width: {$w}; height: {$h};
    }
</style>
<body>
<div></div>
</body>
HTML;
}

if ($_SERVER['argc'] < 2) {
    echo <<<HELP
Image to background CSS convertor by Evgeny Stepanischev. //bolknote.ru Nov 2011

Usage: {$_SERVER['argv'][0]} filename [prefix]

filename  - image file name (PNG, JPEG or GIF)
prefix    - your browser name (Opera, IE, FF, Safari, Chrome)
HELP;
exit;
}

$browsers = array(
    'ie'     => '-ms-',
    'opera'  => '-o-',
    'safari' => '-webkit-',
    'chrome' => '-webkit-',
    'chromium' => '-webkit-',
    'ff' => '-moz-',
    'firefox' => '-moz-',
);

if (isset($_SERVER['argv'][2])) {
    $type = strtolower($_SERVER['argv'][2]);
    $prefix = isset($browsers[$type]) ? $browsers[$type] : '-' . $type . '-';
} else {
    $prefix = '';
}

try {
    echo Bo_convert($_SERVER['argv'][1], $prefix);
} catch (Exception $e) {
    echo "Error: ", $e->getMessage(), "\n";
};

9 комментариев

← 1 2 3 4 5 6 →