Поль Руже: Это блогозапись нашего гостя, Маркуса Стэнджа. Маркус обыкновенно работает над реализацией темы оформления Firefox для Маков, но на сей раз он совершил небольшую окольную прогулку по движку макетирования в Gecko, реализуя -moz-element.
В четвёртой бета-версии нового Файерфокса мы представляем вам новое расширение
<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:
- Фон — живой: что бы ни происходило в том элементе, который указан в свойстве -moz-element, фон будет отражать его изменения. Он также будет отображать выделение текста курсором, моргание текстового курсора, и так далее.
- Фон — просто видимость. На нём нельзя жмякнуть мышою и попасть к элементу-первоисточнику изображения. Так он устроен.
- Фоном можно сделать какой угодно элемент 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, хотя, конечно, с этим свойством ещё многое можно проделать. Теперь ваш черёд!
Благодарности
Напоследок изложу такое же предупреждение, которое mozRequestAnimationFrame касалось: и -moz-element, и document.mozSetImageElement являются экспериментальными API. Мы не гарантируем их бесконечную поддержку, и мы не пропагандируем их использование. Мы реализовали их для того, чтобы людям можно было экспериментировать с ними, а к нам поступали бы отзывы. Мы предложим эти API в качестве стандарта (но без префикса «moz», естественно), так что отзывы о нашей реализации помогут нам улучшить будущий стандарт.