今回は、読書の話をします。読書中、あなたの「意識」はどんなふうに進んでいると感じますか?私は、特に難しい言葉などがなければ、一定のスピードで言葉を理解していると感じます。「流れるように意識が文字の上を通り過ぎていく」というのが感覚的にぴったりとくる表現に思えます。では視線もそんなふうに滑らかに文字の上を通過していくのでしょうか?実はそうではありません。今回はd3を利用して、読書中の目の動きを測定したデータを可視化してみます。
1.HTMLに必要最小限の情報を書き込む
svg要素を埋め込むためのbody要素とdiv要素を作っておきます。次に、スタートボタンを作っておきます。onclickの中のstarttransition(event)はJavascriptの方で定義する関数名です。
<!DOCTYPE html> <html lang="ja"> <body> <div id = "divid"> </div> <button type = "button" id = "start" onclick= "starttransition(event);">start</button> </body> </html>
2. svg オブジェクトの作成
画像を張り付けるキャンバスになるsvg要素を加えます。HTML内で設定したidとselectを利用してsvg要素の挿入位置を決めます。次に、チェインでsvgの高さと幅を設定します。*1
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"> var svg = d3.select("#divid").append("svg").attr("width","400").attr("height", "300"); </script>
3. テキスト画像の読み込み
svgオブジェクトの上にgif画像を貼り付けます。チェインで画像の高さと幅を決めます。元の画像は800x600ピクセルなのですが、張り付けられた画像は設定した高さと幅でぴったりあうように縮小されます。
svg.append("g").append("svg:image").attr("width","400").attr("height","300");
4. 眼球運動データの読み込み
眼球運動データはタブ区切りのテキストファイルとして保存されています。d3のtsvを利用すると、複雑な設定なしでも、ファイル内部の情報が連想配列として読み込まれます。今回の場合、tsvによってdataオブジェクトにファイル内容が読みこまれます。その際、x軸(つまり横軸)の座標をx、y軸(つまり縦軸)の座標をyというキーで読みこんでいます。また、眼球運動データが上手く計測できていない場合は、値がx軸とy軸に対応する変数に-1の値が書きこまれているため、この値をnullに上書きします。最後に、後でこの配列を利用できるように別の配列arrに格納しておきます。
var filename = "EM.txt"; var arr = new Array(); d3.tsv(filename, function(error, data){ for (var i;i < data.length;i++){ if (data[i].CursorX == "-1"){ data[i].CursorX = null; } if (data[i].CursorY == "-1") { data[i].CursorY = null; } arr.push(new Object({x:data[i].CursorX, y: data[i].CursorY})); } });
5. 縦軸横軸の設定
今回の眼球運動を計測した際、画面の解像度が横800ピクセル、縦600ピクセルでした。これを横400ピクセル、縦300ピクセルのsvgオブジェクト上に移るように座標データを変換します。このような場合、d3のscaleという機能が便利です。連続量のデータの場合は、scaleのあとにlinear()をチェインします。さらにdomainで入力データの範囲、rangeで出力の範囲をそれぞれチェインします。
var xScale = d3.scale.linear().domain([0,800]).range([0,400]); var yScale = d3.scale.linear().domain([0,600]).range([0,300]);
6. transition を利用して注視点移動のアニメーションを作成
では、眼球運動データから注視点の変化を再現してみましょう。ここでは「transitionを利用したsvgオブジェクトの位置の変化」で紹介したsvgオブジェクトの移動の手続きを応用します。最初に、注視点を表すcircleを定義します。次に、starttransitionという関数を宣言して、その内部にcircleのtransitionをチェインします。今回の計測は1秒間に60回のサンプリングが行われているので、delayとdurationはどちらも17ミリ秒にしました。次に、さきほど作成した連想配列arrのxとyをキーに値を読みとり、それをcircleの座標に代入します。その際、先ほど作成したxScaleとyScaleを間に挟むことで、座標の値がsvgオブジェクトの範囲に収まるように変換されます。また、xとyがnullの場合は、rを0にします。これで画面上では見えなくなります。
var circle = svg .append("g") .attr("id","cir") .append("circle") .attr("r",10) .attr("cx",200) .attr("cy",150) .style("fill","blue"); function starttransition(event){ arr.forEach(function(d,i){ circle .transition() .delay(i * 17) .duration(17) .attr("cx", function(){ return xScale(+d.x); }) .attr("cy", function(){ return yScale(+d.y); }) .attr("r", function(){ if (d.y == null) { return 0; } else { return 10; } }); }); }
ここまでをまとめると以下のようになります。
読書中の眼球運動
アニメーションが示しているように、読書中の注視点は素早いジャンプと短い停止を繰り返します。ジャンプのような動きは「サッカード」、停まっている状態は「停留」と呼ばれます。基本的に文字が読めるのは停留の間だけで、サッカード中はあまりに移動が速くて文字を読めません。意識の上では、どんなに滑らかに文字の上を移動しているように感じても、実際にはサッカードと停留の繰り返しになっているんです。とても不思議ですよね。