GLOBAL-ALIGN::LEFT
CHAPTER::はじめに
拡張機能
勉強会2
----
自己
紹介
----
Piro
----
作ってる
拡張機能
----
・タブブラウザ拡張(Fx2非対応)
・コンテキストメニュー拡張(Fx2非対応)
・コンテントホルダー(Fx2非対応)
・Ez Sidebar(Fx2非対応)
・ポリシーマネージャ(Fx2非対応)
・タブキラー(Fx2非対応)
----
(´Д`;)
----
・ALTのポップアップ表示
・XHTMLルビサポート
・テキストリンク
・タブカタログ
・2ペインブックマーク
・巻き戻し/早送りボタン
・セカンドサーチ
・分割ブラウザ
・XUL/Migemo【Forked Edition】
----
今日の
ネタ
----
Canvas
----
[[PRE:
]]
----
って
何?
----
スクリプトから
操作可能な
自由描画領域
----
線とか
塗りとか
文字とか
----
SVG
との違い
----
DOM-less
----
オブジェクト的な
操作はできない
(描き捨て)
----
仕様
----
WHATWG
(W3Cではない)
----
Web Applications 1.0
Working Draft
[[3.14.7. The canvas element|http://www.whatwg.org/specs/web-apps/current-work/#the-canvas]]
----
XHTMLの
名前空間に
属する
----
名前空間URIぐらい
変えるだろ、
常識的に考えて……
/ _ノ \
| ( ●)(●)
. | (__人__)
| ` ⌒´ノ
----
CHAPTER::基本的な使い方
基本的な
使い方
----
1. 生成
2. コンテキストを取得
3. 描画
4. (゚д゚)ウマー
----
普通に書く場合
[[PRE:
]]
----
DOMで動的に生成
[[PRE:
const XHTMLNS = 'http://...';
var canvas = document.createElementNS(
XHTMLNS, 'canvas');
canvas.setAttribute('width', 300);
canvas.setAttribute('height', 200);
var box = document.getElementById('box');
box.appendChild(canvas);
]]
----
サイズを指定しなかったら
横300ピクセル
×縦150ピクセル
になります。(豆知識)
----
コンテキストを取得
[[PRE:
var canvas = document
.getElementById('canvas');
var context = canvas
.getContext('2d');
]]
----
描画
[[PRE:
var x = 30;
var y = 30;
var width = 100;
var height = 50;
// CSSの指定と同じ書き方
var color = 'rgb(255, 128, 64)';
// これから先の塗り潰し操作はこの色でやってNE!
context.fillStyle = color;
// この位置を塗りつぶしてNE!
context.fillRect(x, y, width, height);
]]
----
描画の原点や角度の変更
[[PRE:
// 原点を現在の原点からx, y分移動する
context.translate(x, y);
// 描画の倍率を0.5倍にする
context.scale(0.5, 0.5);
// 描画の角度を30度回転する
context.rotate(30 * Math.PI / 180);
]]
----
新しく描く図形の
位置・大きさ・角度が
変わるだけ
----
現在までに描画された内容を
回転させたり移動させたり
することはできない
(画像やcanvasの内容を
canvasに描画する
機能を使えば可能?)
----
フレームの内容の描画
[[PRE:
var w = window;
context.drawWindow(w, w.scrollX, w.scrollY,
w.innerWidth, w.innerHeight,
'rgb(255,255,255)');
]]
----
canvasの大きさに合わせて縮小する場合
[[PRE:
context.scale(canvas.width/w.innerWidth,
canvas.height/w.innerHeight);
context.drawWindow(w, w.scrollX, w.scrollY,
w.innerWidth, w.innerHeight,
'rgb(255,255,255)');
]]
----
こんな感じ
です。
----
詳しくは
[[MDCのCanvas関係のページ|http://developer.mozilla.org/ja/docs/Category:HTML:Canvas]]
をごらんあれ
----
CHAPTER::できること、できないこと
Canvasで
できること
できないこと
----
できること
----
・パスのストローク描画・塗り潰し
・半透明/透過
・グラデーション
・描画結果をPNG/JPEG出力
(Fx2)
・ビットマップ画像の埋め込み
・クリッピング
・拡大/縮小
・フレームの内容を描画
・変換行列による変形(Trunk)
----
できないこと
----
・文字列の描画
・描画結果をDOMで操作
・CSSで描画を制御
----
SVGほどの
パワフルさは
必要ない
という場面向け
----
CHAPTER::Canvasの注意点
注意点
----
toDataUR[[EM:I]]
じゃないよ
toDataUR[[EM:L]]
だよ
----
toDataURLは
[[EM:canvasのメソッド]]
だよ
コンテキストの
メソッドじゃないよ
----
widthとheightは
[[EM:CSSでだけ指定
しても駄目だよ]]
----
CSSでの指定だけだと、
canvasの
[[EM:描画領域のピクセル数]]
は変わらず[[EM:表示サイズ]]
だけが変わる
----
駄目な例
[[PRE:
var canvas = document.createElementNS(
XHTMLNS, 'canvas');
canvas.style.width = '600px';
canvas.style.height = '300px';
]]
→300×150(デフォルトの大きさ)の
キャンバスが2倍の大きさに拡大して
表示されてしまう
----
良い例
[[PRE:
var canvas = document.createElementNS(
XHTMLNS, 'canvas');
canvas.width = 600; // setAttributeも可
canvas.height = 300; // setAttributeも可
canvas.style.width = '600px';
canvas.style.height = '300px';
]]
----
他のウィンドウの
内容を描画するには
[[EM:UniversalBrowserRead権限]]
が必要
----
drawWindowを実行する前に
権限の取得をお忘れなく
[[PRE:
netscape.security.PrivilegeManager
.enablePrivilege('UniversalBrowserRead');
]]
※拡張機能の場合は最初から
権限があるので不要
----
drawWindowを
何度も使う場合
----
toDataURLで画像に
変換してキャッシュ
させた方が高速に
なるよ
----
利用例:[[Tab Effect XP|https://piro.sakura.ne.jp/latest/blosxom/mozilla/extension/2007-02-02_tabeffectxp.htm]]
----
CHAPTER::おまけ:要素の自由な配置
おまけ
----
要素の
自由な配置
----
position:fixed
を指定すると
XUL要素を画面上の
好きな位置に
表示できる
----
[[PRE:
box#someID {
position : fixed;
top : 0;
left : 0;
background : url(transparent-image.png);
width : 1024px;
height : 768px;
}
]]
----
[[日記で詳しく解説してます|https://piro.sakura.ne.jp/latest/blosxom/mozilla/xul/2007-02-02_splitbrowser-popupbuttons.htm]]
----
canvasと
組み合わせると
表現の幅が
広がる
----
注意点
----
たまに
クラッシュ
する
----
style属性で
position:fixedを指定とか
style.position = 'fixed'とか
HTMLの名前空間の要素に
指定とか
----
詳しい条件が
分かり次第
日記あたりで
まとめます
----
CHAPTER::タブカタログでの利用例
話を
戻す
----
タブカタログ
----
canvasの
利用例
----
大まかな
構造
----
[[PRE:
...
...
←背景バッファ
←サムネイル
←サムネイル
...
]]
----
タブの
サムネイル
----
基本的には
さっきの
例の通り
----
工夫1
----
canvas要素を
タブごとに
キャッシュ
----
2度目以降の
表示を
若干高速化
----
工夫2
----
ドロップ
シャドウ
----
サムネイルの
周囲の影
----
tableでの
角丸の応用
----
XBLと
grid
----
[[PRE:
]]
----
[[PRE:
]]
----
上下左右の4辺
+4つの角
=8種類の画像
----
工夫3
----
position:fixed
ではなく
position:absolute
で配置
----
何故?
----
スクロール
のため
----
全部のサムネイルが
position:fixedで
配置されている場合
----
[[PRE:
...
]]
----
表示位置を変えるには
全てのサムネイルの
top/leftを
変更しないといけない
----
めんどい
----
そこで
----
親要素を一つ設けて
position:fixedで配置し
その中に
position:absoluteで
サムネイルを配置
した場合
----
[[PRE:
...
]]
----
ALIGN::center
position:absoluteな
ボックスの配置の原点
||
[position:fixedな親ボックスの位置]
----
つまり
親ボックスの
top/leftをいじれば
----
中にある全ての
サムネイルが
連動して動く
----
楽チン
----
背景の
バッファ
----
なぜ
必要?
----
透過処理の
バグ(?)回避
のため
----
インライン
フレームの上に
半透明の要素や
キャンバスを置くと
----
透明になるはずの部分が
背景色になってしまう
----
実例
----
下にcanvasを置いて
描画バッファにする
----
Fx2のフィッシング
詐欺警告などでも
同じテクニックが
使われている
----
欠点
----
バッファに描画した
フレームには
スクロールバーが
描画されない
----
実例
----
ポイント
----
個々の
サムネイルに
canvasを用意
----
何故?
----
一枚のcanvasに
背景もサムネイルも
全部描画しちゃ
ダメなの?
----
理由
----
canvasは
DOM-lessだから
----
・個々のサムネイル上での
クリック操作の検出
・ドラッグ操作の検出
ができない
----
個々のサムネイルの
表示スタイルを
CSSに分離できない
----
ポリシー
----
開発効率 > 速度
----
富豪
的
----
楽に開発できる方が
他にも色々手を出せて
楽しいよね
----
CHAPTER::その他の工夫
その他の
工夫(?)
----
工夫1
----
なるべくサムネイルが
大きく表示される
ようにするには
どうすればいい?
----
サムネイルの
大きさを算出
するメソッド
----
[[PRE:
calculateThumbnailSize : function(aRelative)
{
// aRelativeはズームの指定
var w = window.innerWidth;
var h = window.innerHeight;
var padding = this.padding;
var header = this.header;
var tabNum = this.tabs.length;
var boxObject = gBrowser.getBrowserForTab(gBrowser.selectedTab).boxObject;
var aspectRatio = boxObject.height / boxObject.width;
var minSize = this.getPref('extensions.tabcatalog.thumbnail.min.enabled') ?
Math.min(this.getPref('extensions.tabcatalog.thumbnail.min.size'),
(aspectRatio < 1 ? boxObject.width : boxObject.height )) : -1;
var maxCol,
overflow = false;
]]
----
ウィンドウの面積を元にして
最大の大きさをざっくり求める
[[PRE:
var thumbnailMaxSize = w * h * 0.8 / tabNum;
var boxWidth = parseInt(Math.min(
Math.sqrt(thumbnailMaxSize),
window.outerWidth * 0.4
)) - 4 - padding;
var boxHeight = parseInt(boxWidth * aspectRatio);
]]
----
先に、最低サイズが決まっている場合の例外処理をこなしておく
[[PRE:
if (aRelative !== void(0) || minSize > 0) {
// ズームイン
if (aRelative > 0) {
boxWidth = parseInt(Math.min(this.catalog.tnWidth * 1.2, boxObject.width));
boxHeight = parseInt(boxWidth * aspectRatio );
} // ズームアウト
else if (aRelative < 0) {
boxWidth = parseInt(Math.max(this.catalog.tnWidth * 0.8, header));
boxHeight = parseInt(boxWidth * aspectRatio );
}
else { // サムネイルの最小サイズが設定されている場合
if (aspectRatio < 1) {
if (boxWidth <= minSize) {
boxWidth = minSize;
boxHeight = parseInt(boxWidth * aspectRatio );
}
}
else if (boxHeight <= minSize) {
boxHeight = minSize;
boxWidth = parseInt(boxHeight / aspectRatio );
}
}
maxCol = Math.max(1, Math.floor(w / (boxWidth + padding)));
overflow = ((boxHeight + padding + header) *
Math.ceil(tabNum / maxCol)) > h ;
}
]]
----
全てのサムネイルが画面内に収まるように大きさを自動調整
[[PRE:
else {
var boxWidthBackup,
maxRow;
do {
// 今のサムネイル幅で最大の列数を求めておく
maxCol = Math.ceil(Math.sqrt(tabNum));
while (maxCol > 1 && (boxWidth * maxCol) > w) {
maxCol--;
}
boxWidthBackup = boxWidth;
// 全てのサムネイルが縦に収るようになるまで縮小する
maxRow = Math.ceil(tabNum/maxCol);
while ((boxHeight * maxRow) > h) {
boxWidth = parseInt(boxWidth * 0.9);
boxHeight = parseInt(boxWidth * aspectRatio );
}
} while (boxWidthBackup != boxWidth);
// もしサムネイル幅が小さくなっていたら、
// 横に余裕が生まれている可能性があるので計算をやり直す
}
]]
----
ここまでで求めた情報全てを返却する
[[PRE:
return {
width : boxWidth,
height : boxHeight,
maxCol : maxCol,
maxRow : Math.ceil(tabNum / maxCol),
overflow : overflow
};
},
]]
この情報を元にサムネイル一覧を描画
----
工夫2
----
↑↓←→キーと
テンキーで
8方向に自由に
フォーカス移動
----
TabCatalog
.moveFocus(aDX, aDY)
----
[[PRE:
moveFocus : function(aX, aY)
{
var focusedNode = this.getFocusedItem();
if (!focusedNode) return;
var maxX = this.catalog.maxX;
var maxY = this.catalog.maxY;
var x = parseInt(focusedNode.getAttribute('x'))-1;
var y = parseInt(focusedNode.getAttribute('y'))-1;
]]
----
[[PRE:
do {
if (aX == 0) {
}
else if (aX < 0)
x = (x - 1 + maxX) % maxX;
else
x = (x + 1) % maxX;
if (aY == 0) {
}
else if (aY < 0)
y = (y - 1 + maxY) % maxY;
else
y = (y + 1) % maxY;
focusedNode = this.catalog.parentNode
.getElementsByAttribute(
'thumbnail-position',
(x+1)+'/'+(y+1)
);
} while (focusedNode.length == 0);
this.moveFocusToItem(focusedNode[0]);
},
]]
----
・サムネイル生成時に
X方向の最大の個数(列数)と
Y方向の最大の個数(行数)を
カウントしておく
・個々のサムネイルに
thumbnail-position="1/3"
という形でX, Yの座標を持たせておく
・それらを手がかりにノードを検索
----
工夫3
----
ドラッグ操作を
活用できるようにする
----
ドラッグで選択
→リリースで処理を実行
またはドラッグ中に実行
----
例
----
・Adobe Photoshop
・Adobe Illustrator
・iRider
など
----
どんな
場合に
便利?
----
・多数のアイテムがある
・同じ操作を複数のアイテムに
対して実行したい
・片手がふさがっている
(Shift-クリックなどが
できない状況)
----
拡張機能に移植
・タブブラウザ拡張
(タブのクローズ)
----
もう
ひとひねり
----
リリース時点で
メニューを出す
→色々な操作ができる
----
例
----
・Konqueror(KDE)
・Windowsでのファイルの
右ドラッグ
----
拡張機能に移植
・タブカタログ
(タブの選択と
クローズ)