まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:css

わけわからないタイトルになってしまいました。これは
  • ウェブブラウザの JavaScript を使わずに、HTML と CSS でマウスの軌跡を追跡する方法が発見された
  • 実際に確認できるサンプルが Go 言語で実装されていたので、JavaScript に移植してみた
ということです。

まず、元ネタはこれです:
Researcher Finds CSS-Only Method to Track Mouse Movements

spyware


セキュリティの研究者である Davy Wybiral 氏の以下のデモ動画付きツイートを紹介する形で伝えられていました。このデモでは JavaScript が無効にされた Tor ブラウザが使われていますが、確かに左画面でのマウスの動きが右画面で確認できています。左の画面をユーザーが使っていて、右画面ではそこでのマウスの動きがほぼリアルタイムに再現されています:




もともとウェブページにおいては JavaScript を利用することでマウスの動きを検知することができるようになっています。移動したり、クリックしたり、クリックが開放されたり、といったタイミングや、そのイベントが発生したときのマウス位置を知ること自体はこれまでも可能でした。

ただし、それには JavaScript が有効になっている、という条件があります。多くのウェブブラウザにおいて、JavaScript ははじめから有効になっているもので、あえて無効になるよう設定しない限りは有効なままです。また最近のウェブページも JavaScript が有効になっている前提で作られているものが多いので、JavaScript が有効であることが特別に危険ということはないと思っています。 一方で JavaScript を使ったイタズラページが存在していることも事実で、あえて JavaScript を無効にしてから利用する、というシーンが(それによって思い通りに動かない、ということはあるかもしれませんが)ないわけではありません。


・・・という中での今回のニュースです。安全性を高める目的でウェブブラウザの JavaScript を無効にしていても、マウスの動きがウェブページ提供側に知られてしまう可能性(というか方法)があった、というものでした。上述の Davy 氏が Go 言語で作成したサンプルも公開されています(みるとわかりますが、このシンプルなコードだけで実現できるという明瞭さ!):
https://gist.github.com/wybiral/c8f46fdf1fc558d631b55de3a0267771


で、自分がこれを参考にしてサーバーサイド JavaScript である、Node.js 向けに移植したサンプルを作ってみた、というものでした。なのでサブジェクトは正確には
 『クライアント JavaScriptを使わずにCSSでマウスを追跡する方法』をサーバーサイド JavaScript で実装してみた
という感じになりますかね。

ちなみに移植したコードはこちら:
https://github.com/dotnsf/noscript-tracking.js


で、(ブラウザ側の)JavaScript を使わずにどうやってマウスの座標を調べるのか、という話です。具体的には CSS の :hover 疑似クラスと、その background 属性に画像を指定することで、特定のエリアに入ったことを知らせるリクエストをサーバー側に発生させる、という方法によって実現しています。

上記移植コードを見ると、2つのページが定義されています。1つはコンテキストルート( "/" )へのリクエストがあった際に表示される index ページで、そのテンプレートは views/index.ejs です。もう1つは "/watch" へのリクエストがあった際に表示される watch ページ(テンプレートは views/watch.ejs)です。index ページには JavaScript は一切使われていないのですが、このページの上でマウスを動かすと、その軌跡がほぼリアルタイムに watch ページから確認できるようになる、という内容のサンプルです:
2019051501
(↑左が index ページ、右が watch ページ。index ページ上でマウスを動かす様子が watch ページ上に表示されている)


では JavaScript を使わずに、どうやって index ページから watch ページへマウスの軌跡を知らせているかを説明します。今回ブラウザの JavaScript は使いませんが、サーバーサイドのロジックは必要になります。要はスタンドアロンでどうにかできる、というものではないということです。

まず index ページについて、index ページにアクセスすると画面には格子状(今回の例では 50x50)のブロックが表示されます。個々の格子は <p> タグで構成されています。格子は見えないスタイルにすることも可能ですが、今回は視覚的にわかりやすいようにあえて枠線を表示することで見やすくしています。

そしてスタイルシートを使って、個々の格子(<p>)に :hover 疑似クラスと、その時に背景画像が設定されるよう指定します(つまり各格子の上にマウスが来ると、その格子に背景画像が表示されるように設定します)。

これが CSS だけでマウス軌跡を追跡する方法の肝になります。つまり「ある格子の上をマウスが通過した時に、画像を表示するようなリクエストがサーバー側に送られ」ます。そしてサーバー側はそのようなリクエストに対してエラー処理を行い(つまり画像は表示されない=何も変わらない)、画像を表示する変わりにリクエスト内容を記録します。これによってどの格子の上をマウスが通過したのか、という情報をサーバー側に溜めることが可能になります:
2019051603


そしてもう1つの watch ページ側では上記の処理によってサーバー側に記録されたマウスの軌跡情報ごと取得し、index ページと同様の格子ブロックを描画します。ただしその際にマウスが通過した格子だけには背景色が設定され、マウスが通過していない格子と視覚的に違いがわかるようにしています:
2019051604


この2つのページを横に並べて表示し、index ページ(下図左)上でマウスを動かすと、その情報が watch ページ(下図右)上で確認できる、ということが実現できています。なるほどね~。
2019051501


※僕のコードでは watch ページをリロードする機能までは実装していないので、F5 キーや Ctl+R などで watch ページを定期的に更新する必要がありますが、リアルタイムでサーバー側には記録されている、ということがわかると思います。



マンホールマップでも使っている、 jqPuzzle を使って任意画像をスライドパズル化する方法を紹介します。

このスライドパズルは「15パズル」とも呼ばれていて、僕くらいのオッサンは↓こんなのがおもちゃ売り場で売られているのをよく目にしました。「懐かしいゲーム」の1つです:
2018083000



この jqPuzzle を使ったスライドパズル機能はマンホールマップ内の全てのマンホール画像で遊べます。例えばこのマンホール画像ページの「スライドゲームに移動」をクリックすると:
2018083001


紹介されているマンホール画像がこんなスライドパズルに早変わり:
2018083002


"shuffle" ボタンをクリックするとランダムにシャッフルされます。16 が空いた状態でパズルスタートです:
2018083003


空いたピースの上下左右にあるピースをクリックすると、そのピースが空いた部分にスライドして移動します。これを繰り返して 1 から 15 までが正しい位置にくる完成を目指す、というものです。個人的な印象としては1、2、3までは簡単だけど、4を揃える所あたりからコツが必要になってくると思ってます:
2018083004


こんな楽しい機能を提供する jqPuzzle は jQuery を併用して、画像にスライドパズルのインターフェースを追加してくれる CSS および JavaScript のセットです。なお、jqPuzzle が対応する jQuery は 1.x までの模様なので、この点のみ注意が必要です:
2018083001


jqPuzzle を使うには公式サイトから zip ファイルをダウンロード&展開して使います(CDN は見当たりませんでした)。なお jqPuzzle の提供ライセンスは 以下の通り、GPL と MIT のデュアルライセンス、だそうです:
2018083002


利用にあたっては jQuery 1.x をロードした後に CSS と JavaScript をロードします。これで準備完了(以下の例では jQuery v1.6.2 を指定しています。また jqPuzzle の両ファイルはこれを記述する HTML と同じ階層に存在しているものとします):
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<link rel="stylesheet" type="text/css" href="jquery.jqpuzzle.css"/>
<script type="text/javascript" src="jquery.jqpuzzle.packed.js"></script>

一番簡単な利用方法は <img> タグに jqPuzzle クラスを指定する方法だと思います。実はこれだけでその画像は 4x4 のスライドパズル化されて表示されます:
<img src="sample.jpg" class="jqPuzzle"/>

2018083005


ちなみに "Original" ボタンをクリックすると完成形が、"Numbers" をクリックすると各ピースの数字の表示/非表示が切り替わります。数字表記がないと難易度は一気に上がります。


カスタマイズの要素を加えることも可能です。例えば以下の例では 4x4 で 16 番目の駒を抜くことは変えずに、ボタンの文字を日本語化し、最初からシャッフル済みになるようにしています(というわけでシャッフルボタンも不要なので非表示にしました):
  :
<script type="text/javascript">
var settings = {
  rows: 4,
  cols: 4,
  hole: 16,
  shuffle: true,
  numbers: true,
  language: 'ja',
  control: {
    shufflePieces: false,
    confirmShuffle: true,
    toggleOriginal: true,
    toggleNumbers: true,
    counter: true,
    timer: true,
    pauseTimer: true
  },
  success: {
    fadeOriginal: false,
    callback: undefined,
    callbackTimeout: 300
  },
  animation: {
    shuffleRounds: 3,
    shuffleSpeed: 800,
    slidingSpeed: 200,
    fadeOriginalSpeed: 600
  },
  style: {
    gridSize: 2,
    overlap: true,
    backgroundOpacity: 0.1
  }
};
var texts = {
  shuffleLabel: 'シャッフル',
  toggleOriginalLabel: '元画像',
  toggleNumbersLabel: '数値表示/非表示',
  confirmShuffleMessage: 'シャッフルしてよろしいですか?',
  movesLabel: '回',
  secondsLabel: '秒'
};

$(function(){
  var t = $('img.jqPuzzle');
  t.jqPuzzle( settings, texts );
});
</script>
 :
2018083006


ちょっとした息抜き機能を追加するのに便利なライブラリです。


EC サイトの商品画像を効率よく表示するテクニックの1つに「カルーセル(carousel、「回転木馬」とか、日本だと「メリーゴーランド」と表現されることも)」と呼ばれる方法があります。スマホなどの限られた画面サイズの中により多くの画面や画像を含ませる技術で、画面・画像を小さくして詰め込むのではなく、個々の画面は横サイズいっぱいに表示させつつ、(一般的には)横スクロールで画面いっぱいの画像を次々に切り替えて表示するテクニックです。

このカルーセルを実装する方法はいくつもありますが、jQuery と組み合わせて使う slick が有名なようでした。というわけで、使い方を調べてみたので以下メモ代わりに残しておきます:
2018021900


CSS と JavaScript の指定
まず slick は jQuery が読み込まれている前提で動くので、最初に jQuery を読み込んでおきます。その後、CSS 、テーマ CSS、そして Slick の JavaScript をロードします。全て CDN を使うとこんな感じになります:
  :
<script type="text/javascript" src="//code.jquery.com/jquery-2.0.3.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css" rel="stylesheet"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>
  :

カルーセルの中身を作成
次にカルーセルの中身になる部分を、以下のような形で作成します(カルーセル全体となる <div> 要素に myclass という名前を付けて、その中に子 <div> 要素の形で各カルーセル内の要素を配置。子要素は HTML でも画像でも可)。

当たり前ですが、このままだと各要素が縦に5つ並ぶだけの UI になります:
  :
<div class="myclass">
 <div>(1番目のカルーセルの中身)</div>
 <div>(2番目のカルーセルの中身)</div>
 <div>(3番目のカルーセルの中身)</div>
 <div>(4番目のカルーセルの中身)</div>
 <div>(5番目のカルーセルの中身)</div>
</div>
  :

カルーセル化
この部分をカルーセル化します。カルーセル全体の div 要素(上記の myclass クラスを指定している部分)に slick() 関数を実行します。実行時のパラメータでカルーセルの挙動をカスタマイズすることも可能です(以下はその例):
<script>
  :
$(function(){
  $('.myclass').slick({
    infinite: false,    //. 端までスクロールしてもループしない
    slidesToShow: 3,    //. 1度に3つ表示
    slidesToScroll: 1,  //. スクロールは1つ単位
    initialSlide: 0,    //. 最初に表示するのは一番最初(上)の要素
    arrows: true,       //. コントロール用の矢印を表示する
    dots: true          //. 全体の位置がわかるような点コントロールを表示する
  })
});
  :
</script>


試しにこのブログエントリ内に同じような要素を作ってみるとこんな感じになりました:
(1番目のカルーセルの中身)
(2番目のカルーセルの中身)
(3番目のカルーセルの中身)
(4番目のカルーセルの中身)
(5番目のカルーセルの中身)


簡単ね~

Node-REDnode-red-node-twitter ノードを使って、ツイッターのリアルタイム検索を行い、その結果を表示するウェブアプリケーションを作ってみました。Node-RED 環境に node-red-node-twitter ノードを追加することで以下の作業が可能になります。或いは IBM Bluemix から Node-RED スターターを使って作成した環境であれば、はじめから同ノードが組み込まれているため利用可能です。

まずは Node-RED のキャンバス内に以下のようにノードを配置します:
balloon_nodered


Twitter のノードにはリアルタイム検索を行うキーワードを指定しておきます。以下の例では "iPhone" というキーワードで Twitter 内をストリーム検索するように指定しています:
2017020501


そして WebSocket 出力ノードでは出力先を /ws/tweets に指定しています。ここは任意の文字列でもいいのですが、後述の HTMLテンプレートの内容が "/ws/tweets" の WebSocket を監視するような内容になっているので、これらの内容を一致させる必要があります:
2017020502


また HTTP 入力ノードでは GET の /tweets を指定しています。つまりウェブブラウザで /tweets というページを参照した時にここで定義するページ内容が表示されるようにしています:
2017020503


その際のページ内容をこちらのテンプレートノードで定義しています。以下のように HTML が指定されており、それがそのまま出力されます:
2017020504


このテンプレートノードの中身は、こちらの template ファイルの内容をそのままコピー&ペーストしてお使いください:
https://github.com/dotnsf/balloon_tweets


※なお、上記で紹介したのとまったく同じノード構成をこちらに用意しておきました。自分でノードを構成しなくても(単に動かしたいという目的だけであれば)この JSON ファイルの内容を Node-RED にインポートして使っていただいてもかまいません:
http://dotnsf.blog.jp/balloon_tweets.json


ノード構成の準備ができたら、Node-RED 画面右上のボタンでデプロイします:
2017020505


デプロイが成功するとここで定義したノードが動き出し、指定したキーワード(今回の場合は "iPhone")で Twitter のリアルタイムツイート検索が行われます。該当するツイートは画面右の debug タブ内に次々と表示されていきます:
2017020506


この様子をもう少し見やすくしたのが HTML テンプレートです。Node-RED と同じホストを指定して、http://(Node-RED のホスト)/tweets をウェブブラウザで開くと、検索されたツイートが画面内に次々と吹き出しを伴って表示されていく様子を確認できます:
2017020500


実際にツイートが次々と追加されていく様子はこちらの動画を御覧ください。"iPhone" くらいに頻度の高いワードで検索すると、こんな感じのスピードでツイートされている、というのが分かる動画になっています:




HTML 内のオブジェクトを移動させるには jQuery の animate メソッド を使うのが一般的(というか簡単)ですが、CSS3 の transition を使ってもできます。

プログラマー/デベロッパーの立場だと jQuery を使うことは珍しくないんですが、CSS はちと遠い存在で、基本は理解しているつもりでしたが、あまり接することがありませんでした。いい機会なので勉強がてら使ってみました。


さて、目的は似てるけどアプローチの異なる2つの手法、その違いはどこにあるのでしょう?

簡単に言えば animate は CPU 処理、transition は GPU 併用の処理になります。PC だと見た目にもあまり差はないかもしれませんが、比較的 CPU 性能の劣るスマホ(それもちと古めの)だと、この差がバッチリでそう。グラフィックチップを併用することで非力なCPU環境でもスムーズなアニメーション処理が実現できます。また見た目に差がなくてもCPUの負荷を軽減することにもなります。


実際に2種類のメソッドで同じアニメーションを実装したのが以下の例です。上の "Animate" をクリックすると jQuery.animate で、下の "Transition" をクリックすると CSS3.transition で、それぞれ重ねあわせスライドスクロール処理が行われます。PC ブラウザだとあまり差を感じないかもしれませんが、非力な環境ほど下の方がスムーズな動きになるはず。



Animate









Transition










ソースコード全文はこのページのソースを参照していただきたいのですが、大まかにはこんな感じになります:

共通の CSS クラス:
<style type="text/css">
.c1{
	position: absolute;
	z-index: 1;
	left: 100px;
	opacity: 1;
	width: 100px;
	height: 100px;
	background: yellow;
}
.c2{
	position: absolute;
	z-index: 0;
	left: 100px;
	opacity: 1;
	width: 100px;
	height: 100px;
	background: red;
}
</style>


jQuery.animate を使った場合の HTML & JavaScript コード:
<a  href="javascript:void(0);" id="aa">Animate</a>
<div  class="c1"></div>
<div  id="c2a" class="c2"></div>
<script type="text/javascript">
var ba = true;
$('#aa').on('click', function(){
	ba = !ba;
	var l = 200;
	if( ba ){ l = 100; }
	$('#c2a').animate(
		{ left: l + 'px' },
		{ duration: '700', easing: "linear" }
	);
});
</script>


CSS3.transition を使った場合の HTML & JavaScript コード:
<a  href="javascript:void(0);" id="at">Animate</a>
<div  class="c1"></div>
<div  id="c2t" class="c2"></div>
<script type="text/javascript">

var bt = true;
$('#at').on('click', function(){
	bt = !bt;
	var l = 200;
	if( bt ){ l = 100; }
	$('#c2t').css({
		left: l + 'px',
		WebkitTransition: 'left 0.7s linear',
		MozTransition: 'left 0.7s linear',
		MsTransition: 'left 0.7s linear',
		OTransition: 'left 0.7s linear',
		transition: 'left 0.7s linear'
	});
});
</script>

どちらのケースもクリック時にトグルのような動きをするのでそのための余分なコードがありますが、jQuery.animate では左座標(left)を指定して 700 ミリ秒でリニアにアニメートを、CSS3.transition でも左座標と効果(transition)を指定して 0.7 秒間で遷移するように css を変えています。違いはこの2箇所です。

もちろん全てのケースで有用というわけではないと思います。またこの例はかなりシンプルな内容なので違いが少なく見えていますが、現実的にはそう簡単にはいかないことはあると思います。ただ後者が使える環境であれば後者にすべきなのかな。



 

このページのトップヘ