どーも、青山です。
もう、9月ですね。
最近、朝晩涼しく、秋ですね。
前回は「WordPressのURLの構造で(標準では)出来ない事」について記事を書かせて
いただきましたが、今回は関係なく、「Kockout.js」についてです。
Knockout.jsについては、最近あだ名が「しゃかまる」に落ち着いた村上君が「過去の記事」で紹介しているので、説明は割愛させていただきます。
しかし、それだとあまりにも、あれなので、Knockout.jsを使おうと思った理由だけ紹介します。
・MVVMモデルでViewのDOM構造に依存せずにロジックを組める
・jQueryのバージョンなど他ライブラリに依存しない
・複数ページで使わず、単一ページで十分
・IE6~8にも対応している
などが主な理由です。
では、タイトルの通り、フォト蔵APIを使って写真一覧を作ってみましょう。
■フォト蔵APIの紹介
http://photozou.jp/basic/api
画像取得のAPIを色々と探してみましたが、当然のように、API Keyの取得のために開発者登録が
必要だったりして、面倒です。
そんな中、フォト蔵の「photo_list_public APIメソッド」は公開されている写真を取得するには特に
API Keyの取得や認証が必要なかったため、使わせていただくことにしました。
■JSONP非対応の壁を乗り越える
しかし、そんなフォト蔵さんのAPIもJSONPには対応していません。
このままだと、別ドメインのJavascriptからはアクセスできないので、困りました。
そこで、下記ブログ記事を参考にさせていただき、Yahoo PipesのAPIを使って
JSONをJSONPに変換して利用することにしました。
「JSONをJSONPに変換するYahoo PipesのAPI」
■動作サンプル
で、knockout.jsを使って作ったものが、下記のものとなります。
「More!」ボタンを押すと、3件ずつ写真を取得してくれます。
読み込み中は、ローディング画像が表示されるようになっています。
(※画像が読み込まれない場合は、Yahoo Pipesでうまく通信ができていない場合があるので「More」ボタンをもう一度押してください。)
■ソース
一応サンプルコードを簡単に紹介していきましょう。
▼ライブラリの読み込み
jQueryとkockout.jsを読み込みます。jQueryのバージョンは、このブログが1.4.4なだけで、特に指定はありません。
1 2 |
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script> <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script> |
▼API通信とViewModel
フォト蔵APIの通信とViewModelになります。
Photozoの部分では、主にフォト蔵のAPIのURLをエンコードして、Yahoo PipesのAPIの引数として渡したり、
API通信部分の定義をしています。
viewModelでは次のページを読み込めるか(isMore)、ロード中か(isLoading)判定する関数を定義しています。
見てもらえば、わかりますが、ViewのHTML要素に依存しない内容となっています。
そのため、ViewのHTMLの内容が変わっても影響がありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<script type="text/javascript"> jQuery(function(){ var Photozo = (function () { var self = this; this.settings = { "limit": 3, "ajax": { "type": "GET", "dataType": "JSONP" }, "query" : { "_render": "json", "jsonp": "_callback" }, "pipes" : "http://pipes.yahoo.com/pipes/pipe.run?_id=332d9216d8910ba39e6c2577fd321a6a&u=", "api" : "https://api.photozou.jp/rest/photo_list_public.json?type=everyone&offset=" }; this.getApiUrl = function (offset) { var time = (function(){var dt=new Date(); return (dt.getMonth()+1)+""+dt.getDate()+""+dt.getHours()+""+dt.getMinutes()+""+dt.getSeconds();})(); return self.settings.pipes + (encodeURIComponent(self.settings.api + offset + '&limit=' + self.settings.limit + '&time=' + time)); }; this.getData = function (jQuery, offset, successAction ) { var ajax = self.settings.ajax; ajax.url = self.getApiUrl(offset); ajax.data = self.settings.query; ajax.success = successAction; jQuery.ajax(ajax); }; this.cutStr = function(text, cutLen){ if (! text) return ''; return (text.length > cutLen) ? text.substr(0, cutLen) + '…' : text ; }; }), viewModel = new (function () { var self = this; this.photozo = new Photozo(); this.jsPhotos = ko.observableArray([]); this.isLoading = ko.observable(false); this.isMore = ko.observable(true); this.currentOffset = ko.observable(0); this.disableButton = ko.observable(false); this.get = function () { if (!self.isMore() || self.isLoading()) return ''; self.photozo.getData(jQuery, self.currentOffset(), function(result){ self.isLoading(true); var obj = (new Function("return " + result))(), data = null; setTimeout(function(){ data = obj.value.items[0]; if (obj.count > 0 && data.stat == 'ok' && self.isMore() && data.info.photo.length > 0) { ko.utils.arrayPushAll(self.jsPhotos, data.info.photo); self.isLoading(false); self.isMore(data.info.photo_num == self.photozo.settings.limit); self.currentOffset( self.currentOffset() + self.photozo.settings.limit); } else if( obj.count == 0) { if (self.isLoading()) self.isLoading(false); } }, 5000); }); }; this.get(); })(); ko.applyBindings(viewModel, document.getElementById("image-gallery")); }); </script> |
▼View(HTML)
「data-bind」という属性(アトリビュート)でViewModelとViewを結び付けています。
View側でも指定できるようにしているので、画像のキャプション(title)を14文字でカットするようにしていたりします。
(「$root.photozo.cutStr(photo_title,14)」の部分)
1 2 3 4 5 6 7 8 9 |
<div id="image-gallery"> <ul data-bind="foreach: jsPhotos"> <li><a data-bind="attr: {href: thumbnail_image_url, title: $root.photozo.cutStr(photo_title,14)}"><img data-bind="attr: {src: thumbnail_image_url, alt: photo_title}"></a></li> </ul> <div class="loader"> <div style="text-align: center;" data-bind="visible: !isLoading() && isMore()"><input style="margin-top:50px;" type="button" value=" M o r e ! " data-bind="click: get"/></div> <div style="text-align: center;" data-bind="visible: isLoading()"><img src="http://tech.aainc.co.jp/wordpress/wp-content/uploads/2013/09/gif-load.gif"></div> </div> </div> |
▼CSS
画像をただ並べるだけだとさびしいので、CSS3でちょっとあしらいをしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<style type="text/css"> @font-face { font-family: 'mplus-1p-regular'; src: url('http://mplus-fonts.sourceforge.jp/webfonts/mplus-1p-regular.ttf') format("truetype"); } div#image-gallery a img { border: none; } div#image-gallery ul { list-style-type: none; margin: 0 auto; padding: 0; width: 90%; } div#image-gallery ul li a { position: relative; float: left; padding: 10px 10px 20px; background-color: white; -moz-box-shadow: 0 4px 10px #333; -webkit-box-shadow: 0 4px 10px #333; box-shadow: 0 4px 10px #333; -webkit-transform: rotate(5deg); -moz-transform: rotate(5deg); transform: rotate(5deg); width: 175px; text-decoration: none; text-align: center; } div#image-gallery ul li:nth-child(2n+1) a { -webkit-transform: rotate(-10deg); -moz-transform: rotate(-10deg); transform: rotate(-10deg); position: relative; top: -5px; } div#image-gallery ul li:nth-child(3) a { -webkit-transform: rotate(30deg); -moz-transform: rotate(30deg); transform: rotate(30deg); position: relative; top: -10px; left: 30px; } div#image-gallery ul li:nth-child(4) a { position: relative; top: -10px; left: 10px; } div#image-gallery ul li a img { width: 175px; height: 175px; margin-bottom: 10px; } div#image-gallery ul a:after { content: attr(title); font-family: 'mplus-1p-regular', sans-serif; font-size: 70%; color: #333; } div#image-gallery ul li a:hover { -webkit-transform: scale(1.25); -moz-transform: scale(1.25); transform: scale(1.25); z-index: 6; } div#image-gallery .loader { clear: both; width: 100%; } </style> |
ひじょーにざっくりですが、こんな感じです。(ソースは貼り付ければ、ローカルでも動くようにしています。)
どこまでをどう説明するか考えているうちに、面倒になってほとんど解説がありませんが、
ありがたいことに、knockout.jsの公式ドキュメントを日本語に翻訳して下さっているサイトがあります。
■knockout.jsの日本語ドキュメント
http://kojs.sukobuto.com/
今回紹介したサンプルは、knockout.jsでできることのほんの一部でしかありません。
興味を持たれた方は、いろいろ試してみるといいのではないでしょうか。
■最後に
アライドアーキテクツでは現在、エンジニアを募集中です。
興味を持っていただけたら、↑の採用サイトからご応募ください。
受託案件を担当しています。 Javascript, Wordpressなどテーマはその時々で変わりますが、役に立つ情報を提供できればと思います。