Firefox 4: отрисовка произвольных элементов в качестве фонов посредством -moz-element

Original author: Markus Stange
  • Translation
  • Tutorial
Поль Руже:  Это блогозапись нашего гостя, Маркуса Стэнджа. Маркус обыкновенно работает над реализацией темы оформления Firefox для Маков, но на сей раз он совершил небольшую окольную прогулку по движку макетирования в Gecko, реализуя -moz-element.

В четвёртой бета-версии нового Файерфокса мы представляем вам новое расширение CSS-свойства background-image: возможность отрисовывать произвольные элементы в качестве фонов, используя -moz-element(#elementID).
<p id="myBackground1" style="background: darkorange; color: white; width: 300px; height: 40px;">
    This element will be used as a background.
    <!-- Этот элемент послужит фоном. -->
</p>
<p style="background: -moz-element(#myBackground1); padding: 20px 10px; font-weight: bold;">
    This box uses #myBackground1 as its background!
    <!-- Этот прямоугольник использует #myBackground1 в качестве фона! -->
</p>
[показать пример]

Изображение -moz-element() срабатывает совершенно так же, как и привычное изображение url(). Это значит, что оно управляется всеми привычными свойствами фонов: background-position, background-repeat, и даже background-size.

Используя background-size, вы можете создать миниатюру (thumbnail) того элемента, который используется в качестве фона — вот пример:
<ul id="thumbnails">
    <li style="background-image: -moz-element(#slide-0)"></li>
    <li style="background-image: -moz-element(#slide-1)"></li>
    <li style="background-image: -moz-element(#slide-2)"></li>
    <li style="background-image: -moz-element(#slide-3)"></li>
</ul>
#thumbnails li {
    width: 160px;
    height: 120px;
    background-repeat: no-repeat;
    background-size: contain;
}
[показать пример]

Имейте в виду три обстоятельства насчёт -moz-element:
  1. Фон — живой: что бы ни происходило в том элементе, который указан в свойстве -moz-element, фон будет отражать его изменения. Он также будет отображать выделение текста курсором, моргание текстового курсора, и так далее.
  2. Фон — просто видимость. На нём нельзя жмякнуть мышою и попасть к элементу-первоисточнику изображения. Так он устроен.
  3. Фоном можно сделать какой угодно элемент HTML. Даже <iframe>:

    [показать пример]

    Даже <video>:

    [показать пример]

    И даже холст <canvas>:

    [показать пример]
Использование холста в качестве фона может прийтись кстати во многих приложениях. Например, если вы перекрашиваете в сепию изображения CSS-фонов во браузере, то теперь не понадобится дополнительно перекодировать изображение, обработанное на холсте, получая URI «data:». Вместо этого можно станет сделать фоновым изображением сам холст.

Использование холста в качестве фонового изображения также поддерживает и Webkit, посредством -webkit-canvas().

Зацикливание отрисовки


Беглое замечание по поводу рекурсивных ссылок: если вы попробуете отрисовать таким образом элемент, который и сам отрисован при помощи -moz-element, то зацикливание отрисовки будет обнаружено и предотвращено. Так что, если пожелаете нарисовать ковёр Серпинского, придётся придумать другой способ.

Сокрытие элемента-первоисточника


Подчас вы можете не захотеть того, чтобы первоначальный элемент был видимым — а оставить на виду только фон -moz-element. И что же тогда придётся сделать вам? Просто задать «display: none» или «visibility: hidden» элементу-первоисточнику не получится, так как тогда нечего будет рисовать и на фоне -moz-element, и оттого он сделается прозрачным.

Вместо этого вам нужно предотвратить отрисовку элемента на экране, не пряча его впрямую. Один из возможных способов — поместить этот элемент внутрь другого, и задать этому другому «height: 0; overflow: hidden;» в CSS.

Три типа элементов являются исключением из этого правила: изображения, холсты и видеозаписи. Этим видам элементов дозволяется иметь свойство «display: none» и всё равно использоваться в -moz-element. Более того: они могут даже не содержаться в DOM основного документа.

Новый DOM API:
document.mozSetImageElement


К объекту document мы добавили новый метод: document.mozSetImageElement(ID_элемента, элемент).

Рассмотрим следующие две строки кода:
var slide5 = document.getElementById("slide-5");
document.mozSetImageElement("current-slide", slide5);
Теперь все те элементы, которые наделены свойством background-image: -moz-element(#current-slide), будут отображать элемент, имеющий ID slide-5 даже если существует элемент, имеющий ID current-slide!

Вызов document.mozSetImageElement("current-slide", null) отменяет действующее переопределение.

Такой API может пригождаться в разнообразных практических ситуациях. Одну из них я косвенно упомянул в предыдущем подразделе: посредством mozSetImageElement можно пользоваться холстами и изображениями, не являющимися частью дерева DOM:
var img = new Image();
img.src = "my_image.png";
document.mozSetImageElement("image", img);

var canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
var ctx = canvas.getContext("2d");
//… далее рисуем через ctx …
document.mozSetImageElement("canvas", canvas);
(показать пример)

Другая ситуация, в которой вызов mozSetImageElement оказывается полезен — создание библиотеки джаваскриптовых функций. Например, у вас может получиться вот такая функция:
var runningNumber = 0;
function addReflectionToElement(reflectedElement) {
    var referenceID = "reflected-element-" + runningNumber++;
    var reflection = document.createElement("div");
    reflection.className = "reflection";
    reflection.style.backgroundImage =
        "-moz-element(#" + referenceID + ")";
    document.mozSetImageElement(referenceID, reflectedElement);
    //… и теперь вставляем reflection в DOM …
}
Следуя таким путём, вы можете уменьшить побочный эффект от применения библиотечной функции: ей не придётся манипулировать ID того элемента, который ей передан.

И наконец, mozSetImageElement также позволяет ссылаться на элементы других документов (например, внутри <iframe>); при этом, разумеется, действуют обычные требования ко взаимодействию документов (единство их происхождения).

-moz-element для серверов отрисовки SVG: узоры и градиенты


Если вам доводилося писать SVG вручную, то вам наверняка знакомо понятие серверов отрисовки (paint servers): это те элементы, узоры и (или) градиенты, которые приходится указывать в атрибутах fill или stroke в тех случаях, когда однотонная заливка не годится. Теперь их можно использовать также и в фонах элементов HTML, используя -moz-element:
<p style="background: -moz-element(#pattern),
            -moz-element(#gradient);
        padding: 10px; color: white">
    This element has both types of SVG paint servers
    in its background: a pattern and a gradient.
    <!-- Фон этого элемента использует оба типа
    серверов отрисовки SVG: и узор, и градиент. -->
</p>

<svg height="0">
    <linearGradient id="gradient" x2="0" y2="1">
        <stop stop-color="black" offset="0%"/>
        <stop stop-color="red" offset="100%"/>
    </linearGradient>
    <pattern id="pattern" patternUnits="userSpaceOnUse"
                width="60" height="60">
        <circle fill="black" fill-opacity="0.5"
                cx="30" cy="30" r="10"/>
    </pattern>
</svg>
[показать пример]

Обратите внимание, что благодаря нашему новому парсеру HTML5 нам не обязательно использовать XHTML для того, чтобы заработал внедрённый код SVG.

Вышеописанная возможность дублирует ужé существующие возможности градиентов CSS и иллюстраций SVG, однако и она бывает необычайно полезною в некоторых ситуациях — например, в анимациях. Предположим, вам надобно создать индикатор процесса, снабжённый анимированным градиентом, наподобие вот этого:

[индикатор процесса]

Вы могли бы достичь желаемого, используя градиент CSS и некоторый джаваскрипт, периодически обновляющий свойство background-position. Однако также вы могли бы использовать градиент SVG, анимированный посредством SMIL, и оттого не требующий вообще никакого джаваскрипта:
<div
        style="background: -moz-element(#animated-gradient);">
</div>

<svg height="0">

    <linearGradient id="animated-gradient" spreadMethod="reflect"
            gradientUnits="userSpaceOnUse"
            x1="16" x2="24" y2="0">
        <animate attributeName="x1" values="16; 0" dur="350ms"
                repeatCount="indefinite"/>
        <animate attributeName="x2" values="24; 8" dur="350ms"
                repeatCount="indefinite"/>

        <stop stop-color="#0F0" offset="0"/>
        <stop stop-color="#0D0" offset="100%"/>
    </linearGradient>

</svg>
(показать пример)

Того же можно достигнуть и CSS-анимациями, однако, пока они не реализованы в Gecko, вы можете следовать вышеописанным путём.

Поддержка изображений SVG в качестве фонов CSS (баг 276431) также вскоре будет обеспечена.

И нате ещё пример: Pacman на CSS и SVG.

Приложения


У меня есть ещё пара предложений насчёт использования -moz-element:

Отражения


Что такое отражение?
#reflection {
    /* Отражение — это копия первоначального элемента… */
    background: -moz-element(#reflected-element)
                bottom left no-repeat;

    /* … отражённая сверху вниз … */
    -moz-transform: scaleY(-1);

    /* … и с постепенным затемнением от верха к низу. */
    mask: url(#reflection-mask);
}
[показать пример]

Так как к отражению мы можем применять какие угодно стили, то можно порождать эффекты наподобие анимированных кругов на воде.

Шикарные переходы между слайдами


В этом демонстрационном примере мне хочется достигнуть такого зрелищного перехода между соседними слайдами, который выглядит так, как если бы верхнюю половину предыдущего слайда завёртывали книзу, открывая под ним следующий слайд:
На этом месте во блоге Mozilla Hacks стоит видеозапись, подключённая элементом <video>, так что показать её на Хабрахабре не получится.
Как бы вы реализовали эту задумку? Несомненно, понадобилось бы какое-нибудь преобразование, однако преобразование какого именно элемента? Верхней половине слайда требуется другое преобразование, чем нижней, так что просто наложить преобразование на весь слайд не получится.

В итоге я создал четыре новых элемента (#previousUpper, #previousLower, #nextUpper и #nextLower) и поместил их в отдельный контейнер (под названием #transition), который становится видимым только во время перехода от слайда к слайду. Тогда я присваиваю им правильные размеры и налагаю соответствующую часть изображения от предыдущего или последующего слайда, используя background-image: -moz-element(#previous/nextSlide) и правильное значение свойства background-position. А затем я налаю на эти вспомогательные элементы желаемое преобразование.

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

Ещё?


У меня сейчас закончились замыслы примеров -moz-element, хотя, конечно, с этим свойством ещё многое можно проделать. Теперь ваш черёд!

Благодарности


Бóльшую часть благодарности по этому поводу заслуживает Robert O’Callahan, который сготовил первоначальную реализацию ещё в 2008 году. После своих первоначальных экспериментов, правда, ему пришлось плотно работать над более важными делами, так что патчи его пролежали в бездействии почти год, покуда (в апреле 2009 года) он не начал обсуждение в группе новостей о том, каким должен стать подходящий API. Вскоре после этого Рё Кавагучи возродил труды Роберта и все последние недели своей стажировки в Мозилле посвятил им. Ещё через год я подготовил этот патч на рецензирование (review) и провёл его через итоговые стадии, вплоть до включения в код Файерфокса.

Напоследок изложу такое же предупреждение, которое mozRequestAnimationFrame касалось: и -moz-element, и document.mozSetImageElement являются экспериментальными API. Мы не гарантируем их бесконечную поддержку, и мы не пропагандируем их использование. Мы реализовали их для того, чтобы людям можно было экспериментировать с ними, а к нам поступали бы отзывы. Мы предложим эти API в качестве стандарта (но без префикса «moz», естественно), так что отзывы о нашей реализации помогут нам улучшить будущий стандарт.

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 36

    +4
    Давно ждал аналог -webkit-canvas(), а тут по сути тот же -webkit-canvas(), да еще и с кучей бонусов. Интересные возможности однако подготовили для нас в Mozilla :)
      +2
      вкусняшки!
        +2
        В IE это появится где-то к пятнадцатой версии, когда у всех остальных данная технология отойдет в разряд устаревших…
          +2
          с какой стати это вообще должно где-то еще появиться?
          w3c и mozilla несколько разные организации.
            +2
            Однако некоторые работники MoFo работают на спецификациями w3c. Например D. Baron.
          • UFO just landed and posted this here
          +14
          Ну и зачем очередные плюшки, которые будут работать только в одном браузере?.. Файрфокс идёт по пути ИЕ6?
            +9
            Ни разу не было такой новой технологии. которая появилась бы на все браузерах одновременно.
              +4
              Чтобы что-то новое имело больше шансов на повсеместное использование, оно должно быть внесено в стандарт. Чтобы это новое появилось в большинстве браузеров (одновременно), разработчики должны в первую очередь уделять внимание соблюдению стандартов.
              (Как же всё просто на словах :)
                +2
                innerHTML появился в стандарте из-за того, что это удобный инструмент, который реализовали основные участники рынка. Это пример того, что кое что может появиться в стандарте после повсеместного использования.
              +1
              Часть вещей уже реализована в вебките и лис теперь догоняет. Мне будет удобней будет писать html5 страницы для айфона с дебагом скриптов в жуке.
                0
                Почитайте, зачем нужны префиксы.
                Если эта фишка «зайдет», она войдет в стандарт и рекомендована к внедрению во всех браузерах. Тогда в Firefox префикс уберут.
                  0
                  Чувак ты не прав. Мозилловцы очень крепко думают перед созданием новой технологии.
                    0
                    А это значит что если технология окажется эффективной для решения конкретных задач и удобной в использовании — ее внедрят остальные игроки рынка.

                    Пример — протокол Sitemaps, придуманный гуглом, теперь используют Яндекс, MSN, Yahoo и другие.

                    И таких примеров если покопать можно найти немало.
                    0
                    Да, к сожалению идет, и уже давно. Ogg Theora вместо H.264, IndexedDB вместо Web SQL как у всех остальных и т.п.
                      0
                      а почему к сожалению-то? к счастью! путь мозиллы отличается от пути мс с их ИЕ тем, что мозилла выбирает открытые стандарты, что весьма вкусно.
                        0
                        Ага, когда Safari, Chrome и Opera вовсю используют Web SQL Database, приходит Firefox и проталкивает идею с IndexedDB, в которой даже join нужно руками писать, то какое тут счастье (тут следует картинка с голубем и кулаком).
                          0
                          спеки IndexedDB сейчас развиваются ОЧЕНЬ быстро, и тот вид IndexedDB, который использовала, например 1ая бета Firefox — уже сильно устарела.
                          Причины по которым МоFo выбрала IndexedDB — описаны здесь: hacks.mozilla.org/2010/06/beyond-html5-database-apis-and-the-road-to-indexeddb/
                          А вот сравнение Web Database и IndexedDB hacks.mozilla.org/2010/06/comparing-indexeddb-and-webdatabase/
                            0
                            Да читал я это, меня 4-й пример в сравнении очень повеселил.
                            0
                            Да, и кстати: хромиум вот тоже собирается скоро использовать IndexedDB: https://groups.google.com/a/chromium.org/group/chromium-html5/browse_thread/thread/ee00c278a33e7905/25cb7c41d1e04a60?lnk=gst&q=indexeddb#25cb7c41d1e04a60
                            Так что эту идею может подцепить и хром.
                    • UFO just landed and posted this here
                      • UFO just landed and posted this here
                          0
                          Зацикливание. Не будет работать
                        • UFO just landed and posted this here
                            +1
                            Вместо этого вам нужно предотвратить отрисовку элемента на экране, не пряча его впрямую. Один из возможных способов — поместить этот элемент внутрь другого, и задать этому другому «height: 0; overflow: hidden;» в CSS.

                            Три типа элементов являются исключением из этого правила: изображения, холсты и видеозаписи. Этим видам элементов дозволяется иметь свойство «display: none» и всё равно использоваться в -moz-element.

                            Как-то это сложно. Может стоило сделать что-то вроде секции в SVG, где можно описать элементы, которые не отображаются напрямую, но на них могут ссылаться другие элементы?..
                              0
                              Секции <defs>.
                                0
                                Лучше сделать управление при помощи псевдокласса, например :asMozElement. Тогда на уровне css можно было бы управлять отображением.
                              0
                              Круто придумали, молодцы. Главное идея какбы на поверхности лежала.
                                0
                                мелкое наблюдение: если перейти на эту страницу — hacks.mozilla.org/wp-content/uploads/2010/08/flowinggradient.html
                                и сделать окно лисы неактивным, то у прогрессбаров даже цвет меняется с зелёного на серый.
                                  +1
                                  CSS-псевдокласс «:-moz-window-inactive» творит чудеса.
                                    0
                                    да, это я просто ещё раз подчеркнул преимущество SVG над обычной GIF анимацей, ведь в случае с GIF чтобы сделать подобное — пришлось бы загружать отдельную картинку.
                                  0
                                  Как-то эта технология не способствует разделению модели и представления.
                                  Т.е. на странице для отражения, например, должен быть по-сути «мусорный» тэг, да ещё и с уникальным идентификатором.
                                  Отражения у группы элементов, опять же слишком сложно: обращение только по ид, следовательно в ксс этого так просто не добъёшься, нужно описание под каждый элемент списка.
                                  Итого: логику создания придётся выносить в жс, а так же привязку CSS'ом элементов одного к другому.
                                  Хотя если в CSS будет возможность манипулировать DOM…
                                    0
                                    Лучше бы они наконец уже сделали возможным вставлять SVG в фон, как это описано в спеках W3C на протяжении многих лет…
                                      0
                                      Сделают, по-видимому, в этом году.
                                        +1
                                        Ну если оно будет с анимацией и грамотно ресайзится, то я их расцелую! Виртуально хотя бы (: Заждался уже, столько всего хорошего можно сделать с помощью SVG…

                                    Only users with full accounts can post comments. Log in, please.