はじめに
人間がブラウザ上でどのようにマウスポインタを動かしたか、ということを取得できればウェブデザインへも応用できるかもしれません。やり方について考えてみました。
マウスの軌跡情報の取得
まず、マウスを動かした瞬間の座標は以下のように取得することができます。
document.onmousemove = function(e){
console.log([e.pageX, e.pageY]);
}
この時、マウスが止まっていれば当然イベントは走らないので、ポインタが止まったということは分からず、この情報だけ持っていても嬉しくありません。何もしていないときに座標を取得するようなものは無かったです。
そこで、一定間隔でマウスポインタの座標を取得・配列に格納するには、次のようにsetIntervalを使います。
g_pos_arr = [];
tmp_x_pos = '';
tmp_y_pos = '';
sample = 50;// ms
document.onmousemove = function(e){
tmp_x_pos = e.pageX;
tmp_y_pos = e.pageY;
}
timer = setInterval(push_pos_arr, sample);
// ウィンドウがアクティブになっているときだけ記録
$(window).bind('focus', function(){
timer = setInterval(push_pos_arr, sample);
}).bind('blur', function(){
clearInterval(timer);
});
//実際に記録している関数
function push_pos_arr(){
if(tmp_x_pos != '' && tmp_y_pos != ''){
g_pos_arr.push([tmp_x_pos, tmp_y_pos]);
$('#x-pos').text(tmp_x_pos);
$('#y-pos').text(tmp_y_pos);
}
}
sampleは座標取得間隔です。
一度一時変数に入れているので同じ場所にいれば同じ値がずっと配列に格納され続けます。
ウィンドウやタブがアクティブのまま1日くらい放置していたら大変なことになりそうなので、実用上は適当なところで打ち切るのでしょう。
マウスの軌跡を表示
こちらはjQueryを使って超簡単に書くことができます。HTMLは1行です。マーカーは●にしました。
<div id="marker" class="display-none" style="position:absolute;">●</div>
軌跡を再生するjQueryは以下のようになります。
function play(arr){
console.log('start');
var play_arr = arr;
var frame = 0;
var len = play_arr.length;
$('#marker').removeClass('display-none');
playtimer = setInterval(function(){
$('#marker').animate({'top': play_arr[frame][1] + 'px', 'left': play_arr[frame][0] + 'px'}, sample);
frame++;
if(frame >= len){
console.log('end');
clearInterval(playtimer);
}
}, sample);
}
この関数を使って、座標を時系列順に格納したg_pos_arrをplay(g_pos_arr)とすれば画面上を動くマウスポインタが再現されます。これで終わりです。
1週間くらいネットに繋がらなくて暇だったので以下のようなこともやってみました。遊び半分なので実用性に欠けます。
軌跡情報を圧縮?
上記のやり方だと、座標取得の時間間隔sampleを1000msとか広くすると、軌跡の表示が明らかに違和感のあるものになります。かといって5msとかにしちゃうと、膨大すぎてサーバー上に保存するのをためらい、一期一会となります。
そこで、そのまま軌跡情報を扱うのではなく、圧縮すればサーバーによりたくさん保存できます!
どのように?
マウスポインタは通常それほど速く動かさないので周波数特性が低いほうに偏っていると考えられます。(未検証)
軌跡を、x(t),y(t)とみなしそれぞれに対して離散コサイン変換をして高い周波数をカットしちゃいます。この考え方は、bmp->jpgの変換方式などの基礎となっています。
以下は離散コサイン変換、逆変換関数です。
// コサイン変換
function cosine_piece(arr){
var re = 0;
var res_arr = [];
var len = arr.length;
for(var k=0; k<len; k++){
for(var i=0; i<len; i++){
re += Math.cos(Math.PI/len*(i + 1/2)*k)*arr[i];
}
res_arr.push(re);
re = 0;
}
return res_arr;
}
// コサイン逆変換
function inv_cosine_piece(arr){
var re = 0;
var res_arr = [];
var len = arr.length;
for(var k=0; k<len; k++){
for(var i=1; i<len; i++){
re += Math.cos(Math.PI/len*(k + 1/2)*i)*arr[i];
}
res_arr.push(2*re/len + 1/len*arr[0]);
re = 0;
}
return res_arr;
}
ただ、全体の軌跡をそのまま変換すると大きなトレンドが補足できなくなるので、ある一定の幅で区切り、ずらしながら変換していく、よくある処理をします。
このあたりの処理は細部にわたり煩雑なので、以下のcodepenにソースとして載せるだけにします。
(Qiitaにcodepen埋め込めない……?)
かっこつけて全部英語で書いてしまいました。
Recordボタンを押すと、押してからのマウスポインタの動きを取得し、stopボタンでやめます。その後、playボタンを押すと黒丸で実際のポインタの動きを表示、赤丸で情報圧縮されたポインタの動きを表示します。
compress rateでどの程度の圧縮率であるかを表示しています。200%なら情報量は1/2です。
前提条件がそうなので当然のように、マウスの激しい動きにはあまり追従してくれません。よく考えたらウェブを閲覧しているときのマウスの動きって静止、素早く移動、静止のサイクルなので周波数は高いほうにも分布しているのでは……。
というわけで、こんな自己満足よりは普通に保存したほうが無難ですね、と一人静かに結論を出したのでした。