Архив за Апрель 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');
    	});
    });
    

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

    Метки: , ,
  • Кнопки: reloaded

    Удивительно, как некоторые граждане извращаются, чтобы сделать элементарные вещи. Странно, что никто не предложил простого решения такой простой задачи, особенно когда не учитывается размер шрифта и не нужна полупрозрачность. Вот мой вариант, как можно решить такую задачу:

    xbtn

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

    stage1

    То есть нужно сделать инлайновый блок высотой 22 пикселя, боковым паддингом в 12 пикселей и фоновой картинкой. Но есть небольшая проблема: текст внутри может меняться. Вместе с этим, естественно, должна «увеличиваться» фоновая картинка. Как этого добиться? Очень просто.

    А что если мы сделаем два вложенных друг в друга блока и обоим положим одну и ту же картинку: первому блоку сделаем выравнивание по левому краю, а второму — по правому?.

    stage2

    Для этого саму картинку нужно сделать достаточно длинной — пикселей 400 по ширине,— чтобы даже очень длинный текст поместился. Реализация очень простая: два элемента с фоновой картинкой, вложенных друг в друга, внутреннему говорим padding: 0 12px;. Внутренний элемент разопрет внешний до нужных размеров.

    Но есть еще одна незадача: фоновая картинка внутреннего элемента полностью перекрывает фон внешнего элемента:

    stage3

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

    stage4

    Теперь приступаем к верстке. По сути, нам нужно указать свойства только внутреннему элементу, сделав так, чтобы внешний элемент сам подстроился под внутренний. Для задания точных размеров нам всего-лишь нужно сделать эти элементы инлайн-блоками:

    .styled-button, .styled-button button {
    	background:url(button.png) no-repeat;
    	display:-moz-inline-stack;
    	display:inline-block; /* Эмуляция для FF2 */
    	font-size:11px;
    }
    
    .styled-button {
    	margin-right:3px;
    }
    
    .styled-button button {
    	border:0;
    	margin:0;
    	padding:0 12px 0 9px;
    	position:relative;
    	left:3px;
    	height:22px;
    	line-height:22px;
    	background-position:top right;
    	white-space:nowrap;
    }
    

    Сам HTML-код совсем уж простой:

    
    

    Тестовая страница

    Естественно, мы, как и автор заметки на Хабре, забили на то, что пользователь захочет увеличить размер шрифта. Для таких случаев есть более универсальный метод.

    Метки: , ,
  • Не используйте @import

    Стив Саудерс (Steve Souders) — инженер из Yahoo! — написал прекрасную заметку о том, почему не стоит использовать @import для подключения CSS. Если коротко: @import в большинстве случаев блокирует параллельную загрузку CSS-файлов. А так как сама загрузка CSS блокирует отрисовку документа — пользователь будет долго видеть пустой экран.

    Метки: ,
  • Aptana Zen Coding v0.1 (beta)

    Выпустил первую бета-версию Aptana Zen Coding. Хватайте ее с сайта проекта на Google Code. Там же есть краткая документация по установке и настройке (более полную версию сейчас пишет Вадим).

    Все возможности, а также некоторые новые фичи показаны на скринкасте:

    Видео в хорошем качестве

    Метки: , ,
  • Aptana Zen Coding Demo

    По мотивам Zen Coding Concept.


    Видео-демонстрация

  • ФДВР. Каналы и маски: практика — I

    В этой статье мы научимся на практике применять каналы и маски для оптимизации изображений в формате PNG.

    Первое, на что хочется обратить внимание, так это на игнорируемые многими разработчиками маски влияния на цветовое распределение и на смешивание в окне Save for Web:

    masks

    Давайте посмотрим, как это может помочь нам в оптимизации PNG-изображения. Возьмем исходник:

    clock

    Попробуем сохранить его в PNG8 со следующими настройками: Color reduction: Adaptive, Dither: No dithering, Colors: 256. Не будем рассуждать, насколько целесообразно сохранять такое изображение в PNG (мы ведь тренируемся), лучше посмотрим на то, что получилось:


    42 КБ

    Первое, что мне бросается в глаза — «побитый» маятник. На общем фоне это очень яркое пятно, которое притягивает взгляд зрителя, и это же пятно выглядит очень некрасиво. Попробуем сгладить цветовые переходы маятника, выставив в параметрах сохранения Dither: 100%.

    step2
    46 КБ

    Маятник выглядит немного лучше, чего не скажешь о всем остальном изображении: вес файла увеличился на 4 КБ и на однородном фоне появился шум:

    step2_noise

    Зачастую от шума можно избавиться, снизив значение параметра Dither, но и качество самого изображения может пострадать.

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

    Начнем с цвета. Переходим в палитру Channels и создаем новый канал под названием color. Мы уже определили, что проблемной областью является маятник, поэтому в нужном месте рисуем белый кружок (для удобства лучше включить канал RGB):

    step3

    Переходим в диалоговое окно Save for Web и выставляем там следующие параметры: Color reduction: Adaptive, Dither: No, Colors: 128 (как видите, количество цветов делаем в 2 раза меньше). Теперь надо выбрать маску влияния на цвет: жмем на иконку рядом с Color reduction и в списке выбираем канал color. Смотрим:

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

    Маятник перетащил одеяло на себя, поэтому нужно снизить интенсивность цвета белого кружка, чтобы остальные области не были обделены цветом. Закрываем окно Save for Web, выделяем канал color и применяем к нему Levels (Ctrl+L или ⌘+L). Ставим нижнему белому ползунку в Output levels значение 50. Таким образом мы снизили интенсивность цвета в маске, сказав, что самый яркий цвет должен быть не больше 50:

    step5

    Пробуем еще раз сохранить с теми же параметрами:

    step6

    Уже лучше, но появились другие проблемные области, которые тоже нужно исправить:

    step7

    Думаю, вы уже поняли, как работают маски влияния. Вы подсказываете фотошопу, каким именно областям изображения нужно уделить больше внимания при снижении количества цветов. Чем ярче цвет в маске, тем больше цветов из этой области старается сохранить фотошоп. Методом проб и ошибок у меня получилась вот такая маска (вы можете скопировать изображение себе, чтобы проверить в действии):

    step8

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

    У меня получилась вот такая маска:

    step9

    Соединив все вместе получим такое изображение (параметры сохранения: Adaptive, 128 цветов):

    step10

    Вполне неплохо для 128 цветов, не находите? Сделаем последние штрихи: увеличим количество цветов до 180, а верхнюю границу смешивания снизим до 80%. В итоге наш модельный ряд выглядит следующим образом:


    256 цветов, без смешивания, без оптимизации
    42 КБ
    result
    180 цветов
    34 КБ
    step10
    128 цветов
    31 КБ

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