Архив за Апрель 2009

  • Для вдохновения

    Покопался тут в своих старых проектах, нашел некоторые демки, которые я делал для разных конференций и проектов. Может, кому пригодится. Сразу скажу: большая часть из них не работает в IE, некоторые глючат во всех остальных браузерах. Все представленные демки — не более чем разминка для решения более сложных задач.

    • Стиралка — водим курсором по картинке, стирая защитный слой (HTML, JS, CSS).
    • Экскаватор — Для конференции РИТ-2008. Полностью программная анимация, использовались принципы инверсной кинематики (Canvas, JS).
    • Инфоскроллер — Реализация инфоскроллера Артема Горбунова на JS: анализирует текущий документ и строит из него упрощенную схему для скроллера. Делался в качестве домашнего задания для технологов Студии Лебедева (я, кстати, был той сволочью, которая их раздавала). Руки не дошли довести его до нормального состояния.
    • Волшебные пузырьки — Демонстрация способа имитации масок не прямоугольной формы (HTML, CSS, JS). Эта демка использовалась разработчиками IE8 для тюнинга производительности.
    • Спрей — Для конференции РИТ-2008. Простенькая анимация цвета и движения объектов вдоль кривой Безье (JS, jTweener).
    • Переливающиеся слова — Для конференции РИТ-2008. Тоже простая анимация: в основном демонстрируется способ у обычного текста выхватить отдельные слова и анимировать их (HTML, CSS, JS). Кстати, повторить это на флэше гораздо сложнее :) . Особенно с учетом того, что текст растягивается и можно менять размеры шрифта.
    Метки: , , , ,
  • ФДВР. Каналы и маски: практика — II

    Продолжаем мучить фотошоп в поисках оптимального способа сохранения графики в формате PNG. Сегодня мы рассмотрим способ оптимизации, основанный на восприятии цвета человеческим глазом (йес! давно хотелось написать что-нибудь такое пафосное :) . Выражаясь человеческим языком: мы будем снижать детализацию полупрозрачных областей картинки.  Именно тех областей, на которые обычный зритель не обращает внимания.

    Прежде чем приступать к оптимизации, необходимо разобраться, как сохраняются полупрозрачные PNG (PNG24 в терминологии фотошопа). В этом формате каждый пиксель описывается четырьмя байтами: RGBA (red, green, blue, alpha). Последний байт отвечает за прозрачность.

    Возьмем для примера два пикселя в формате RGBA: 123,49,54,0 и 65,130,60,0. Несмотря на то, что значения этих пикселей разные, для зрителя они выглядят абсолютно одинаково: они оба полностью прозрачные. Так как пиксели разные, PNG-упаковщик не сможет их нормально обработать фильтрами. А что будет, если мы оба этих пикселя заменим на 0,0,0,0 и 0,0,0,0? Зритель совершенно ничего не заметит, а вот для упаковщика разница будет огромной: эта последовательность гораздо эффективнее упаковывается фильтрами. Приведенный выше пример можно записать как 0(8), то есть значение 0 повторить 8 раз.

    Именно на этом и будет основан наш способ оптимизации — мы будем заменять детализированные полупрозрачные области на менее детализированные, которые будут лучше упаковываться. Для этого урока нам понадобится плагин Remove Transparency.

    Откроем исходное изображение в фотошопе:

    stage13

    Оригинал весит 199 КБ. Первое, что бросается в глаза, это тень часов и блики маятника. Продублируем текущий слой и выполним на нем фильтр Photo Wiz → Remove Transparency. Теперь мы видим, что же на самом деле сохранилось в изображении:

    stage22

    Посмотрим поближе на тень:

    stage32

    Отчетливо виден шум, но сама область — полупрозрачная (прозрачность менее 50%), и пользователь его совсем не заметит.

    Эта область — наш основной кандидат на оптимизацию. Если бы у нас был исходник, мы бы просто переделали тень, перезалив ее сплошным цветом (вы можете увидеть это в скринкасте). Но что делать, если исходник нам не доступен? Правильно — нам помогут волшебные маски. Наша задача: «упростить» цвета в полупрозрачных областях. Для этого нам пригодится фильтр Median, который сглаживает переходы между пикселями.

    Делаем Ctrl+клик или ⌘+клик по слою Layer 0 и создаем на основе выделения маску для слоя Layer 0 copy (если вы не знаете, как это сделать, перечитайте предыдущие уроки). Переходим в палитру Channels и делаем дубликат канала с маской. Теперь необходимо указать область воздействия фильтра Median. Сами часы трогать нельзя, а вот с тенью и бликами можно немного поиграться. Инвертируем маску (Ctrl+I или ⌘+I) и применяем к ней Levels (Ctrl+L или ⌘+L). Передвигаем светлый ползунок влево до значения 75 (подбирается на глаз). Таким образом мы получили маску, которой будет ограничиваться действие фильтра Median:

    stage42

    Выделяем текущий канал (Ctrl+клик или ⌘+клик), переходим в палитру Layers, кликаем на пиктограмме изображения в слое Layer 0 copy (именно на нее, а не на маску) и применяем фильтр Noise → Median. Значение радиуса выбираем на глаз, у меня получилось 3:

    stage51

    Как видите, шум совсем пропал. Попробуем сохранить изображение в формате PNG (не забудьте включить маску, если вы ее отключили).

    result1

    У меня получилось изображение весом 172 КБ (было 199 КБ) — вполне неплохо за такое короткое время. А умные мальчики и девочки должны сразу догадаться, что можно еще оптимизировать саму маску (например, с помощью Posterize или цветового режима Indexed) и сделать вес картинки еще меньше :) .

    Я показал простой способ оптимизации полупрозрачные области изображения. Но наибольший эффект достигается при комбинировании этого способа со способом из предыдущего урока. Алгоритм такой: сначала оптимизируете «сердцевину» — непрозрачные пиксели, — перегнав их в индексирванную палитру, затем добавляете полупрозрачные пиксели, снизив им детализацию. Тогда у вас получится такой же результат, как в этом примере: со 199 КБ сможете ужать картинку до 88 КБ без видимых ухудшений.

    ***

    Так получилось, что я пока не знаю, что можно еще написать на тему использования фотошопа для веб-разработки. Можете в комментариях предлагать свои идеи и задавать вопросы, самые интересные и востребованные будут описаны в виде уроков. Это могут быть как общие вопросы (например, как оптимизировать JPEG), так и частные (есть макет, как лучше всего сохранить графику).

  • Zen Coding на Python (+TextMate)

    Портировал движок Zen Coding на Python, теперь его можно интегрировать в другие редакторы, которые могут вызывать сторонние скрипты. Сам скрипт уже в SVN.

    В качестве демонстрации сделал бандл для TextMate, который пока работает только с HTML. Просьба к читателям потестировать и сказать, что не так и чего не хватает (про некоторые отсутствующие аббревиатуры писать не надо — я не скопировал их в настройки).

    PS: в работе плагин для Coda, но в этом редакторе есть некоторые сложности, которые пока не удалось обойти. Жду ответа от разработчиков.

    Метки: , ,
  • Aptana Zen Coding v0.2

    Обновил плагин Aptana Zen Coding до версии 0.2:

    • Добавлена поддержка CSS.
    • В настройки внесены все HTML и CSS аббревиатуры и сниппеты.
    • zen_settings.html.short_names переименован в zen_settings.html.aliases для более точного соответствия смыслу.
    • Добавлен новый синтаксис для секции default_attributes: [{name1: 'value1'}, {name1: 'value2'}] из-за бажной версии движка Rhino, который всегда хранит ключи объекта в алфавитном порядке (из-за этого нельзя было задать точный порядок следования атрибутов). Старый синтаксис продолжает действовать.
    • Новый синтаксис для секции aliases (бывшая short_names): 'name:*': 'name'. Это значит, что для всех аббревиатур, начинающихся с ‘name:’ (например, input:radio, script:src), будет использоваться имя тэга ‘name’. Таким образом удобно создавать заготовки тэгов с необходимыми атрибутами.
    • Свои собственные настройки теперь можно хранить в файле my-settings.js и добавлять в глобальную коллекцию. Поэтому можно спокойно обновлять библиотеку не опасаясь потерять свои заготовки. Например, для того, чтобы добавить свои собственные HTML-сниппеты в файле my-settings.js нужно написать вот такой код:
      zenExtend(zen_settings.html.snippets, {
      	foo: 'bar',
      	snip2: 'Hello world!'
      });
      

    Комментарии и пожелания приветствуются :)

    Метки: , , ,
  • Верстка табов с помощью <dl>

    Когда-то возникла необходимость сделать табы на странице. Это довольно типовая задача, поэтому, чтобы не изобретать велосипед, полез в Гугл посмотреть, как народ делает. Все реализация, которые я увидел, выглядели довольно стандартно, примерно так:

    <div class="tabs">
    	<ul class="nav">
    		<li>Tab 1</li>
    		<li>Tab 2</li>
    		<li>Tab 3</li>
    		<li>Tab 4</li>
    		<li>Tab 5</li>
    	</ul>
    
    	<ul class="content">
    		<li>Tab content 1</li>
    		<li>Tab content 2</li>
    		<li>Tab content 3</li>
    		<li>Tab content 4</li>
    		<li>Tab content 5</li>
    	</ul>
    </div>
    

    То есть два независимых списка, в одном заголовки, во втором — содержимое.

    Лично меня такая структура не устраивает, главным образом из-за того, что отсутствует семантическая связь между заголовком и содержимым. Хоть я и не страдаю наркозависимостью верстать все «валидно и семантично» ради респекта и уважухи гиков, тут отсутствие четкой связи было все-таки недостатком. Во-первых, данные из бэкэкнда придется выводить в два цикла (сначала заголовки, а потом контент), а не одним. Это хоть немного, но увеличивает объем кода и явно не добавляет производительности. Во-вторых, клиентский скрипт для переключения табов тоже будет больше и сложнее, особенно с учетом того, что на странице может быть несколько блоков с табами.

    Решил попробовать избавиться от этих проблем, а заодно размять мозги :) При выборе структуры элементов первым делом выбор пал на <dl>-список, который как нельзя лучше подходит для этой задачи. Думаю, не я первый решил попробовать сделать табы с помощью этого списка. Вот что у меня получилось.

    Чтобы приведенный пример не стал очередным источником бездумного копи-пэйста, давайте рассмотрим, за счет чего это работает.

    Предположим, у нас есть набор элементов, которым указан float: left:

    stage12

    А что будет, если одному из элементов этой последовательности указать float: right?

    stage21

    Уже неплохо: элементы, следующие за <dd>, встали ровно за предыдущими. Но нам нужно, чтобы содержимое таба растягивалось на всю ширину, поэтому прописываем этому элементу width: 100%:

    stage31

    Как и ожидалось, элемент разорвал поток. Размышляем так: если элемент прижат к правому краю контейнера, значит при изменении ширины будет изменяться левый край элемента (то есть <dd> растет справа налево). Значит, нам нужно подавить влияние левого края элемента. Сделать это можно с помощью margin-left: -100%:

    stage41

    Осталось сместить элемент вниз, сделать это можно с помощью margin-top:

    stage5

    Вот и все. По умолчанию все <dd>-элементы скрываем с помощью display: none, выбранному элементу говорим display: block. Скрипт переключения табов на jQuery выглядит совсем уж просто:

    $(function(){
    	$('dl.tabs dt').click(function(){
    		$(this)
    			.siblings().removeClass('selected').end()
    			.next('dd').andSelf().addClass('selected');
    	});
    });
    

    Способ получился не супергибким (есть привязка к точному размеру закладки таба), однако позволяет решать большинство подобных задач.

    Метки: , ,

← cтарое