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

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

タグ:jquery

マンホールマップでも使っている、 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番目のカルーセルの中身)


簡単ね~

jQuery を使って HTML の iframe の内側コンテンツを、iframe の外側の JavaScript からスクロールさせる方法を紹介します。

今回は2つの HTML ファイル(main.html と sub.html)を使います。main.html が iframe の外側(というか、iframe を記述する側)のファイルで、main.html の中の iframe に sub.html が指定されて埋め込まれて表示される、という関係だと想定します。sub.html はどんな内容でも構いませんが、強制スクロールさせて表示することを想定しているので、それなりに縦長の、普通に見ても縦スクロールさせないと全体が見えないくらいの大きさのページを使ってください:
2017113001

一方、iframe の外側である main.html が今回の主役で、main.html 内の JavaScript で sub.html をスクロールさせて表示させることが今回の目的です。実現するためには以下のような内容で記述します:
<html>
<head>
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script>
$(function(){
  $('#sub').load( function(){
    iframe_scroll( 100 );
  });
});

function iframe_scroll( top ){
  //. 「0.8 秒で 100 ピクセルスクロールし、0.2 秒待つ」を繰り返す
  $('body,html', $('#sub').contents() ).animate( {scrollTop: top}, 800, 'swing' );
  setTimetout( iframe_scroll, 1000, top + 100 );
}
</script>
<title>main</title>
</head>
<body>
<h1>main</h1>
<hr/>
<iframe id="sub" src="./sub.html" width="90%" height="50%"></iframe>
</body>
</html>

jQuery を使って、まず sub.html がロードされた直後に( $('#sub').load() 関数内で)iframe_scroll 関数を実行しています。この関数では iframe 要素のコンテンツを取り出し、animate 関数を使ってスクロールさせています。この例では 0.8 秒かけて 100 ピクセル下にスクロールし、0.2 秒(1000 ミリ秒 - 800 ミリ秒)止まって繰り返す、という例を実装しています。

なお、sub.html をローカルで用意する代わりに外部コンテンツ(http://www.xxx.com/xxxxx.html など)を指定した場合、iframe 内に表示はされますが、クロスドメイン制約にかかって iframe 内の要素にアクセスすることができず、スクロールは行われません。ご注意ください。


jQuery を使うと、スクロールイベントを簡単に取得したり、スクロールイベントに対するハンドリング処理を実現できます:
$(window).scroll( function(){
  var scroll_top = $(this).scrollTop();  //. スクロール位置
    :
  (スクロール時の処理)
  console.log( 'top = ' + top );
    :
});

ただ、少し取扱が難しい面もあります。上記の内容のままだとスクロールを感知する毎にイベントが発生し、その全てをハンドリングすることになります。要するに一回のスクロールの中で何度もこの処理を繰り返すことになります。

これを「スクロールが一段落したらハンドリング処理を行う」ように改良してみます。考え方として一定時間(以下の例では 200 ミリ秒)スクロールイベントが発生しなかったらハンドリングする、という考え方で擬似的に実装してみました:
var timer = false;
$(window).scroll( function(){
  if( timer !== false ){
    clearTimeout( timer );
  }
  timer = setTimeout( function(){
    var scroll_top = $(this).scrollTop();  //. スクロール位置
      :
    (スクロール時の処理)
    console.log( 'top = ' + top );
      :
  }, 200 );
});

青字が追加した部分です。考え方としてはスクロール発生時に 200 ミリ秒のタイマーを設定し、200 ミリ秒以内に同じイベントが発生したらタイマーを再度設定し直しています。そして200ミリ秒間同一のイベントが発生しなかった場合にハンドリング処理を行う、というアルゴリズムです。

これで擬似的にスクロールエンドのイベントに対するハンドリング処理を実現できました。

(参考)
http://www.web-labo.jp/archives/963

例えば HTML (の一部)など、複数行に渡るようなテキストファイルの中身をまとめて1つの文字列変数内に格納したい場合にヒアドキュメントと呼ばれる構文を使うのが便利です。例として PHP であればこんな感じで定義できます:
<?php
$head = <<< EOT
<head>
<title>テスト</title>
<script>
  :
</script>
</head>
EOT;
?>

この例の場合は <head> から </head> までの6行部分がまとめて文字列変数として $head に代入できます。

このヒアドキュメント、残念ながら JavaScript ではサポートされていません。が、ヒアドキュメントもどきのようなことはできます。具体的には jQuery を使ってこんな感じ:
var head = (function(){/*
<head>
<title>テスト</title>
<script>
  :
</script>
</head>
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];

この方法で同じ内容が文字列変数として head に代入できます。JavaScript 上でコメント扱いとなってる箇所(/* ~ */)の "/*" と "*/" で挟まれた部分を match 関数で取り出して代入する、というアイデア賞ものの方法です。

テンプレート的な HTML を JavaScript 内に直書きして扱いたい場合に便利です。



(参考文献)
http://qiita.com/_shimizu/items/837b529de9f3302e315c



 

このページのトップヘ