Компоненты HTML5: Cпециальные (ad-hoc) компоненты. Часть 1

Освойте модель HTML5-компонентов и приступайте к реализации ad-hoc-компонентов

Это первая статья в коротком цикле статей Дэвида Джири, посвященном реализации HTML5 -компонентов. В этой статье вы познакомитесь с основами технологии HTML5-компонентов и начнете изучать порядок реализации сложного компонента типа ползунок (slider) "с нуля".

Дэвид Джири, президент, Clarity Training, Inc.

David GearyАвтор, лектор и консультант Дэвид Джири является президентом компании Clarity Training, Inc., Он обучает разработчиков создавать Web-приложения с использованием JSF и Google Web Toolkit (GWT). Он участвовал в экспертных группах JSTL 1.0 и JSF 1.0/2.0, был соавтором сертификационного экзамена Web Developer Certification Exam компании Sun, а также принимал участие в проектах с открытым кодом, в том числе Apache Struts и Apache Shale. Книга Дэвида Graphic Java Swing стала одной из самых продаваемых книг о Java, а Core JSF (в соавторстве с Кэем Хорстманом) - одна из самых продаваемых книг о JSF. Дэвид регулярно выступает на конференциях и встречах пользовательских групп. Он является регулярным участником конференций NFJS с 2003 года, проводил курсы в университете Java и дважды удостаивался звания JavaOne rock star.



09.09.2013

Об этом цикле статей

Модель стандартных компонентов пользовательского интерфейса для платформы HTML5 все еще находится в стадии развития. В этом цикле статей Дэвид Джири, высококвалифицированный специалист по HTML5, показывает, как создавать собственные специальные (ad-hoc) HTML5-компоненты с помощью существующей технологии — и как задействовать спецификации, которые пока еще находятся в процессе определения подлинной системы HTML5-компонентов.

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

HTML5 является относительно "молодой" технологией, которая описывается серией еще более "молодых" спецификаций, призванных стандартизировать реализацию HTML5-компонентов и их интеграцию с другими компонентами. На данный момент модель компонентов HTML5 определяется следующими тремя спецификациями:

  • "Introduction to Web Components"
  • "Shadow DOM"
  • "HTML Templates"

Эти спецификации охватывают следующую функциональность: инкапсуляция DOM-деревьев компонентов (Shadow DOM); создание специальных тегов для компонентов, которые авторы страниц могли бы использовать в своих HTML-страницах (специальные HTML-элементы); декларативное задание деревьев Shadow DOM и точек вставки (HTML-шаблонов). (Более подробные сведения приведены на врезке Спецификации HTML5-компонентов").

Спецификации HTML5-компонентов

Спецификация Introduction to Web Components (Введение в веб-компоненты) представляет собой высокоуровневый обзор веб-компонентов и предназначена преимущественно для создания других, производных спецификаций, таких, например, как спецификация Shadow DOM и спецификация HTML Templates.

Спецификация Shadow DOM имеет статус рабочей версии (Working Draft) организации W3C (World Wide Web Consortium). Фактически это означает, что данную спецификацию можно считать частью комплекта спецификаций на технологию HTML5. Спецификация Shadow DOM предоставляет обычным разработчикам исключительные возможности для инкапсуляции реализуемых ими компонентов — примерно таким же образом, каким поставщики браузеров уже давно осуществляют инкапсуляцию низкоуровневых элементов в своих видеоэлементах и вводимых данных типа range.

Спецификация HTML Templates пока не имеет статуса рабочей версии, однако по имеющимся сведениям она привлекла к себе значительное внимание со стороны разработчиков и имеет хорошие шансы на получение такого статуса. Шаблоны (Template) позволяют декларативно задавать деревья Shadow DOM, имеющие точки вставки, в которые можно вставлять элементы, выбираемые с помощью CSS-селекторов.

В разделе Ресурсы приведены ссылки на каждую из этих спецификации.

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

  • Специальные (ad-hoc) компоненты
  • Shadow DOM, шаблоны и специальные элементы
  • X-Tag от Mozilla

В этой статье и в следующей статье данного цикла демонстрируется реализация специальных HTML5-компонентов "с нуля" без какой-либо поддержки со стороны новых спецификаций. В последующих статьях будут рассмотрены спецификации и показано, как включить Shadow DOM, специальные элементы и шаблоны в ваш набор навыков для построения HTML5-компонентов.

В частности, в этой статье рассматриваются следующие задачи.

  • Реализация специальных (ad-hoc) HTML5-компонентов
  • Рисование привлекательной графики с помощью элемента canvas
  • Уровень элементов canvas для реализации специальных эффектов
  • Реализация перемещения HTML-элемента в реальном времени

Создание одного HTML5-компонента из нескольких компонентов

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

Рисунок 1. Компонент обработки изображений
Combination of two screenshots of an image-viewer component: The top screenshot illustrates use of the resize slider; the bottom screenshot shows the image after it has been rotated with the rotate slider.

Из пиктограмм, представленных на панели инструментов, три пиктограммы — Resize (изменение размеров), Rotate (вращение) и Brightness (яркость) — ассоциированы с соответствующими ползунками. Когда пользователь нажимает на какую-либо из этих пиктограмм, компонент обработки изображений отображает под панелью инструментов ползунок и две связанных с ним клавиши. Верхний снимок экрана на рис. 1 иллюстрирует использование ползунка Resize; нижний снимок экрана показывает изображение после того, как оно было повернуто с помощью ползунка Rotate.


Компонент типа "ползунок" (slider)

Ползунок состоит из направляющей и кнопки; изменение значение ползунка осуществляется посредством перемещения кнопки вдоль направляющей этого ползунка. Положение кнопки определяет значение ползунка (и наоборот — когда вы программным образом задаете это значение и, соответственно, перерисовываете ползунок). Это значение — изменяющееся от 0,0 до 1,0 — представляет долю направляющей, которая находится слева от кнопки; например, если слева от кнопки находится три четверти направляющей, то значение ползунка равно 0,75.

На рис. 2 показано простое приложение, иллюстрирующее использование ползунка. Помимо ползунка это приложение имеет две клавиши, которые осуществляют пошаговое перемещение кнопки ползунка вправо (+) или влево (-). Кроме того, это приложение поддерживает индикатор под ползунком, который отображает текущее значение ползунка. В разделе Ресурсы приведена ссылка для исполнения данного приложения; вы также можете загрузить его исходный код.

Рисунок 2. Ползунок
Screenshot of a slider

Пользователь может переместить кнопку ползунка следующими тремя способами.

  • Нажимать на одну из клавиш для увеличения (+) или для уменьшения (-) значения ползунка с шагом 10%.
  • Передвинуть кнопку.
  • Нажать на какое-либо место на направляющей, расположенное за пределами кнопки, для перемещения центра кнопки в это место.

Когда значение ползунка устанавливается программно — что имеет место в том случае, когда вы активируете клавиши (+) или (-) либо задействуете нажатие на направляющую ползунка за пределами кнопки — перемещение кнопки анимируется посредством CSS-перехода с функцией ease-out, которая замедляет движение кнопки по мере ее приближения к своему конечному пункту назначения. Компонент типа ползунок задает свойства перехода с помощью JavaScript.

Чтобы использовать ползунок, необходимо создать его и добавить его к существующему DOM-элементу (см. листинг 1).

Листинг 1. Создание ползунка
var slider = new COREHTML5.Slider( // All of the following arguments are optional
   'navy',           // Stroke color
   'cornflowerblue', // Fill color
   0.5,              // Initial slider value
   500);             // Knob animation duration in milliseconds
...

slider.appendTo('someDiv');  // Appends the slider to a DOM element with the ID someDiv

Метод appendTo() ползунка изменяет размеры этого ползунка таким образом, чтобы он расширялся или сжимался в соответствии с размерами элемента, к которому он присоединен.

Почему бы ни использовать ввод данных типа range?

Даже с учетом того, что HTML уже поддерживает входные данные типа range (диапазон), существуют две причины для реализации компонента типа "ползунок". Во-первых, иногда нужно гарантировать аналогичные "впечатления и ощущения" во всех браузерах, например, при использовании компонента обработки изображений, показанного на рис.1. Во-вторых, некоторые браузеры, такие как Firefox, настаивают на простом текстовом вводе для представления диапазона. Во многих случаях этого недостаточно.

Особенности описываемого ползунка:

  • Присоединяется к DOM-элементам с помощью метода appendTo()
  • Автоматически меняет свои размеры с целью заполнения DOM-элемента, к которому присоединен этот ползунок
  • Регистрирует слушателей события типа "изменение" с помощью метода addChangeListener()
  • Инициирует события изменений с целью изменения слушателей при перемещении кнопки ползунка
  • Анимирует кнопку с помощью CSS-перехода, когда пользователь нажимает на направляющую

Ползунки являются экземплярами объекта COREHTML5.Slider, что позволяет избежать конфликтов пространства имен. Весьма высока вероятность того, что какой-нибудь разработчик реализует ползунок с более тривиальным именем, напр., с именем Slider, которое заменило бы все существующие глобальные объекты с таким же именем. Менее вероятно, что кто-либо самостоятельно придумает имя COREHTML5.Slider.

Все аргументы конструктора COREHTML5.Slider являются опциональными; все параметры имеют разумные значения по умолчанию. В таблице 1 указаны основные методы COREHTML5.Slider.

Таблица 1. Основные методы ползунка
МетодОписание
appendTo(elementId)Добавляет DOM-элемент типа ползунок к элементу, идентификатор (ID) которого соответствует значению, переданному в этот метод.
addChangeListener(listenerFunction)Добавляет к ползунку функцию слушателя изменения. Когда кнопка ползунка изменяет свое положение, ползунок инициирует событие для всех своих слушателей изменения.
draw()Рисует направляющую ползунка и кнопку ползунка.
erase()Стирает ползунок.
redraw()Стирает ползунок, а затем снова рисует его.

В таблице 1 перечислены только внешние методы, которые разработчики применяют для манипулирования ползунком. Кроме того, объект COREHTML5.Slider имеет большое количество методов, которые он использует внутри себя, напр.: initializeStrokeAndFillStyles() и createKnobCanvas().

Разработчик получает доступ к значению ползунка посредством его атрибута knobPercent.


Использование ползунка

В листинге 2 приведен HTML-код для приложения, показанного на рис. 2.

Листинг 2. HTML-код для примера с ползунком
<!DOCTYPE html>
<html>
   <head>
      <title>Ad hoc components</title>

      <style>
         body {
            background: rgb(240,240,240);
         }
         
         #title {
            font: 18px Arial;
         }

         #slider-component {
            width: 400px;
            text-align: center;
         }

         #buttons {
            display: inline;
            font: 14px Arial;
         }
         
         #readout {
            margin-left: 25%;
            color: blue;
            font: 18px Arial;
            text-shadow: 2px 2px 2px rgb(255,255,255);
         }
         
         #slider {
            width: 75%;
            height: 30px;
            float: right;
         }

         .slider-button {
            background: rgba(100, 100, 100, 0.2);
            font: 24px Arial;
            font-weight: 1;
            border-radius: 4px;
            border: 1px solid rgba(100, 100, 180, 0.7);
            background: rgba(255, 255, 0, 0.2);
            box-shadow: 1px 1px 2px rgba(0,0,0,0.5);
            cursor: pointer;
            margin: 0px;
         }
      </style>
   </head>
   
   <body>
      <div id='title'>A custom slider</div>

      <p>
         <div id='slider-component'>
            <div id='controls'>
              <div id='buttons'>
                 <input type='button' class='slider-button'
                          id='minus-button' value='&ndash;'/>

                 <input type='button' class='slider-button' 
                          id='plus-button' value='&plus;'/>
              </div>

              <div id='slider'></div>
            </div>

            <div id='readout'>0</div>
         </div>
      </p>
   </body>

   <script type="text/javascript" src="lib/slider.js"></script>
   <script type="text/javascript" src="sliderExample.js"></script>

</html>

HTML-код в листинге 2 создает DOM-дерево, показанное на рис. 3.

Рисунок 3. DOM-дерево для примера с ползунком
The slider example's DOM tree, which contains a DIV with the ID of slider-component, which in turn contains two other DIVs, one with an ID of controls and another with an ID of buttons. The buttons DIV has two input elements, each of type button with a class of slider-button. One button has an ID of minus-button and the other has an ID of plus-button. Finally, the slider-component DIV contains an empty DIV with the ID of slider.

Код в листинге 2 (и HTML, и CSS) является достаточно простым. HTML ссылается на два скрипта, один скрипт для ползунка, а другой скрипт — для самого приложения. Этот скрипт приложения показан в листинге 3.

Листинг 3. JavaScript-код для примера с ползунком
var slider = new COREHTML5.Slider('black', 'cornflowerblue', 0),
    readoutElement = document.getElementById('readout');

document.getElementById('minus-button').onclick = function (e) {
   slider.knobPercent -= 0.1;
   slider.redraw(); 
   updateReadout();
}

document.getElementById('plus-button').onclick = function (e) {
   slider.knobPercent += 0.1; 
   slider.redraw(); 
   updateReadout();
}

function updateReadout() {
   if (readoutElement)
      readoutElement.innerHTML = slider.knobPercent.toFixed(2);
}

slider.addChangeListener(updateReadout);

slider.appendTo('slider');
slider.draw();

В листинге 3 верхняя часть JavaScript-кода приложения создает ползунок со стилем для контура черного цвета, со стилем для заливки василькового цвета и с начальным значением, равным нулю. В нижней части JavaScript-кода приложения осуществляется добавление ползунка к DOM-элементу, у которого идентификатор (ID) имеет значение slider. В средней части JavaScript-кода описываются три обработчика событий, которые реагируют на нажатия клавиш и на изменения значения ползунка.

Приложение добавляет обработчиков события onclick к клавишам (+) и (-); эти обработчики подстраивают значение ползунка (knobPercent), перерисовывают ползунок и обновляют показание индикатора (значение ползунка).

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

Теперь, когда вы увидели, как использовать ползунок, рассмотрим реализацию компонента типа ползунок.


Создание и инициализация ползунка

В листинге 4 показан JavaScript-код для конструктора ползунка.

Листинг 4. Конструктор ползунка
COREHTML5 = COREHTML5 || {};

COREHTML5.Slider = function(strokeStyle, fillStyle,
                            knobPercent, knobAnimationDuration) {
   knobPercent = knobPercent || 0;
   knobAnimationDuration = knobAnimationDuration || 1000; // milliseconds

   this.railCanvas = document.createElement('canvas');
   this.railContext = this.railCanvas.getContext('2d');
   this.changeEventListeners = [];

   this.initializeConstants();
   this.initializeStrokeAndFillStyles(strokeStyle, fillStyle);
   this.initializeKnob(knobPercent, knobAnimationDuration);

   this.createDOMTree();
   this.addMouseListeners();
   this.addKnobTransitionListeners();

   return this;
}

Первая строка в листинге 4 использует типовую идиому JavaScript, чтобы гарантировать существование глобального объекта — в данном случае, объекта COREHTML5 (если он существует, то присваивается сам себе; в противном случае он присваивается пустому объекту).

Функция конструктора COREHTML5.Slider принимает четыре дополнительных аргумента: stroke color (цвет контура), fill color (цвет заливки), initial slider value (начальное значение ползунка) и knob animation duration in milliseconds (продолжительность анимации кнопки в миллисекундах). Атрибут knobPercent отражает значение ползунка.

Конструктор создает элемент canvas под названием railCanvas, который содержит направляющую ползунка. Кроме того, он создает второй элемент canvas под название knobCanvas с помощью метода createKnobCanvas() (см. листинг 5), вызов которого в листинге 4 осуществляет метод initializeKnob(). И, наконец, функция конструктора создает DOM-дерево ползунка и добавляет слушателей к этому ползунку.

TПервые три метода ползунка, вызов которых осуществляет конструктор ползунка —initializeConstants(), initializeStrokeAndFillStyles(), initializeKnob()— показаны в листинге 5.

Листинг 5. Методы инициализации в ползунке
COREHTML5.Slider.prototype = {
   initializeConstants: function () {
      this.SHADOW_COLOR = 'rgba(100, 100, 100, 0.4)';
      this.SHADOW_OFFSET_X = 3;
      this.SHADOW_OFFSET_Y = 3;
      this.SHADOW_BLUR = 4;

      this.KNOB_SHADOW_COLOR = 'rgba(255,255,0,0.8)';
      this.KNOB_SHADOW_OFFSET_X = 1;
      this.KNOB_SHADOW_OFFSET_Y = 1;
      this.KNOB_SHADOW_BLUR = 0;

      this.KNOB_FILL_STYLE = 'rgba(255, 255, 255, 0.45)';
      this.KNOB_STROKE_STYLE = 'rgb(0, 0, 80)';

      this.HORIZONTAL_MARGIN = 2.5 * this.SHADOW_OFFSET_X;

      this.VERTICAL_MARGIN = 2.5 * this.SHADOW_OFFSET_Y;

      this.DEFAULT_STROKE_STYLE = 'gray';
      this.DEFAULT_FILL_STYLE = 'skyblue';
   },

   initializeStrokeAndFillStyles: function(strokeStyle, fillStyle) {
      this.strokeStyle = strokeStyle ? strokeStyle : this.DEFAULT_STROKE_STYLE;
      this.fillStyle = fillStyle ? fillStyle : this.DEFAULT_FILL_STYLE;
   },

   initializeKnob: function (knobPercent, knobAnimationDuration) {
      this.animatingKnob = false;
      this.draggingKnob = false;

      this.knobPercent = knobPercent;
      this.knobAnimationDuration = knobAnimationDuration;

      this.createKnobCanvas();
   },

   createKnobCanvas: function() {
      this.knobCanvas = document.createElement('canvas');
      this.knobContext = this.knobCanvas.getContext('2d');

      this.knobCanvas.style.position = "absolute";
      this.knobCanvas.style.marginLeft = "0px";
      this.knobCanvas.style.marginTop = "1px";
      this.knobCanvas.style.zIndex = "1";
      this.knobCanvas.style.cursor = "crosshair";
      ...

   },
   ...
};

Отдельные элементы canvas для направляющей и для кнопки

Компонент типа ползунок использует отдельные элементы canvas для своей направляющей и для своей кнопки, чтобы облегчить использование CSS-перехода для анимирования элемента canvas, содержащего кнопку, при изменении положения этой кнопки. Если бы вместо этого данный ползунок рисовал и направляющую, и кнопку на одном и том же элементе canvas, такой переход был бы невозможен, поскольку переходы могут применяться только к HTML-элементам.

Метод initializeConstants() создает переменные для всех констант ползунка, в т. ч. значения по умолчанию для стилей обрамления и заливки, которые метод initializeStrokeAndFillStyles() использует в том случае, если эти значения не были заданы.

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

Теперь, когда вы увидели, как инициализируются элементы canvas для направляющей и для кнопки, посмотрим, как они используются для рисования ползунка.


Рисование ползунка

Методы draw() и erase() компонента типа ползунок показаны в листинге 6.

Листинг 6. Рисование и стирание ползунка
COREHTML5.Slider.prototype = {
   ...

   erase: function() {
      this.railContext.clearRect(
         this.left - this.knobRadius, 0 - this.knobRadius,
         this.railCanvas.width  + 4*this.knobRadius,
         this.railCanvas.height + 3*this.knobRadius);

      this.knobContext.clearRect(0, 0, this.knobCanvas.width,
                                       this.knobCanvas.height);
   },

   draw: function (percent) {
      this.drawRail();
      this.drawKnob(percent ? percent : this.knobPercent );
   },
};

Метод erase() стирает оба элемента canvas ползунка (один canvas для направляющей и другой canvas для кнопки). Метод draw(), наоборот, рисует направляющую и кнопку. Вы можете передать параметр percent кнопки (содержащий значение ползунка) методу draw(), или вызвать этот метод без параметров; в последнем случае он будет использовать существующее значение ползунка.

Рисование направляющей

В листинге 7, компонент типа ползунок рисует направляющую в два прохода.

Листинг 7. Рисование направляющей
COREHTML5.Slider.prototype = {
   ...
   drawRail: function () {
      var radius = (this.bottom - this.top) / 2;

      this.railContext.save();
      
      this.railContext.shadowColor   = this.SHADOW_COLOR;
      this.railContext.shadowOffsetX = this.SHADOW_OFFSET_X;
      this.railContext.shadowOffsetY = this.SHADOW_OFFSET_Y;
      this.railContext.shadowBlur = this.SHADOW_BLUR;

      this.railContext.beginPath();
      this.railContext.moveTo(this.left + radius, this.top);
      this.railContext.arcTo(this.right, this.top, this.right, this.bottom, radius);
      this.railContext.arcTo(this.right, this.bottom, this.left, this.bottom, radius);
      this.railContext.arcTo(this.left, this.bottom, this.left, this.top, radius);
      this.railContext.arcTo(this.left, this.top, this.right, this.top, radius);
      this.railContext.closePath();

      this.railContext.fillStyle = this.fillStyle;
      this.railContext.fill();
      this.railContext.shadowColor = undefined;
      this.railContext.restore();

      this.overlayRailGradient();

      this.railContext.restore();
   },

   overlayRailGradient: function () {
      var gradient =
         this.railContext.createLinearGradient(this.left, this.top,
                                           this.left, this.bottom);

      gradient.addColorStop(0,    'rgba(255,255,255,0.4)');
      gradient.addColorStop(0.2,  'rgba(255,255,255,0.6)');
      gradient.addColorStop(0.25, 'rgba(255,255,255,0.7)');
      gradient.addColorStop(0.3,  'rgba(255,255,255,0.9)');
      gradient.addColorStop(0.40, 'rgba(255,255,255,0.7)');
      gradient.addColorStop(0.45, 'rgba(255,255,255,0.6)');
      gradient.addColorStop(0.60, 'rgba(255,255,255,0.4)');
      gradient.addColorStop(1,    'rgba(255,255,255,0.1)');

      this.railContext.fillStyle = gradient;
      this.railContext.fill();

      this.railContext.lineWidth = 0.4;
      this.railContext.strokeStyle = this.strokeStyle;
      this.railContext.stroke();
   },
   ...
};

Сначала метод drawRail() ползунка заполняет направляющую чистым цветом (рис. 4).

Рисунок 4. Основа ползунка
Drawing the slider base

Затем метод drawRail() накладывает белый градиент, как показано на рис. 5.

Рисунок 5. Наложение белого градиента
Drawing the slider overlay

Результат, показанный на рис. 6, придает направляющей определенную глубину и заставляет ее выглядеть таким образом, как если бы свет падал на нее сверху.

Рисунок 6. Составной объект ползунка
Drawing the slider base and overlay

Метод ползунка overlayRailGradient() использует метод HTML5 Canvas createLinearGradient() с целью создания градиента. Затем метод overlayRailGradient() добавляет параметры типа color stop в точках, расположенных вдоль линии градиента. Каждый параметр color stop соответствует чисто белому цвету и имеет свою степень непрозрачности. И, наконец, метод overlayRailGradient() заполняет ползунок градиентом и рисует его контур.

Рисование кнопки

На рис. 7 показана кнопка, которую ползунок рисует на отдельном элементе canvas..

Рисунок 7. Элемент canvas для кнопки
A screenshot of the slider's knob, showing the slider's color (rgba(255,255,0,0.5) and an abridged version of the function that draws the knob: createKnobCanvas(). That function creates the canvas element for the knob with this line of code: this.knobCanvas = document.createElement('canvas').

Напоминаю, что в листинге 5 ползунок создает элемент canvas для кнопки посредством вызова метода document.createElement(). Методы fillKnob() и strokeKnob() ползунка, показанные в листинге 8, осуществляют рисование на этом элементе canvas.

Листинг 8. Рисование кнопки
COREHTML5.Slider.prototype = {
   ...

   drawKnob: function (percent) {
      if (percent < 0) percent = 0;
      if (percent > 1) percent = 1;

      this.knobPercent = percent;
      this.moveKnob(this.knobPercentToPosition(percent));
      this.fillKnob();
      this.strokeKnob();
   },
   
   fillKnob: function () {
      this.knobContext.shadowColor   = this.KNOB_SHADOW_COLOR;
      this.knobContext.shadowOffsetX = this.KNOB_SHADOW_OFFSET_X;
      this.knobContext.shadowOffsetY = this.KNOB_SHADOW_OFFSET_Y;
      this.knobContext.shadowBlur    = this.KNOB_SHADOW_BLUR;

      this.knobContext.beginPath();

      this.knobContext.arc(this.knobCanvas.width/2, this.knobCanvas.height/2,
                           this.knobCanvas.width/2-2, 0, Math.PI*2, false);

      this.knobContext.clip();

      this.knobContext.fillStyle = this.KNOB_FILL_STYLE;
      this.knobContext.fill();
   },

   strokeKnob: function () {
      this.knobContext.lineWidth = 1;
      this.knobContext.strokeStyle = this.KNOB_STROKE_STYLE;
      this.knobContext.stroke();
   },
   ...
};

Метод drawKnob() принимает параметр percent, который отражает положение ползунка. Этот метод задает значение ползунка и перемещает кнопку соответствующим образом, а затем заполняет ее и прорисовывает ее контур.

Метод fillKnob() заполняет кнопку полупрозрачным желтым цветом, что позволяет увидеть направляющую сквозь эту кнопку; при этом сама кнопка выглядит таким образом, как будто она освещена. Метод strokeKnob() прорисовывает контур кнопки чистым цветом.


Перемещение кнопки

Код ползунка для перемещения кнопки показан в листинге 9.

Листинг 9. Перемещение кнопки
COREHTML5.Slider.prototype = {
   ...
   
   addMouseListeners: function () {
      var slider = this; // Let event handlers access this object

      this.knobCanvas.addEventListener('mousedown', function(e) {
         slider.draggingKnob = true;
         e.preventDefault();
      };
      
      this.knobCanvas.addEventListener('mousemove', function(e) {
         var mouse = null,
             percent = null;

         e.preventDefault();

         if (slider.draggingKnob) {
            mouse = slider.windowToCanvas(e.clientX, e.clientY);
            percent = slider.knobPositionToPercent(mouse.x);

            if (percent >= 0 && percent <= 1.0) {
               slider.erase();
               slider.draw(percent);
            }
         }
      }, false);

      this.knobCanvas.addEventListener('mouseup', function(e) {
         e.preventDefault();
         slider.draggingKnob = false;
      }; 
   }, 

   windowToCanvas: function(x, y) {
      var bbox = this.railCanvas.getBoundingClientRect();

      return {
         x: x - bbox.left * (this.railCanvas.width  / bbox.width),
         y: y - bbox.top  * (this.railCanvas.height / bbox.height)
      };
   },

   knobPositionToPercent: function(position) {
      var railWidth = this.right - this.left - 2*this.knobRadius;
          percent = (position - this.left - this.knobRadius) / railWidth;

      percent = percent > 1.0 ? 1.0 : percent;
      percent = percent < 0 ? 0 : percent;

      return percent;
   },
   ...
};

Напоминаю, что в листинге 4 конструктор ползунка вызывает метод addMouseListeners(), показанный в листинге 9. Этот метод добавляет к элементу canvas с кнопкой нескольких обработчиков событий (типа mouse-down, mouse-move, mouse-up), которые руководят перемещением этого элемента canvas с кнопкой. Два helper-метода (windowToCanvas() и knobPositionToPercent()) упрощают понимание этих обработчиков событий.


В следующей статье данного цикла

В этой статье была продемонстрирована реализация специального (ad-hoc) HTML5-компонента. В следующей статье данного цикла исследование компонента типа "ползунок" будет продолжено; в частности, будет показано, как поддерживать слушателей изменения (change listener), как программным образом использовать CSS-переходы для анимирования кнопки ползунка и как добавить компонент типа ползунок к любому DOM-дереву. До встречи в следующей статье.


Загрузка

ОписаниеИмяРазмер
Учебный программный кодwa-html5-components-1.zip8 КБ

Ресурсы

  • Ориргинал статьи: HTML5 components: Ad-hoc components, Part 1.
  • Спецификации организации W3C по HTML5-компонентам
  • Core HTML5 Canvas (David Geary, Prentice Hall, 2012). Книга Дэвида Гири, предлагающая обширный обзор API Canvas и сведения по разработке игр. Посетите также сопутствующий веб-сайт и блог.
  • Приложение Slider: Онлайновая версия приложения с ползунком, описываемого в данной статье.
  • Mind-blowing apps with HTML5 Canvas(Умопомрачительные приложения с применением HTML5 Canvas): презентация Дэвида Гири на конференции Strange Loop 2011.
  • "Основы HTML5 developerWorks, октябрь 2011 г. Познакомьтесь с основами HTML5 с помощью этой учебной программы developerWorks серии "Путь к знаниям" (Knowledge path).
  • HTML5 2D game development (Разработка 2D-игр на HTML5); developerWorks. Цикл статей Дэвида Гири, посвященный созданию игры на платформе HTML5.
  • Раздел для веб-разработчиков на ресурсе developerWorks: Статьи по различным веб-решениям. Материалы по тематике "Веб-разработка" в разделе Technical library (техническая библиотека): обширный ассортимент технических статей, рекомендаций, руководств, учебных пособий и стандартов, а также материалов серии IBM Redbook.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура
ArticleID=943775
ArticleTitle=Компоненты HTML5: Cпециальные (ad-hoc) компоненты. Часть 1
publish-date=09092013