HTML5 コンポーネント: 特定の目的のためのコンポーネント、第 1 回

HTML5 のコンポーネント・モデルを理解して、特定の目的のためのコンポーネントの実装に取り掛かる

この記事は、David Geary が HTML5 コンポーネントの実装方法について説明する短い連載の第 1 回目です。今回は、HTML5 コンポーネント技術について概説し、高度なスライダー・コンポーネントを一から実装する方法の説明を始めます。

David Geary, Author and speaker, Clarity Training, Inc.

David GearyCore HTML5 Canvas』の著者、David Geary は HTML5 Denver User's Group の共同設立者でもあり、Swing と JavaServer Faces に関するベストセラーの本を含め、Java に関する 8 冊の本の著者でもあります。また彼は、JavaOne、Devoxx、Strange Loop、NDC、OSCON などのカンファレンスで頻繁に講演を行っており、JavaOne Rock Star にも 3 度選ばれています。彼は developerWorks の連載記事、「HTML5 による 2D ゲームの開発」、「JSF 2 の魅力」、そして「GWT の魅力」の著者でもあります。



2013年 6月 06日

この連載について

HTML5 の標準 UI コンポーネント・モデルは今でも進化を続けています。この連載では、HTML5 のエキスパートである David Geary が、既存の技術を使用して特定の目的のための独自の HTML5 コンポーネントを作成する方法、そして正規の HTML5 コンポーネント・システムを定義する途上にある仕様を利用する方法を説明します。

どのプログラミング・プラットフォームでも、コンポーネント・モデルは、UI 要素の標準セットを提供し、追加の UI 要素を作成するためのインフラストラクチャーとなる重要な部分です。

HTML5 は比較的新しい技術であり、HTML5 コンポーネントの実装、および HTML5 コンポーネントと他のコンポーネントとの統合を標準化することを目的とした一連の仕様が登場してから、それほど時間は経っていません。現時点では、HTML5 コンポーネント・モデルは次の 3 つの仕様によって定義されています。

  • 「Introduction to Web Components」
  • 「Shadow DOM」
  • 「HTML Templates」

この 3 つの仕様で定義されている機能は、コンポーネント DOM ツリーのカプセル化 (Shadow DOM)、ページ作成者が HTML ページで使用できるコンポーネントのカスタム・タグの作成 (カスタム HTML 要素)、および Shadow DOM と挿入ポイントの宣言による指定 (HTML テンプレート) に関するものです (詳細については、囲み記事「HTML5 コンポーネント仕様」を参照してください)。

HTML5 コンポーネント仕様

「Introduction to Web Components」は Web コンポーネントの大まかな概要であり、その主な目的は、「Shadow DOM」や「HTML Templates」などの他の仕様を生み出すことです。

「Shadow DOM」仕様は、W3C (World Wide Web Consortium) のワーキング・ドラフトであり、実質的に HTML5 の一部であるとみなすことができます。これまでブラウザー・ベンダーでは、動画要素やレンジ入力に下位レベルの要素をカプセル化することができましたが、Shadow DOM によって一般の開発者でも、実装するコンポーネントをカプセル化する際に実にさまざまなことができるようになります。

「HTML Templates」仕様はまだワーキング・ドラフトにはなっていませんが、伝えられているところによると、開発者たちの間でかなりのマインドシェアを獲得していることから、ワーキング・ドラフトになる可能性は十分にあります。HTML テンプレートでは、挿入ポイントを持つ Shadow DOM を宣言によって指定し、その挿入ポイントに CSS セレクターで選択した要素を挿入することができます。

各仕様の詳細については、「参考文献」に記載されているリンクを参照してください。

けれども、コンポーネントを実装するとなると、まったく異なるスキル・セットが必要になってきます。具体的には、魅力的なグラフィックを作成するスキル、HTML 要素を層にしてユーザーの興味を引く対話型インターフェースをサポートするスキル、そしてイベント・リスナーをサポートするスキルなどが必要です。そこで、この連載では、HTML5 コンポーネントの開発に伴う次の側面を取り上げます。

  • 特定の目的のためのコンポーネント
  • Shadow DOM、テンプレート、カスタム要素
  • Mozilla の X-Tag

連載の今回および次回の記事では、特定の目的のための HTML5 コンポーネントを一から実装する方法を説明しますが、新しい仕様の内容は一切盛り込まずに実装します。新しい仕様についてはその後の記事で詳しく探り、Shadow DOM、カスタム要素、テンプレートを取り入れる方法を説明して、皆さんが HTML5 コンポーネントを作成する際のレパートリーに加えます。

具体的には、この記事では以下の内容について説明します。

  • 特定の目的のための HTML5 コンポーネントを実装する方法
  • canvas 要素を使用して魅力的なグラフィックを描画する方法
  • canvas 要素を層に重ねて特殊なエフェクトを実装する方法
  • HTML 要素のリアルタイム・ドラッグ操作を実装する方法

複数の HTML5 コンポーネントから 1 つのコンポーネントを作成する

図 1 に、画像処理コンポーネントを示します。このコンポーネントは、複数の一般的なコンポーネント (アイコン、ツールバー、スライダー、および画像表示域) で構成されています。

図 1. 画像処理コンポーネント
画像ビューアー・コンポーネントの 2 つのスクリーン・ショットを組み合わせたもの。上のほうのスクリーン・ショットでは、サイズ変更スライダーが使用されています。下のほうのスクリーン・ショットは、回転スライダーを使って回転した後の画像を示しています。

ツールバーにあるアイコンのうちの 3 つ (「Resize (サイズ変更)」、「Rotate (回転)」、「Brightness (輝度)」) には、スライダーが関連付けられています。この 3 つのアイコンのいずれかをクリックすると、画像処理コンポーネントはツールバーの下にスライダーといくつかの関連するボタンを表示します。図 1 の上のほうのスクリーン・ショットには、サイズ変更スライダーが使用されている様子が示されており、下のほうのスクリーン・ショットには、回転スライダーを使って回転した後の画像が示されています。


スライダー・コンポーネント

スライダーはレールとノブで構成されています。スライダーの値を変更するには、ノブをレールに沿って移動させます。ノブの位置が、スライダーの値を決定します (逆に、プログラムによって値が設定されてスライダーが再描画される場合には、スライダーの値がノブの位置を決定します)。スライダーの値 (0.0 から 1.0 までの値) は、ノブの左側にあるレールのパーセンテージを表します。例えば、レールの 4分の3 がノブの左側にある場合、スライダーの値は 0.75 になります。

図 2 に、スライダーの使い方を説明する単純なアプリケーションを示します。このアプリケーションでは、スライダーの他に 2 つのボタンを使ってスライダーのノブを右 (+) または左 (-) に少しずつ動かすことができます。また、スライダーの下に示されている読み出しに、スライダーの現在の値が表示されます。このアプリケーションを実行するためのリンクを「参考文献」で参照してください。アプリケーションのソース・コードをダウンロードすることもできます。

図 2. スライダー
スライダーのスクリーン・ショット

ユーザーは以下の 3 つの方法でスライダーのノブを動かすことができます。

  • 2 つのボタンのいずれかをクリックして、スライダーの値を 10 パーセント単位で増やすか (+)、減らします (-)。
  • ノブをドラッグします。
  • レール (ノブ以外の場所) をクリックすると、クリックした位置にノブの中心が移動します。

スライダーの値がプログラムによって設定される場合 (これに該当するのは、プラス (+) ボタンまたはマイナス (-) ボタンをアクティブにする場合、あるいはスライダーのレール (ノブ以外の場所) をクリックできるようにする場合です)、ノブの動きはアニメーション化されます。このアニメーション化は、ノブが最終位置に近づくにつれてノブの動きを遅くする ease-out 機能を使用した CSS トランジションによって行われます。スライダー・コンポーネントは、このトランジションのプロパティーを JavaScript の中で設定しています。

スライダーを使用するには、リスト 1 に示すように、スライダーを初期化してから既存の DOM 要素の最後に追加します。

リスト 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() メソッドは、スライダーを追加する先の要素のサイズに合わせてスライダーのサイズを拡大または縮小します。

レンジ入力を使用しない理由

HTML にはレンジ入力が用意されていますが、それを使用する代わりにスライダー・コンポーネントを実装するのには、2 つの理由があります。まず 1 つは、すべてのブラウザーで同じルック・アンド・フィールにするためです。図 1 に示した画像処理コンポーネントがこのケースです。もう 1 つの理由として、Firefox などの一部のブラウザーでは単なるテキスト入力でレンジを表すよう要求しますが、多くの場合、それでは不十分だからです。

このスライダーには以下の特徴があります。

  • appendTo() メソッドによって DOM 要素の最後に追加されます。
  • 追加先の 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 に、図 2 に示したアプリケーションの HTML を記載します。

リスト 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>

リスト 2 の HTML の DOM ツリーは、図 3 のようになります。

リスト 3. サンプル・スライダーの DOM ツリー
サンプル・スライダーの DOM ツリーには、slider-component という ID を持つ DIV があり、この DIV に別の 2 つの DIV が含まれています。そのうちの一方の ID は controls に設定され、もう一方の ID は buttons に設定されています。buttons DIV には 2 つの入力要素があり、どちらも type はボタン、class は slider-button に設定されています。ただし、ボタンの ID は、一方が minus-button で、もう一方が plus-button です。slider-component DIV には、slider という ID を持つ空の DIV が含まれています。

リスト 2 に記載した HTML と CSS は見たとおりの単純な内容です。HTML が参照するスクリプトは 2 つあり、一方はスライダーを対象としたスクリプト、もう一方はアプリケーション自体のスクリプトです。リスト 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 の先頭で、アプリケーションは黒のストローク・スタイル、コーンフラワーブルーの塗りつぶしスタイル、そして初期値 0 を設定したスライダーを作成します。JavaScript の最後では、slider という ID の DOM 要素の最後にスライダーを追加します。その中間で JavaScript が定義しているのは、それぞれのボタンのクリック、およびスライダーの値の変更に応答する 3 つのイベント・ハンドラーです。

このアプリケーションでは、スライダーの値 (knobPercent) の調整、スライダーの再描画、読み出しの更新を行うために、onclick イベント・ハンドラーをプラス (+) ボタンとマイナス (-) ボタンのそれぞれに追加しています。

また、スライダーにも変更リスナーを追加し、アプリケーションの読み出しを更新して、スライダーの新しい値が反映されるようにします。コンポーネントが、イベント・リスナーの登録と、登録したイベント・リスナーに対するイベントの起動を行うためのメカニズムを提供することはよくあります。スライダー・コンポーネントもその例外ではありません。

スライダーの使用方法がわかったところで、次はスライダー・コンポーネントの実装について見ていきましょう。


スライダーを作成および初期化する

リスト 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 は自身に割り当てられ、存在しない場合は空のオブジェクトに割り当てられます)。

COREHTML5.Slider コンストラクター関数が取るオプションの引数には、ストロークの色、塗りつぶしの色、スライダーの初期値、ノブのアニメーションの時間 (ミリ秒) という 4 つがあります。knobPercent は、スライダーの値を表します。

コンストラクターは、スライダーのレールを収容するキャンバス (railCanvas) を作成します。また、リスト 4initializeKnob() によって呼び出される createKnobCanvas() を使用して (リスト 5 を参照)、2 つ目のキャンバス (knobCanvas) も作成します。最後に、このコンストラクター関数はスライダーの DOM ツリーを作成して、リスナーをスライダーに追加します。

リスト 5 に、スライダー・コンストラクターによって呼び出される最初の 3 つのスライダー・メソッド (initializeConstants()initializeStrokeAndFillStyles()initializeKnob()) を記載します。

リスト 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";
      ...

   },
   ...
};

レール用とノブ用の個別のキャンバス

このスライダー・コンポーネントでレール用とノブ用に別々のキャンバスを使用している理由は、ノブの位置が変更されたときに CSS トランジションを使用して、ノブ・キャンバスをアニメーション化できるようにするためです。スライダーが 1 つのキャンバスにレールとノブを描画するとしたら、CSS トランジションは不可能になります。それは、トランジションは HTML 要素にしか適用できないためです。

initializeConstants() メソッドは、スライダーのすべての定数に対して変数を作成します。これらの定数には、値が指定されていない場合に initializeStrokeAndFillStyles() が使用するストローク・スタイルと塗りつぶしスタイルのデフォルト値も含まれます。

リスト 5 で最も興味深いメソッドは、initializeKnob() です。このメソッドはいくつかの変数を設定してから、スライダーのノブ専用のキャンバスを作成するために createKnobCanvas() を呼び出します。createKnobCanvas() は、キャンバスがそれを包含するキャンバスの前面の左上に配置されるように、キャンバス要素を作成してそのスタイル属性を設定します。

レール用のキャンバスとノブ用のキャンバスを初期化する方法は以上のとおりです。次は、これらのキャンバスを使用してスライダーを描画する方法について見ていきましょう。


スライダーを描画する

リスト 6 に、スライダー・コンポーネントの draw() メソッドと erase() メソッドを記載します。

リスト 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() メソッドは、スライダーの両方のキャンバス (レール用のキャンバスとノブ用のキャンバス) を消去します。逆に、draw() メソッドはレールとノブを描画します。draw() メソッドは、ノブの percent (スライダーの値) を渡して呼び出すことも、引数なしで呼び出すこともできます。引数を渡さない場合、メソッドはスライダーの既存の値を使用します。

レールを描画する

リスト 7 では、スライダー・コンポーネントが 2 つのパスでレールを描画します。

リスト 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. スライダーのベース
スライダーのベースの描画

drawRail() は次に、白のグラデーションを重ね合わせます (図 5 を参照)。

図 5. スライダーのオーバーレイ
スライダーのオーバーレイの描画

これにより、図 6 に示すようにレールにある程度の奥行きが出て、上から光が当たっているような外観になります。

図 6. 合成されたスライダー
スライダーのベースとオーバーレイの描画

スライダーの overlayRailGradient() メソッドは、HTML5 Canvas の createLinearGradient() メソッドを使用してグラデーションを作成します。続いて overlayRailGradient() は、グラデーションをつけた線に沿ったいくつかの点でグラデーションの色指定を追加します。色指定はすべて純白ですが、点ごとに異なる不透明度が設定されています。最後に、overlayRailGradient() はスライダーにグラデーションを適用し、スライダーの輪郭を描画します。

ノブを描画する

スライダーは別のキャンバスに、図 7 に示すノブを描画します。

図 7. ノブのキャンバス
スライダーのノブを示すスクリーン・ショット。スライダーの色 (rgba(255,255,0,0.5)) と、ノブを描画する関数 createKnobCanvas() を要約したものが示されています。この関数は、コード行 this.knobCanvas = document.createElement('canvas') を使用してノブのキャンバス要素を作成します。

リスト 5 で、スライダーはノブのキャンバスを作成するために document.createElement() を呼び出していることを思い出してください。そのキャンバス内にノブを作成するのは、リスト 8 に記載するスライダーの fillKnob() メソッドと strokeKnob() メソッドです。

リスト 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;
   },
   ...
};

リスト 9 に記載されている addMouseListeners() メソッドは、リスト 4 でスライダーのコンストラクターが呼び出していることを思い出してください。このメソッドによって、ノブのキャンバスでのドラッグを制御するマウスダウン、マウスムーブ、マウスアップの各イベント・ハンドラーがノブのキャンバスに追加されます。2 つのヘルパー・メソッド (windowToCanvas() および knobPositionToPercent()) により、これらのイベント・ハンドラーは理解しやすくなっています。


次回の予告

この記事では、特定の目的のための HTML5 コンポーネントを実装する方法を説明しました。連載の次回の記事では、引き続きこのスライダー・コンポーネントを取り上げ、変更リスナーをサポートする方法、プログラムで CSS トランジションを使用してスライダーのノブをアニメーション化する方法、そしてスライダー・コンポーネントを任意の DOM ツリーに追加する方法を紹介します。次回の記事でまたお会いしましょう。


ダウンロード

内容ファイル名サイズ
Sample codewa-html5-components-1.zip8KB

参考文献

学ぶために

製品や技術を入手するために

  • IBM 製品の評価版: DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。

議論するために

  • developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、Wiki を調べることができます。
  • Web 開発に興味を持つ他の developerWorks メンバーを見つけてください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=932376
ArticleTitle=HTML5 コンポーネント: 特定の目的のためのコンポーネント、第 1 回
publish-date=06062013