こんにちは。ピグワールドでFlashデベロッパーを担当している金原です。
今回は2012年の11月下旬にリリースされたピグワールドと、その開発体制などをご紹介させて頂きます。

ピグワールドについて

ピグワールドピグライフピグアイランドピグカフェに続く、第4のゲームとしてリリースしたピグの街ゲームです。
住宅やショップなどを建て、ピグともを住民にして自分好みの街を作ることができます。

ワールド画像3


ピグワールドの楽しみ方

ピグワールドは街の人口を増やし、街を発展させることが目的になります。
街の人口を増やすことで、新しい建築物が建てられるようになり、より自分好みの街にすることができます。


住民に挨拶したり、住民のお悩みを解決してゲットできるスマイルポイントを使い、家をレベルアップさせて人口を増やす事もできます。


ゲームを進めて、人口を増やせば上のような素敵な街ができます。
この他にも住民に服をプレゼントして、着せ替えしたり、デコレーションで街を彩ることもできます。皆さんもぜひピグワールドで自分好みの街を作ってみてください。

ピグワールドの開発体制

ここからは少しだけピグワールドの開発体制とリリース前とリリース後のお話をしたいと思います。

現在のピグワールドの開発はデザイナー2名、デベロッパー3名、エンジニア4名のメンバーで行っています。
また、その他にもプロデューサー、プランナー、イラストレーター、CSが在籍しています。

開発は基本的に以下の流れで進めます。
仕様決め ➡ デザインと設計 ➡ 実装 ➡ テスト ➡ リリース

[仕様決め]
仕様を決めるため担当者でミーティングを行います。
そこではデザイン案、機能の仕様、機能の実現の可否などを話し合います。
ミーティングでは職種は関係なくアイデアや意見を出します。

[デザインと設計]
仕様をもとにデザイナーはデザインを行い、デベロッパーとエンジニアは設計を行います。
ここで、不明な点があれば担当者で話し合いを行います。

[実装]
仕様とデザインをもとにデベロッパーとエンジニアで実装を行います。
ある程度完成したら、デベロッパーとエンジニアで繋ぎ込みを行います。

[テスト]
実装が終わったら、今度はテスト環境で問題がないかをチェックします。
ここで不具合があれば、修正を行い、再度テストを行います。

[リリース]
不具合がないことを確認し、リリースを行います。
また、リリース後も不具合がないか?ユーザーの反応はどうか?等を確認し対応を行います。

これが基本的な開発の流れになり、期間は大体1週間~2ヶ月になります。

リリース前

リリース前はゲームの基盤や基本的な機能の開発を進めます。
同時にデザインをチェックするためのツールや、NPCの住民をつくるツールなども制作します。

そして、β版が完成したら一部のユーザーにテストプレイしてもらい、意見を頂きます。
その後、意見を参考に機能の追加や改善を行い、ゲームを仕上げていきます。
もちろん、自分たちで実際にプレイして改善点を見つけたりもします。

以下はβ版の意見を参考に改善した一部です。
・エリアが広く建物の描画に時間がかかるため、描画方法を改善
・人口の増加によって配置できる建築カタログを追加
・建物の上にでるアイコン、住民のおしゃべりのOn/Off機能の追加
・道路の上にデコレーションの配置をできるようにする
・もっと使い易いようにUIの変更

上記以外にも大きい仕様変更から小さな改修まで多くの実装を行いました。

リリース後

リリース後はイベントの運用と共に、新機能の追加や改善などを行います。
リリース前とは異なり、既にユーザーが遊んでいるため不具合を出す事は避けなければなりません。

以下はリリース後2ヶ月でリリースした機能の一部です。
・ユーザー同士でギフトを交換できる機能
・住民がプレゼントを持って来てくれる機能
・新しいログインボーナス機能
・お気に入り機能

このようにユーザーが飽きないための施策や機能改善を行っています。

また、ピグワールドでは自分たちで改善案を出す場を設け、ユーザーにもっと楽しんでもらえるように改善しています。

最後に

リリース前もリリース後もやることは沢山ありますが、ピグワールドを長く遊んで頂けるように
メンバー一丸となって今後も開発を行っていますので、今後のピグワールドにご期待ください。

ピグワールド
http://world.pigg.ameba.jp/

はじめまして、こんにちは。
アメーバ事業本部ゲーム部門コア室という所で、ディベロッパーをしております、佐藤圭明と申します。

前回の「ガールフレンド(仮)」の秋山さんのお話につづき、アメーバ内製タイトルのヒット作である、「天下統一クロニクル(※以下「天クロ」と表記)を元にお話しさせて頂きたいと思います。

「天クロ」では、アニメーションの制作にFlash CS6とToolkit For CreateJS(※以下、「TFC」と表記)を使用しており、ゲーム内のイベントに合わせて月に3、4本くらい作っているイメージです。また、その他の内製タイトルでは、「天空のクリスタリア」「不良魂 ~全国制覇~ 」「ガールフレンド(仮)」「ぼくらのブレイブガーディアン」でもTFCを使用しております。

ゲーム自体は2012年の4月末から開発がスタートし、9月の頭にリリースとなりました。開発時からリリース後の運用に至るまで、アニメーションについても様々な問題と直面しながら、解決に至ってきたわけですが、そんな中でみつけた具体的なお話を今回はさせて頂ければと思います。

アニメーションの制作フローについて

まずは、イベントのオープニングアニメーションの制作フローを元に、具体的な作業を説明したいと思います。
フロー図
※「マージ」とは、出来上がったHTMLやJSファイルをシステムと結合する作業の事です。
この工程の中で、私が担当するのは3から5の部分となります。もちろん、1のブレストにも参加して、実現可能な表現かどうかという話もしています。

それでは、担当する工程の詳細をお話ししていきます。


受け取ったデータを元にスプライト画像を作成

使用する画像は、PNG中心で解像度も高いため、重いデータになりがちなのですが、スマフォの回線はまだまだ貧弱です。アニメーションでは3G回線を考慮して、devicePixelRatio=1の画像を使用し、devicePixelRatioが1.5や2の実機上では拡大されたものが再生されております。

また、リクエスト数を減らすために、スプライト画像を使うのはごく当たり前の事ですので、受け取ったPSDを元に透過有り(png)と透過無し(jpg)の2枚のスプライト画像を作成して、ページ全体のリクエスト数を10本程度まで抑えております。

透過有りスプライトスプライト画像が完成したら、透過有りの画像をデザイナーさんに減色処理してもらいます。これにより容量が3分の1ほどまで減り、見た目的にはそれほど劣化しません。その後、ImageOptimなどで不要なメタデータの削除なども行い、出来るだけ計量化します。


Flash上でオーサリングしてアニメーションを作成

ここのフローは通常のFlashでのアニメーション作成手順とほぼ同様ですが、いくつか気を付ける点があります。

●スプライト画像から各パーツを作成
素材は1枚、もしくは2枚の画像に入っているので、これらの画像を配置したMovieClipを作成し、必要な部分だけが表示されるようにマスクを追加して行きます。
※Flash CS6には、スプライトシートの書き出し機能もあるので、もっと楽なフローが作れそうです。

マスクでスプライト


●動くパーツはMovieClipで作成する
swf作成時と同様、動かすものはMovieClipで作成します。グラフィックシンボルを使って動かした場合は、tweenオブジェクトが追加されるため、書き出されるコードが若干増えます。動かすオブジェクトが増えれば増えるほど、一緒に増えてしまうので、必ずMovieClipで作成しましょう。

ライブラリ

●ラベル名に気を付ける
swfでの書き出しを想定した場合は、あまり気にしなくて良かった事ですが、TFCで書き出す場合、ラベル名はそのままObjectのkeyとして使われます。

うっかりJSの予約語を使っていて、一度問題が出てしまった事がありますので、分かりやすい名前にする事はもちろん、このコードが関わるあらゆる仕組みの中で問題が出ないよう、気つけて命名しましょう。

//MovieClipの初期化
this.initialize(mode,startPosition,loop,{tfcWLStart:0,tfcWLLoop:3},true);
※フレームラベルはinitializeの第4引数に{ラベル名:フレーム番号}の形で渡されます。


●透明ボタンは完全な透明にはしない
swfで書き出す場合は問題無いのですが、アルファが0のオブジェクトはcanvas上では存在しないものになってしまうので、1%とかに設定して完全に消えないようにする必要があります。


出力したものをサーバサイドとマージして完成

「天クロ」は、サーバサイドはJavaで作られており、FreeMarker(.ftl)というテンプレートエンジンを使っています。なので、TFCから書き出されたコードのHTMLの部分は、この.ftlファイルに埋め込む必要があります。実際には、サービスのimageファイルを置く場所を参照するように画像URLを書き換える、アニメーションをタップした際の遷移先を定義する、などの作業になります。

1つ、これ以外で重要な事で、setMaxConnections()の記述を追加する必要があります。これは、preloadJSがデフォルトで1本ずつしか画像リクエストを投げないため、ローディングに時間がかかってしまうのを解消するためです。一方で、端末側の挙動を見ていると、ブラウザ側で持っている最大接続数もあるようで、リリース後から検証を兼ねて1本ずつ増やし、現在は5を設定しています。

//同時接続数の変更
   var loader = new createjs.PreloadJS(false);
   loader.onFileLoad = handleFileLoad;
   loader.setMaxConnections(5);//この行を追加
   loader.onComplete = handleComplete;
   loader.loadManifest(manifest);
※initializeの第四引数に{ラベル名:フレーム番号}の形で渡されます。

以上のような手順で、イベント用のアニメーションが完成するわけですが、3から5までの工程で、大体1.5日から2日くらいで作業しております。


Androidが落ちる、止まる、ブラックアウトする、などの問題

主にはGALAXY S3で発生した問題です。シェア率の高い端末ですので、対応出来ないとかなりのインパクトとなってしまうため、ノウハウを蓄積するまでは、かなり苦労させられました。そんな中、みつけたいくつかの方法をご紹介させて頂きますので、同様の問題に遭遇したら試して頂ければと思います。

●canvasの領域外に描画しようとするとブラックアウト
canvas領域よりはみ出てしまう画像だと、canvas外に描画しようとする事があり、この場合うまく再生されずに、ブラックアウトする事があり、画面がチラついているように見える問題です。1.1から追加されたマスク機能により、canvasサイズと同サイズのシェイプでマスクするという手法で回避する事が出来ました。

●canvasアニメーションを再生しようとするとほぼ同じタイミングでブラウザが落ちる
Xperia Zという人気端末での事象です。TFCを使っている全てのゲーム、全てのアニメーションがこの問題で再生不可となってしまいました。
これは、canvasと同じサイズでclearRect()しようとするとブラウザが落ちる。というバグだったらしく、0,0座標からcanvas.widthとcanvas.heightでclearRectする処理は全てこれ以上の領域を消すように処理を変えました。結果、問題無く再生されるようになっただけでなく、S3で発生していた問題も解消され、ステージ同様のサイズでマスクする。というハックも不要になりました。TFCを使っていなくても、canvasでこの処理を行っている場合は、発生すると思います。

ちなみにこういった解決策は、社内のディベロッパー達が積極的に情報交換する事で解決されたものがほとんどであり、もし一人で開発していたらスマフォブラウザ向けサービスの開発は困難を極めることでしょう。

最後に、スマフォブラウザ向け開発について

スマートフォンブラウザ向けの開発は端末のバグや、キャリアの回線速度だったり、ネイティブアプリとのクオリティ比較だったりと、色々と苦しめられる点も多いです。そんな中でも、ベストプラクティスがなんなのか、まだ明確に定まっていない時期に、こういった仕事が出来る事はとても貴重な体験であると考えています。そんな中で、この1年間ゲーム開発を行ってきて、「技術の適材適所」を考える事が特に大事だなと、痛感いたしました。

「技術の適材適所」とは、その実装をJSでやるべきなのか、CSSでやるべきなのか、フロントエンドでやるべきなのか、サーバサイドでやるべきなのか、きっちりと考えて的確に判断する事です。これを怠ると、重すぎて動かない。動くんだけど、ブラウザが良く落ちる。電池の消耗が激しく、端末が熱くなって辛い、などなど、様々な問題が発生します。こういった問題は一つ一つが小さく見えても、積もりに積もって、いつか思わぬ問題を生むものです。

アニメーションをFlashで作っているのも、良い動きをするアニメーションをトライ&エラーしながら作るには、現状では最も良いプロセスであると考えているからです。Flashを使う事でリリースまでの工数が減らせていますし、問題が出た時の対応も即座に行えるし、何より良い動きを付ける事が出来ます。

まだまだ先の趨勢が見えないスマフォブラウザのベストプラクティスですが、常に一番良い方法が何なのか考えて正しい判断が出来るよう努力を続ける事は、作るプロとしての義務だと考えていますので、引き続き良い方法を追求していきたいと思います。



はじめまして。こんにちわ。
「ガールフレンド(仮)」でフロントエンドの開発をしている秋山と申します。

「ガールフレンド(仮)」とは、ユーザーが主人公となり、様々な女の子と出会っていく“声が出る♪”学園恋愛カードゲームです。各カードには、人気声優のボイスが付いていて、さまざまな演出に合わせてキャラクターの声を聞くことができます。

今回は、ガールフレンド(仮)に声が入るまでの苦労話とあわせてどう解決(実装)したかなどご紹介できたら思います。

スマホのブラウザ版カードゲーム × 声

ガールフレンド(仮)の開発に入る時点で、スマホブラウザゲームで音声を出しているサービスは他にもありましたが、カードに声が付いているものは少なかったかと思います。
また、スマートフォンのブラウザで音声を再生するには色々と制限や、OS・端末によって実装が異なるため気をつけなくてはいけない点も多く、ガールフレンド(仮)に声が入るまではなかなか苦労する部分も多かったです。

ガールフレンド(仮)は、アニメーション内で声を流すことが多いので、開発初期はAndroidの各OSと無数にある端末をカバーするためにFlashによる実装をしていました。
しかし!昨夏のFlashPlayer騒動後にはFlashからの脱却を迫られ、両OSともにJavaScriptでAudioオブジェクトを制御する実装に変更した経緯があります。

まずはそのAudioオブジェクトについて簡単に

audio要素は、HTML5から新たに追加された音声を再生するための要素です。audioは、HTML5の<audio>タグで直接記述したり、JavaScriptから生成・再生を制御することができます。ガールフレンド(仮)では、すべてJavaScriptで制御しています。

スマートフォンブラウザ × Audio

ガールフレンド(仮)は、スマートフォン専用のサービスなのですが、スマートフォン上でAudioを実装するには、OSはもとより端末ごとに挙動が違ったりします。
ここではその一部を紹介します。

対応OSと音声ファイルについて

ガールフレンド(仮)では、各OSに最適化するために下記のファイル形式を採用しています。
ただ、管理・運用コストを考えるとmp3に統一してしまったほうが良かったかなと感じています。

iPhone/iOS4以降/caf/モノラル ビットレート48kHz
Android/2.3以降/mp3/モノラル ビットレート48kHz

ちょっと話しは逸れますが、私自身開発時に知ったことですが、mp3を商用で利用する際にはライセンス料が発生しますのでご注意ください。

iPhone(iOS)は音声ファイルの先読みができない

通常、下記のように記述すれば音声ファイルの読み込みが期待できます。

// 初期設定
var voice = new Audio();
voice.src = 'sample.mp3';
voice.load();
しかし、iPhoneはタップなどのユーザーアクションをトリガーにしないと音声ファイルの読み込みができないので(ガラケーみたいですね)、下記のように音声ボタンを押したときにreadyStateプロパティから再生可能かを判定する必要があります。

// 初期設定
var voice = new Audio();
voice.src = 'sample.mp3';
voice.load(); // Androidは先読みできるので記述しておく

// 音声ボタンの処理
voiceBtn.addEventListener('touchstart', function(e) {
    // 再生可能状態か判定
    if(voice.readyState === 4) {
        // ボタンを再生中に切り替える
        gf.audio.showPlaying();
        // 再生
        voice.play();
    } else {
        // ボタンをローディングに切り替える
        gf.audio.showLoading();
        // 音声ファイルの読み込みを開始
        voice.load();
        // 再生可能になった時に処理
        voice.addEventListener("canplay", function(e) {
            voice.removeEventListener("canplay", arguments.callee);
            gf.audio.showPlaying();
            voice.play();
        });
    }}, false);
しばしば「ユーザーアクションによってしか再生できない」と書いている方がいますが、実際には読み込みさえ完了していれば、こちらの意図したタイミングで再生することができます。

currentTime設定に注意

同一の音声ファイルを繰り返し再生することも多いと思います。その際は、現在の再生位置を設定・取得するプロパティcurrentTimeを0にすることで、再生位置を音声ファイルの0秒に設定します。

しかし、ここでも注意!

下記のようにplay()直前で設定すると、iPhoneでは再生を開始してくれません。PCやAndroid端末では再生されます。

voice.currentTime = 0;
voice.play();

これを回避するためには、再生終了時のendedイベントが発生時にcurrentTimeを0にする必要があります。

// 再生終了の処理を設定
voice.addEventListener('ended', gf.audio.ended, false);

// 再生終了時の処理
gf.audio.ended = function() {
    // 再生位置を0秒に戻す
    currentTime = 0;
    // 音声ボタンを停止中に戻す
    gf.audio.showStopping();
};

また、Glaxy S3では、再生完了イベント(endedイベント)発生後にcurrentTimeの設定ができなくなり、複数回再生することができません。

うっかり再生は絶対NG

これが一番気を使った点です。スマートフォンにはマナーモードの設定がありますが、スマートフォンブラウザからは端末の音声ミュートボタンの状態を取得することができません。
つまり、ユーザーがマナーモードにしていると思っていても、問答無用に声が出てしまいます。
スマートフォンサービスの特性上、ユーザーは電車の中などの外出先で使用することも多いので、ユーザーが意図せず声が出てしまうのを防ぐ必要があります。

そこでガールフレンド(仮)では、下記の2点を実装しました。

ミュートボタンを設置

Cookieを使用したミュートボタンを各ページに設置し、ユーザーが任意で設定できるようにしています。
このボタンがオフになっている場合は、アラートを表示するようにしています。


アニメーションで再生前にアラートを表示

フルアニメーションでは、上にミュートボタンは設置できないので、声が出る場合はアニメーション開始前に下記のようなアラートを表示しています。
また、このアラートでタップさせることで、前述のiPhoneの読み込み問題も回避しています。


あれ?声でないよ?

うっかり再生対策もし、開発もだいぶ進んだ頃、開発陣以外の人にもテストプレイをしてもらった時に「あれ?声でなくない?」という言う人が現れました。

原因は、端末のメディア音量が0になっていたためでした。

スマートフォンには、ボタン操作音などのシステム音量と音楽を聴いてるときなどのメディア音量など、複数の音量調節があります。

・ブラウザでページを閲覧中はシステム音量
・ブラウザで音声再生中はメディア音量

つまり、いくら声が流れる前に音量を上げても、メディア音量が0になっていたら声が聞けないということです。
こちらの問題には、チュートリアルで声を聞ける部分の再生前に、「音声が聞こえない場合は、再生中に端末の音量UPボタンを押してください。」というアラートを表示するようにしました。

最後に

今回は実装における苦労話などを書かせてもらいましたが、本当に苦労したのは実際の“声”を集めるところだったと思います。
多くの方々にご協力いただいたおかげで、総勢60名以上の豪華声優の方々に参加いただくことができました。

キャラクター(カードイラスト)と声、それらを活かすゲーム性や演出などが上手く混じり合ったときにユーザーに喜んでもらえるサービスになるのかな、とあらためて実感しています。

最後まで読んでいただきありがとうございました!
今回の記事で少しでも興味持たれた方は、ぜひガールフレンド(仮)で遊んでみてください。



はじめまして、
Ameba事業本部 teens事業部でフロントエンドディベロッパーをしている
2012年入社の山内と申します。

業務では、スマホ限定デコれるホムペ!Candyと新規アプリのHTML/CSSでの運用と開発を担当しています。

今回は、最近のSNSアプリでよく見ることが出来る、いいねボタンなどのアニメーションについて、CSS3アニメーションを用いて心地良い動きを実現していく方法を紹介します。

技術としては目新しくないかもしれませんが、
非常に短い時間の中でアニメーションのストーリーを組み立てたり、アニメーションを考える上で少しでも参考になればと思います。

ポヨン!プルン!という動き

今回は下記のイメージのような2つの動きについて書いていきます。

1. ポヨンとオブジェクトが正面方向に拡大して縮小するまでの動き


2. プルンとオブジェクトがバネのように震える動き


この2つのアニメーションを作る上で意識しなければならないことは、バネのように加速度が付いた動きを如何に実現するかという所にあります。初速が速くてだんだんとゆっくりになったり、初速が遅くて徐々に速くなるなどそのような動きをCSS3アニメーションを用いて制作していきます。

1. ポヨンとオブジェクトが正面方向に拡大して縮小するまでの動き

1つめの動きについて見ていきます。
このアニメーションのストーリーを見ていくと、

アニメーションエリアをタップ(またはクリック)すると、
アニメーションさせるオブジェクトが勢い良く拡大し、
その後、バウンスしながら縮小拡大し、
本来のアニメーションエリアと同じサイズになって終了するというストーリーです。

1. バウンスの表現
2. モーションのスピードとサイズ

が重要となります。

これらの事を踏まえて記述したCSSのアニメーション部分のみを抜き出すと以下のようになります。(記載したCSSコードでは、-webkit-, -moz-などのベンダープレフィックスは省略させていただきます。)

.anim-symbol {
  background-image: url("anim-symbol-on.png");
  background-size: 27px 22px;
  animation: anim-scale 0.5s ease-out;
  transform-origin: 50% 50%;
}

@keyframes anim-scale {
  0% { transform: scale(0.4, 0.4); }
  40% { transform: scale(1.2, 1.2); }
  60% { transform: scale(1, 1); }
  80% { transform: scale(1.1, 1.1); }
  100% { transform: scale(1, 1); }
}

ここでのポイントは@Keyframesの設定です。

順番にまずanimationプロパティを見ていきます。

0.5秒という短い時間の中でアニメーションさせ、ease-outでアニメーションがゆっくりと終了するように設定します。また、アニメーションさせるオブジェクトの軸を、transform-origin: 50% 50%;でオブジェクトの中央に設定します。

animation: anim-scale 0.5s ease-out;
transform-origin: 50% 50%;

次に、ポイントでもある@keyframes内の具体的なアニメーションの内容を見ていきます。

ここではオブジェクトを拡大縮小させてバウンスしているようなアニメーションを作るため、
transform: scale( ); を用いて拡大縮小のサイズを記述していきます。

まず、0%から40%の間で最初の勢い良く拡大する部分を記述します。
拡大したら40%から60%の間で縮小し、
縮小したら60%から80%の間で再度拡大、
最後に80%から100%の間で縮小して、
オブジェクトがアニメーションエリアと同じサイズになって定着し終了となります。

@keyframes anim-scale {
  0% { transform: scale(0.4, 0.4); }
  40% { transform: scale(1.2, 1.2); }
  60% { transform: scale(1, 1); }
  80% { transform: scale(1.1, 1.1); }
  100% { transform: scale(1, 1); }
}

このように0.5秒という短い時間の中で、
@keyframes内に4つの細かな動きを記述することで、
拡大縮小するバウンスの動きを実現しています。

2. プルンとオブジェクトがバネのように震える動き

さて、先程は正面方向にバウンスする動きを見てきましたが、
次はバネのように上下に震えるような動きについて見ていきます。
CSSのアニメーション部分は以下のようになります。

.anim-balloon {
  animation: anim-rotate 2s ease-out 2s infinite;
  transform-origin: 0 40%;
}

@keyframes anim-rotate {
  0% { transform: rotate(0deg); }
  10% { transform: rotate(-6deg); }
  20% { transform: rotate(7deg); }
  35% { transform: rotate(-3deg); }
  50% { transform: rotate(3deg); }
  65% { transform: rotate(-1deg); }
  80% { transform: rotate(1deg); }
  95% { transform: rotate(0deg); }
  100% { transform: rotate(0deg); }
}

先ほどのアニメーションとの違いは、
animationプロパティ内でinfiniteを指定し、繰り返しアニメーションするようにしている点と、
アニメーションさせるオブジェクトの軸を左上から、下方に40%ずらしている部分となります。

animation: anim-rotate 6s ease-out 2s infinite;
transform-origin: 0 40%;

@keyframes内をでは、上下方向にプルンとオブジェクトを震わせるため、
transform: rotate( ); を用いてオブジェクトを回転させる角度を調整します。

最初はプラス方向とマイナス方向に振れ幅を大きくし、
徐々にその値を小さくして、最終的に値を0にすることで、
加速度が付いたような動きを表現します。

また、ジワジワと動きが小さくなる部分を表現するため、0% ~ 100%間のアニメーションが進行していくタイミングを細かに調整してリアルな動きを表現しています。

.@keyframes anim-rotate {
  0% { transform: rotate(0deg); }
  10% { transform: rotate(-6deg); }
  20% { transform: rotate(7deg); }
  35% { transform: rotate(-3deg); }
  50% { transform: rotate(3deg); }
  65% { transform: rotate(-1deg); }
  80% { transform: rotate(1deg); }
  95% { transform: rotate(0deg); }
  100% { transform: rotate(0deg); }
}

このようなanimationプロパティと@Keyframesのセットを複数組み合わせることによって、
よりバリエーションの高いモーションを作ることが可能です。

ストーリーと動きの観察

ここまではCSSを用いて細かなアニメーションを記述する際の手法について書いて来ました。

しかし、先に紹介したようなCSSを記述するためには、
「オブジェクトをどのようにアニメーションさせるか?」
「それが何秒でアニメーションするのか?」
「最終的にどのように終了させるか?」
などアニメーションのストーリーをしっかりと組み立てることが重要です。

また、「バウンス」や「拡大縮小」「不透明度」の設定など、
よりリアルなアニメーションを実現するためには、
現実世界の物体の動きをよく観察して、
それに「如何に近づけていくか、どこまでマネできるか」ということが重要となってきます。

私たち人間は生活の中で、
視覚的に何が心地良くて何に違和感を感じるのかを、
暗黙の内にしっかりと認識しています(個人差はあります)。

PCやスマートフォンなどのデバイス上で、ユーザーに良質な体験を与えていくためにも、現実世界の動きの細かな部分をよく観察し、
それをアニメーションとして精緻に応用していくことが大切です。

最後に

普段の生活の場面 1つひとつを振り返ってみると、
私たちの身の回りは面白い動きで溢れています。

「ボールが弾んだり、放物線を描く描写」
「風船が膨らんだり縮んだりする様子」
「お風呂の排水口に水が吸い込まれていく様子」
などなど

応用によっては、
世の中にまだ無い、新しいアニメーションを作ることができるかもしれません!
私も常に目を光らせて見付けていきます。

最後までお読みいただきありがとうございました。

はじめまして。
センスフルでデザインを担当しております東恩納(ヒガシオンナ)です。

センスフルとは、ユーザーが「お題」に画像を投稿し合い、
センスフル(いいね!)やコメントでわいわい盛り上がるコミュニティです。

今回は、コンセプトが大幅に変わった場合の
デザインをどういうプロセスでリニューアルしていったか、
センスフルの場合のケースについてお話したいと思います。

ダカイゼンとは?

サイバーエージェントでは、月に2度サービスの見直しと、抜本的な改善を進めています。
それが「ダカイゼン」(打開+改善)と呼ば れている運用制度です。

(渋谷ではたらく社長のアメブロ)
http://ameblo.jp/shibuya/entry-11347403598.html

サービスの成長を遮る問題が生じていたら、
発見し速やかに大なり小なりの打開策を投じて、サービスを向上させ続ける。
それがダカイゼンです。

まずはコンセプトを再定義。

まず大前提として、

デザインは「コンセプト」を体現するものであること。
それをいかにユーザーにとって使いやすく、分かりやすく、
そして魅力的にみせるか。
そこがデザイナーにとっての重要な役割です。


「センスフル」は、ダカイゼンにてチーム全体で話し合った結果、
「おもしろ画像バトルで楽しむメディア」から、
「スマホの画像をお題に投稿するコミュニティ」に
なる事が決まりました。
サービスの基本ループとなる「お題に画像を投稿して、他の人からのリアクションを楽しむ」という内容は変わりません。

「バトルで楽しむ」おもしろ画像メディアから、
「お題に投稿して楽しむ」なんでも画像コミュニティへ


上記のように、センスフルは軸となるコンセプトががらっと変わりました。
それに伴ってデザインや世界観を改変していく必要があります。


ロゴのリニューアル

今回のダカイゼンでは、軸となる方向性を変えたため、サービスの「顔」であるロゴからリニューアルすることになりました。


●おもしろ画像「センスフル」の場合

キーワード:センスが光る、かっこつけてるけどダサイ感じ、ちょっとイタイ感じ、中2病っぽいテイスト、面白そう

★具体的な落とし込み方法:
おもしろ画像の時は、センス を競い合うサイトだったため、「センスが光る」イメージで「ン」の部分をキラリと光らせる感じでつくりました。なるべくエッジがたった感じで、「おもしろい画像がありそう」と思ってもらえるように、あえてスタイリッシュすぎず、でもダサすぎず、というぎりぎりのラインを目指しました。

 

● なんでも画像「センスフル」の場合

キーワード:なんでもある、色々なジャンル(カラー)の人やコンテンツ、ごちゃまぜ、にぎわっている

★具体的な落とし込み方法:
沢山色を使い、様々なカラーの人やコンテンツがある感じを出していく。好きなジャンルを自分で選んで投稿する「なんでも画像コミュニティ」になったことで、色々なジャンル(カラー)、人がわいわい賑わうイメージにしました。
おもしろ画像ではなく、日常的でパーソナルな写真で楽しめるような、
コミュニティ感を出すようにしました。

ロゴは、サービス全体の世界観の起点となるものなので、
決めるまでには数十個は最低でも案を出して、
チーム全体でミーティングを繰り返し、決定していきます。


メインユーザーを想定してトンマナを決める。

コンセプト設定のあとは、それを最大限に引き出す世界観を決めて行きます。

2ちゃんやニコ動画好きな男性中心


から・・・・


20代の女性中心とした オールリーチ


に変更することをチームで話し合い決定。

それを汲み取り、
力強く、男性的な色使いから、女性中心~中性的なポップで明るい色使いに変えて行くことにしました。
あくまでも中心は女性だけど、男性が使っていても全くおかしくない雰囲気を出すことが重要です。

※カラーパレットの比較


世界観をターゲットユーザー層にあわせる

ゲーム等と違いコミュニティはキャラ等がいないので、
世界観をつくるのは、ロゴやUIパーツになってきます。

世界観を作る主なデザイン&UIパーツ
①サービスロゴ
②見出し
③メインカラー、サブカラー
④メインフォント、サブフォント選定
⑤各種アイコン

一回の「ダカイゼン」で全てリニューアルしきるのは
時間の制約等あり、なかなか大変なので
何回かで分けてやって行きます。

※アイコンデザインの比較


※ヘッダの比較


※見出しの比較


※ボタンのリニューアル

コミュニティサービスになったことで、
ユーザーにたくさん投稿してもらう事がさらに重要になってきます。
そこで投稿ボタンも全体的にリニューアルしました。

つい押したくなるような、そして押した時に楽しい
ポップなボタンにリニューアル。
しかけとして、タップした瞬間に凹んだ画像に切り替わる処理を入れています。

デザインリニューアルのまとめ

ダカイゼン前のデザインはこのような感じ。


ダカイゼン後のデザインはこのような感じ。


「センスフル」ダカイゼンでのデザインリニューアルは以上です。
サービスの抜本的な変更があった場合、リニューアルするにしても何から手をつけていいのか分からないときなど、クリエイターの方に少しでも参考になれば幸いです!!

最後まで読んでくださり、ありがとうございます。
今まで改善を繰り返しているセンスフルですが、
今後更に進化・発展する予定なので、皆様ご期待ください!!
引き続きどうぞよろしくお願いいたします。

【なんでも画像コミュニティ センスフル】


上のQRコードを読み取るか、下記リンクよりアクセスしてください♪
【スマートフォン】【PC】
http://senseful.jp/


はじめまして(´ω`) teens事業部 デザイナーの芳賀です。
現在は「DECOLINK」というアプリのデザインを担当しています。
DECOLINKとは、1万点のデコが無料で送れる中高生をメインターゲットとしたメッセージアプリです。

1万点のデコが無料!DECOLINK

(DECOLINKについて詳しくはスタッフブログをご覧ください。)

今回はこのDECOLINKの最大の特徴とも言えるデコがダウンロードし放題の「デコ広場」について、デコの特徴やUIなどを3つ簡単にご紹介したいと思います!

1.あえてごちゃっと!見てるだけでワクワクするTOPページ

デコ広場のTOPは特集ページとなっていて、イベントものやおすすめなど、色々なデコパックへの導線が並んでいます。

デコ広場トップページ

ズラズラ~と一見ごちゃっとした印象のトップページですが、
これはデコ広場を訪れたユーザーがとにかく「見てるだけで楽しい!」「デコがいっぱいある!使ってみたい!」と思ってもらえるよう、あえてあまり情報を制限せず多くの画像を並べています。
また何度訪れても楽しめて様々なデコに出会えるように、いくつかのバナーがランダムに表示されるようになっています。

バナーたち


2.3種類の特徴の違うデコたち

デコ広場にはそれぞれ特徴の違う3種類のデコがあります。

①伝えたい感情をキャラクターの表情や表現で表す「スタンプ」
一覧ではキャラクターに興味をもってもらえるよう、それぞれの簡単な性格も一緒に表示。

スタンプ説明

②様々なモチーフやキャラクター+文字・アニメーションで思いを伝える「メッセージ」
一覧では動くイラストとタイトルのみでメッセージの楽しげな雰囲気を伝えています。

メッセージ説明

③吹き出し内に文字と一緒に入れて、動く絵文字として使える「チビデコ」
一覧ではとにかく多くのデコを並べ、チビデコ独特のゴチャッとしたかわいらしさが伝わるように。

チビデコ説明

これらをタブで整理し、またそのデコの中でも人気順や新着順、コラボものやカテゴリで探せるようになっています。

・時間のない時には特集ページからサッとダウンロード。
・ピンポイントに探したい時や時間のある時には色々なページを回遊して楽しみながらダウンロード。

そんなシチュエーションを想定して、このような構成になりました。

3.ダラダラ探せてどんどんダウンロードできる仕組み

デコ広場を作る際にチームのメンバーと話していたのが、「ダウンロードを待つだけの時間を作りたくない。次々と色んなデコをダウンロードしたい!」ということ。
せっかくたくさんのデコがあっても、1つずつに時間がかかっては意味がありません。
そのためデコ広場では1つのパックをダウンロードしている間にも別のページを回遊してデコを探し、またダウンロードすることができます。
そしてダウンロードが完了したものから順にトーストを表示しお知らせします。

ダウンロードの仕組み


以上、とても簡単ではありましたがデコ広場の特徴の中から3つご紹介いたしました。
まだリリースしたばかりのDECOLINKですが、これからユーザーの動きや意見を元にどんどん改善していく予定です。

おわりに

最後まで読んでいただきありがとうございました!
実際にデコを送り合うのはとっても楽しいので、まだの方はぜひ体験してみてください♪
ダウンロードはこちらから⇒ http://deco-link.jp/

また、スタンプのキャラクターでもあるデコりんが、twitterでデコの情報や日常をゆるくつぶやいているので、こちらもぜひチェックしてみてください☆
デコりんアイコン< よろしくリン♪https://twitter.com/DECOLINKbyAmeba

これからも多くのユーザーに愛されるサービスを目指して努力していきます。
引き続きDECOLINKをよろしくお願いします。
はじめまして。GIRLS UPでデザイナーをしています、アビン(GUPDesinger)です。

今回はGIRLS UPのデザインを元に、かわいい女性向けアプリのデザインの要素と作り方をお勉強しましょう!センスに自信が無い人でも、「かわいい」のロジックさえわかっていれば、簡単にかわいいアプリを作ることができると思います。そのロジックをぜひ参考にしてみてください!

まずはたくさんのキーワードを用意する。


よく「かわいいデザイン」って聞きますが、「かわいい」という言葉を聞いてイメージするものが、全ての人にとって全く同じとは限りません。

「かわいくて〇〇」をキーワードで表現する

自分が持っている「かわいい」というイメージを表現するには、たくさんのキーワードで表現するとわかりやすくなります。
まずは、「20代の美を追求する人たちが使うアプリ」というテーマから連想する、〇〇に入りそうな言葉をたくさん並べてみました。
ガーリー、キュート、ラブリー、シック、クール、スタイリッシュ、ポップ、暖かいなど…

キーワードからロゴタイプをイメージする

フォントが持っているイメージはとても強いので、キーワードに合った適切なフォントを使ってロゴタイプをデザインしましょう。
有名なフォントとロゴに関しては、こちらが参考になります。

出した言葉から2つの軸を作りました。
軸1は「スタイリッシュ・力強さ・シック」←→「ポップ・キュート」、
軸2は「クール」←→「暖かい」です。

その後、色々なフォントからGIRLS UPと書いて、それを直感でグラフの上に並べました。GIRLS UPではポップでキュート、暖かい感じのCalibriというフォントのBoldを選びました。

もしここでスタイリッシュでクールな方向性になったら、
スタイリッシュすぎて却下になりそうなデザインです。コミュニティサービスというよりはファッション誌的な印象を受けるこのフォントはDidotです。

デザインムードボード

ロゴタイプを決めたので、チームの人にどういうデザインにするか共有しましょう。しかし、デザインの方向性は決まったものの、アプリの画面はまだ1つもありません。
こういう時に、他の人にデザインのイメージを伝える方法でよく使われるのが「ムードボード」という方法です。
キーワードだけだとどうしてもズレが発生してしまいます。このズレを最小限にするため、既存の写真、ブランド、雑誌などがもっているイメージを借りてきてお互いが思ってる「カワイイ」を合わせる作業です。

GIRLS UPのムードボード

花柄、ドット、レザーテクスチャー、ハートや小物などでできています。なんとなくすごくピンクなアプリになる予感ですね。わたしはこういうデザインをするんだよと、あらかじめみんなに言っておくことで、ムダなやりとり・修正がなくなります。

カワイイ色味

その次は、実際アプリで使うメイン色を決めることです。ムードボードに使われている色からメイン1色を持ってくると便利です。
わたしは、このサイトを配色の参考にしました。Web配色のセンスがあふれる人は直感でやっても良いんですが、世の中の優れた人たちが良い配色の例をすでにたくさん出しているので、そちらを参考にすることもいいと思います。

この3色がGIRLS UPが考える一番かわいい色です!
ここで設定するメインカラーは2つ~3つくらいが適切です。
これを元にアプリ全体の画面を構成します。

アプリ構成

メインの色、フォント、デザインの方向性が決まってから、ワイヤーフレームに沿ってアプリアイコンと画面をデザインしていきます。これがGIRLS UPのアプリアイコンと主な画面です。

かわいい要素

①テクスチャー

GIRLS UPのアプリアイコンです。

アプリのヘッダーです。

アプリ内で使われる見出しです。


女性のユーザに大切に使ってもらいたいという気持ちを込めた、かわいいピンクの革製の手帳をモチーフにして作っています。かわいくてさわりたくなります!!
そして、アプリの中身は、クレヨンで塗ったような、手描き感をイメージして作っています。

②装飾と説明


スタイリッシュで直感的なUIをしているかっこいいアプリもたくさんありますが、GIRLS UPでは「わかりやすい」「簡単にできる」を目指してデザインしています。初めて来た人がいかに使いやすいかを考えたデザインです。初めて来た人に簡単にツイートを促して、初投稿には目立つようにアイコンをつけて、「ほめてあげよう」という文言でたくさんほめられる一連の流れになっています。見た目だけではなくて、使う人を考えるかわいいUIです。

まとめ

GIRLS UPを参考に、かわいいアプリの制作過程について書きました。ぜひかわいいアプリ作りに挑戦してみてください!
ここまで読んでいただき、ありがとうございました。

最後に、GIRLS UPの宣伝をさせてください。

GIRLS UPってなに?

まだまだ改善していくので、楽しみにしてください^0^
【GIRLS UPの制作ブログ】
http://gupdesign.tumblr.com/

【GIRLS UPのスタッフブログ】
http://ameblo.jp/girlsup-staff/

Author : @abibibibi


はじめまして!こんにちワン!
スマートフォン版Amebaプラットフォームでフロントエンドの開発を担当している2012年入社の鳥山と申します。


スマートフォン版Amebaプラットフォームのフロントエンドの開発チームは、HTML、CSS、JavaScriptといったクライアントサイドからNode.jsを使用したサーバサイドの開発もおこなっています。
そこで今回は、Node.jsを始める上で知っておくと便利な知識を現場での開発例を交えて紹介させていただきたいと思います。

deka_img


想定している読者は、
・普段HTML、CSS、JavaScriptなどのクライアントサイドの開発をしている方
・Node.jsに興味があり、始めてみたいと思っている方
・サーバサイドプログラミングと聞くと身震いしてしまう方
です。

※今回は、記事にも制限があるので、Node.jsについての説明や環境構築については省略させていただきます。ご容赦下さい。
その辺りについて知りたい方は、Node.js 日本ユーザグループに日本語ドキュメントで掲載されていますので、そちらの方をご覧下さい。
また、まだNode.jsをインストールしていない方は、Node.jsの公式サイトからダウンロードできるインストーラーを実行すれば簡単にインストールできます。
なお、本記事でのサンプルコードの実行はWindowsならコマンドプロンプト、Mac/Linuxはターミナルでおこなっています。


Node.jsとは?

Node.jsは、簡潔に言うと「Javascriptでサーバープログラミングをするためのプラットフォーム」です。

特徴

JavaScriptと同じ記述方法、同じ仕様で動作するので、JavaScriptを使用したことのある人であれば簡単にサーバサイドプログラムを作成することができます。
また、Node.jsは基本的なネットワークサーバとしての機能をもっているため、PHPやPerlのような別途ApacheのようなHTTPサーバを導入しなくてもすぐにHTTPサーバやソケットサーバとして動作します。
簡単に言えば、「Node.jsだけインストールしてしまえばオッケー」ということです。

なぜ今注目されているのか?

・非同期処理を簡単に扱える
現在、インターネットがよりリアルタイムな情報を扱うようになったため、常時接続や一定期間待機して応答を待つことが不可欠となりました。
Node.jsは非同期I/Oを扱うことで、1つのスレッドで大量の接続を処理でき、1台のサーバで大量の接続を取り扱うことができます。非同期I/Oを扱うには、高度な知識と非同期と同期処理を使い分ける経験が必要なのですが、Node.jsは非同期前提なので、特に高度な知識や経験がなくてもプログラミングすることができます。
実際、私も現在の業務に就くまでは、Node.jsをまったく触れたことがなかったのですが、HTTP通信をしてくれる関数も用意されていますし、処理を書くときもJavaScriptの構文で書けるので、サーバサイドプログラミングだからといって毛嫌いしていたのですが、思っていた以上にすんなりと取りかかることができました。

・JavaScriptの台頭
昨今のスマートフォンの急速な普及と非Flash対応デバイスなどの出現により、JavaScriptの需要性が増してきており、JavaScirptでサーバサイドプログラムを実行できるNode.jsは今非常に注目されています。

以上のようなことからスマートフォン版AmebaもNode.jsを採用しています。


モジュールの使い方

requireとexports

ブラウザ上で動くJavaScirptは、複数あるjsファイルが全て統合され、違うファイルにある関数も自由に呼び出せるのに対して、Node.jsにはモジュールという単位で機能を分割しています。
モジュールが1つのjsファイルとなり、閉じた状態になっています。他のファイルを参照したい場合は、参照したいファイルをexportsオブジェクトで渡す用意をし、それをrequire関数で受け取ることできます。

sample.js(使われるファイル)

exports.print = function() {
 console.log('sample.jsからきたよ');
};

main.js(使うファイル)

var sample = require('./sample.js');
sample.print();

これを以下のように実行すると

$ node main.js

コンソールに「sample.jsからきたよ」と出力されます。
Node.jsはこうしてモジュール間で変数のやりとりをします


HTTPサーバを動かしてみよう

Node.jsサイトのトップページにあるコードを少しローカル用に変えた以下のコードで動きを見てみます。

http_sample.js

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write('Hello World\n');
    res.end();
}).listen(8080);
console.log('server started on 8080');


まず、Node.jsに標準搭載されているhttpモジュールを呼び出し、その中のcreateServerメソッドでHTTPリクエストを処理してレスポンスを返しています。このサンプルでは無名関数としてリクエストの内容にかかわらず、ブラウザに送るレスポンスとして、writeHead関数を使って送るデータの各種情報を定義します。今回は、ステータスコード「200」とし、常に成功の状態を渡し、Content-typeにtext/htmlを返します。そしてwrite関数を使ってデータの中身を定義します。今回は、「Hello World」を書き出しています。そしてlistenメソッドで8080番ポートにバインドしてサーバを開始しています。

そしてこれを実行し、ブラウザで「http://localhost:8080/」と入力すると以下のように表示されます。

http_sample.js


このようにNode.jsでは簡単にHTTPサーバ通信をおこなうことができます。


非同期処理プログラミングをしてみよう

非同期処理プログラミングをする上で、必要なものとその使い方を紹介します。

npmを活用しよう

Node.jsで開発する際にnpm(Node Package Manager)というツールを使うと便利です。Node.jsではすでに多くのライブラリパッケージが公開されており、それらのパッケージをnpmというパッケージ管理ツールを使って簡単に導入することができます。npmを使うとインストールしようとしているパッケージが依存している他のライブラリも自動でインストールしてくれます。(npmに登録されているライブラリ一覧

$ npm install hoge

↑実行したディレクトリ下にnode_modulesディレクトリを作成してモジュールを展開する

$ npm install -g hoge

↑どのディレクトリからでも使える


非同期処理における注意点

さあ、いよいよここから非同期処理のプログラミングにはいっていくわけですが、注意しなければいけないことがあります。それは非同期処理は「イベント駆動」だということです。

イベント駆動とはその名の通り、イベントが発生した時に動き出すものです。つまり非同期処理プログラミングをおこなう時は、いつ発生するかわからないイベントに対して処理を登録していけなければならなく、同期処理をしているJavaScriptのようにコードを上から下に処理してくわけではないのです。


例外処理

上記のようなことからNode.jsでは例外処理の扱いに注意しなければなりません。
JavaScriptの例外処理といえば、try/catchをよく用いるが、これをNode.jsで使用すると、


miss.js

var fs = require('fs');
try { //このブロックの中の処理をまずおこなう
    fs.readFile('hoge.text', function(err, data) {
        console.log(data);
    });
} catch(e) { //try内でエラーおきたらこっち
   console.log('failed this called');
}
$ node miss.js
undefined

hoge.textというファイルが存在しない状態で、このmiss.jsを実行してみると「undefined」が出力されます。
try-catch文はそもそもtryブロックから処理をし、その中でエラーおきた場合にcatchブロックの中の処理をおこないます。
今回の場合は、非同期処理がtryブロックを抜けた後に実行されるため、tryブロックにいる時は、まだエラーかどうかも判断できなく、catchブロックにいかなくdataの中身を出力してしまっています。今回の場合は、readFileの処理が走ってないので出力するdataは「undefined」となっています。

では、どう対処するのが良いのか。
正しくは、登録した処理すべての各々のコールバックで例外処理をおこなう必要があります。


correct.js

var fs = require('fs');
fs.readFile('hoge.text', function(err, data) {
    if (err) { //エラーのとき
        cosole.log(err.message);
    } else { //成功のとき
        console.log(data);
    }
});

Node.jsではコールバックの第一引数にエラーが返ってくるため、上記のように処理すればエラーメッセージがちゃんと出力されます。

$ node correct.js
ENOENT, open 'hoge.text'

async.js

例外処理の仕方もわかったところで非同期プログラミングの中身にはいっていきます。今回は、非同期プログラミングで欠かせないasync.jsについて紹介します。

async.jsはnpmでインストールできます。

$ npm async

前述のとおり、非同期処理はコールバックで定義されるので、大規模な処理をおこなおうとするとコールバックがどんどんネストしてしまい、見た目がとても複雑になってしまいます。


ネスト例

var fs = require('fs');
fs.mkdir('./hoge', function (err) {
    if (err) { // エラー処理
        return;
    }
    fs.mkdir('./hoge/piyo', function (err) {
        if (err) { // エラー処理
            return;
        }
        fs.writeFile('./hoge/piyo/neko', 'Hello Node.js', function (err) {
            if (err) { // エラー処理
                return;
            }
            console.log('success');
        });
    });
});

上記のように、大規模になればなるほどどんどん入れ子になっていき、とても見づらくなってしまいます。
そういった問題を解決してくれるモジュールの一つがasync.jsです。
async.jsには非同期処理を管理する関数がいくつか用意されていますが、今回は代表的なseriesとparallelを紹介します。

・async.series
seriesは、それぞれの関数を実行し、実行が完了したら次に定義されている関数を順次実行していく関数です。


async_series.js

var async = require('async');
console.log('start');
async.series([
    function (callback) { //処理1
        console.log('call function1');
        setTimeout(function() {
            console.log('done function1');
            callback(null, 'result1');
        }, 3000);
    },
    function (callback) { //処理2
        console.log('call function2');
        setTimeout(function() {
            console.log('done function2');
            callback(null, 'result2');
        }, 2000);
    },
    function (callback) { //処理3
        console.log('call function3');
        setTimeout(function() {
            console.log('done function3');
            callback(null, 'result3');
        }, 1000);
    }],
    function (err, results) { //ここでまとめて処理
        console.log('true end');
        console.log(results);
    });
console.log('end?  ... NO. while processing.');

実行すると...

$ node async_series.js
start
end?  ... NO. while processing.
call function1
done function1
call function2
done function2
call function3
done function3
true end
[ 'result1', 'result2', 'result3' ]

となり、配列に定義された関数を順次実行しているのがわかります。
順次実行された関数はまとめて最後に処理します。(今回の場合は、resulltsにそれぞれの関数の処理がオブジェクトで格納される)


・async.parallel
parallelは、それぞれの関数を一度に実行し、全ての関数の結果が得られると完了用のコールバックを実行する関数です。

例は、先ほどのasync_series.jsをasync_parallel.jsに書き換え、ファイル内の3行目のseriesをparallelに書き換えたものを実行します。

$ node async_parallel.js
start
call function1
call function2
call function3
end?  ... NO. while processing.
done function3
done function2
done function1
true end
[ 'result1', 'result2', 'result3' ]

実行の順序は違いますが、結果は同じになります。

以上のように、非同期処理は、実行の順番に気をつけながらプログラミングしていくことが重要となります。
asyncにはまだまだ役に立つ関数が用意されていますので是非見てみて下さい。(async.js


現場での使用例

まとめとして、実際に現場で開発しているものを例として紹介します。(※一部改変あり)

開発例

ディレクトリ構造
directory


game.js

//必要なモジュールをrequireする
var async = require('async');
var api = require('../api');
/*---------------------1------------------------*/ var graph = api.graph; // api/graph.jsをreqiureできる var preregister = api.preregister; var operation = api.operation;
//exportsオブジェクトで外部とやりとりできるようにする exports.register = function(app) {     app.get('/api/game/top/?', function (req, res) {         async.parallel({             func1: function (callback) { //func1の処理                 var opts = {                     query: {category : 'game'}                 };                 //graph.jsで定義されているhttpサーバーにアクセス                 graph.get('hoge/', req, opts, function (err, obj) { /*---------------------2------------------------*/                     if (obj && obj.summary) {                         callback(null, obj);                     } else {                         var noObj = {auth: 0, obj: 0}
                    };                         callback(null, noObj);                     }                 });             },             func2: function(callback) { //func2の処理                 //operation.jsで定義されているhttpサーバーにアクセス                 operation.get('me/events', req, function (err, obj) { /*---------------------3------------------------*/                     if (err) {                         callback(err, null);                     } else {                         callback(null, obj);                     }                 });             },             func3: function(callback) { //func3の処理                 //preregist.jsで定義されているhttpサーバーにアクセス                 preregister.get('me/app', req, opts, function (err, obj) {                     if (err) {                         callback(null, {error: err});                     } else {                         callback(null, obj);                     }                 });             }         }, function (err, results) {             if (err) {                 res.json({ //JSON形式で返す                     func1: 0,                     func2: undefined,                     func3: results.func3                 });             } else {                 res.json({ //JSON形式で返す                     func1: results.func1,                     func2: results.func2,                     func3: results.func3                 });             }         });     }); };
この例は、なるべく本記事で紹介したものを使用しているところを選んで掲載してみました。


ポイントを説明すると、まずコメントアウトで1と書いてあるところです。
1の上の行で「var api = require('../api');」と定義しています。このように定義すると「var graph = api.graph;」のようにapi/配下にあるものを定義できます。

次にコメントアウト2と3です。これは非同期処理の注意点のところでお話したエラー処理の部分です。
2のところでは、コールバックにエラーが返ってきた時に「callback(null,noObj);」と書き、エラー内容を返してません。
それに対して3では、「callback(err,null);」と返しています。これは、async.parallelを使用する際に気をつけなければなりません。async.parallelでは、複数の処理を同時におこなっているのですが、どれかの処理がエラーを返してしまうと処理全体がエラーの扱いになってしまいます。なので処理によってエラーを返すか(3)、またはエラー用のオブジェクトを用意してそれを返すか(2)を考え開発しなければなりません。これは私もNode.jsをさわり始めた頃ハマったところでした。

現場で感じたこと

以上のように、実際の現場でクライアントサイドのJavaScriptとサーバサイドのNode.jsの両方の開発をおこなっていて感じたこととしましては、「非同期への危機管理」が非常に高まったということです。
この業務に就く前もクライアントサイドのJavaScriptで非同期を扱うことがありましたが、基本的にはコードを上から下に処理していくという手続き形式でプログラムをしていました。しかし非同期が強制されるNode.jsに触れてからは、手続き形式のプログラムをしているとバグだらけのプログラムになってしまうので自動的にスケーラブルなプログラムを書くように心がけるクセをつけることができました。これが結果的にクライアントサイドのプログラムにも活きており、フロントエンドエンジニアとしての幅を広げることができています。


最後に

今回、入門といった形で、あくまでフロントエンドエンジニアという立場で「Node.js」を紹介させていただきました。
ですのでサーバ構築やデータベース連携などの深い部分のお話は端折らせていただきました。中には物足りないと思った方もいらっしゃると思いますが、HTML、CSS、ブラウザ上のJavaScriptにしか関わってこなかった方達にとって少しでもNode.jsを敷居の低いものとして感じて興味をもっていただけたならば幸いです。

最後まで読んでいただきありがとうございました!
何卒スマートフォン版Ameba(スマホで見てください)をよろしくお願いいたします。

はじめまして。アメーバ事業本部でディベロッパーをしています、平木(id:Layzie)です。

先日2/9に行なわれたFrontrend Vol.4で"JavaScript Development Tools – JavaScript開発の効率アップ"というテーマで登壇させていただいたのですが、セッションでは時間の都合でお話できなかった補足や、その他のツールのご紹介をしていきたいと思います。

こちらの公式サイトで各講演のスライドと動画を見ることができますので、残念ながらイベントにいらっしゃらなかった方は、ぜひご覽になってください。

Chrome Canaryビルドについて

講演ではお話できませんでしたが、Chromeには複数のバージョンが存在しており、開発時にはその内のChrome Canaryビルドを使用すると便利です。

便利な理由としては以下です。
  • ほぼ毎日ビルドが更新されているので最新の機能が使用できること
  • プロファイルが正式版とは別になり、余計なCookieや拡張機能などが入らないので普段使いのChromeと開発用のCanaryビルドと使い分けができること
Web Develoer Toolsでの最新機能例としては、Canvas要素のFrameのProfileが取れたり、Networkタブのパスを右クリックすると、URLがcurlコマンドとしてコピーできたりなどなど。また、もちろんchrome://flagsからアクセスできる試験運用機能も正式版より豊富になっています。

Chrome(WebKit)の最新のAPIなどを使用したい場合は、特にこちらのCanaryビルドの使用をお勧めします。

余談ですが、ChromeのDeveloper ToolsはCSSを変えることによっていつもの見た目とは違うテーマを使うことができたりします。
たまに気分を変えてみるのも良いかもしれません(例:http://github.com/frontdevDE/mnml-devtools-theme)

Charles

デバッキングプロクシとして、ご紹介したCharlesについてです。時間の都合で結構端折ってしまったのですが、このソフトはやたらと高機能です。講演でご紹介できなかった機能を一部ご紹介したいと思います。

HTTP通信の内容が気軽に見れる

$1 pixel|サイバーエージェント公式クリエイターズブログ

これに関しては、特定の1ページだけの通信などは前述のChrome Developer Toolなどでも確認が可能ですが、一度ベージ遷移したりリロードしてしまうと、以前のリクエストなどは分からなくなってしまいます。

Charlesの場合は独立したソフトのため、どのブラウザのいつの通信でも記録してくれるのが特徴です。Chromeの拡張機能の通信なども記録してくれますので、拡張機能の開発時などには重宝するのではないでしょうか。JSONのレスポンスなどもきちんと分かりやすく構造化して見れます。

$1 pixel|サイバーエージェント公式クリエイターズブログ

モバイルブラウザの通信内容のキャプチャ

例えばMacでCharlesを動作させておき、iPhoneの通信内容をCharlesでキャプチャしたいという場合には以下のような手順で作業することにより実現可能です。
  1. まずはPCとモバイル機器が一緒の無線ルーターに繋っていることが前提になります。
  2. CharlesのメニューのProxy→Proxy Settings→Proxiesタブの"HTTP Proxy"のPort番号をメモ

    $1 pixel|サイバーエージェント公式クリエイターズブログ
    $1 pixel|サイバーエージェント公式クリエイターズブログ
  3. PCのIPアドレスをメモ(Macだとシステム環境設定→ネットワーク→IPアドレスが該当します)
  4. iPhone前提ですと、設定→Wi-fi→自分の無線ルーターの項目にHTTPプロキシという項目があるので"手動"に設定し、サーバーに3のIPアドレス、ポートに2のポート番号を入れる

    $1 pixel|サイバーエージェント公式クリエイターズブログ
以上でiPhoneなどのモバイル機器の通信内容がCharlesで見れると思います。

HTTPのリクエスト・レスポンスの書き換え

メニューからTools…→Rewrite…→Rewrite Settingsで"Enable Rewite"にチェックを入れて、ルール設定をすると、HTTPリクエスト・レスポンスなどを書き換えすることができます。

$1 pixel|サイバーエージェント公式クリエイターズブログ

HeaderやBody、Queryなどを好きなように書き換えすることが可能になります。これを使うと特別ブラウザで設定しなくてもUAなどの偽装が可能になったりします。

$1 pixel|サイバーエージェント公式クリエイターズブログ

まだ他にも色々と機能があるのですが、正直自分も全ての機能を使った…ということはないので、「こんな機能を使ったら開発が楽になった」など事例があれば、教えていただけると嬉しいです。

1つ注意点として、開発時にCharlesでローカルのCSSやJavaScriptを見るように設定していると、他の人がサーバーにアップした変更がいつまで経っても反映されない…ということがあります。「修正してくれたって言ってたのに何も変わってない…」という場合はCharlesのMap Localの機能を切ってみることをオススメします。自分はこれで何回か泣きました。

jshint

.jshintrcファイル

サイトの下にあるチェックボックスでどのエラー内容を細かくセッティングするようになっています。CUIでも同じようにセッティングしたいと思うのが人情です。

このセッティングをするのが".jshintrc"という設定ファイルになります。俗にrcファイルというものですが、コマンドラインのツールでは".hogerc"などのようにピリオドを先頭にし、コマンド名+rcという名前が設定ファイルとして良く使われます。

書き方としてはJSONファイルになっており、設定したい項目(サイトのチェックボックスの項目)をkeyにして、valueを真偽値で設定するのが一般的な設定項目になっています。一例として自分がサンプルとしてGistに上げたものをご覽ください。

こちらのファイルを自分の$HOMEディレクトリか、プロジェクトのトップディレクトリに置いて`--config`オプションでそのファイルを指定してあげるなどして使います。

詳しい項目はjshintのドキュメントにもあるのですが、日本語で解説をしてくださっている方がいらっしゃいますので、そちらの方が見やすいかと思います。

JSHintで気軽なコーディングを

jshintのエラーの対処

jshintのエラーメッセージは分かりやすいものもあれば、「何でこんなエラー出てるの?」と思うものもあるかと思います。JSLint Error Explanationsでエラーのテキストを検索すると、どのような時に出るエラーか?なぜエラーにされるのか?正しい対応の仕方が1ページにまとまって参照できるようになっているので、便利です。

ただ単に読むだけでも、JavaScriptの勉強になるのではないかと思います。

jshint-i

jshintをコマンドとして叩いた場合、エラーの数が多いと画面をスクロールしないと最初のエラーが分からなかったりと不便な場合があります。色々と対処の仕方はあるかと思いますが、jshint-iというツールを入れるのも良いかもしれません。

使い方はほとんどjshintと変わりがありません。
npm install -g jshint-i
npmコマンドでグローバルオプションを付けて、jshint-iをインストール後に
jshint-i path/to/jsfile
とすると
line: 23
Character: 12

> }))
Missing semicolon.

[n] Next; [r] Rescan; [q] Quit; Action:
とエラーを1個ずつ表示してくれるようになります。次のエラーを見たい場合はn、もう一度最初から調べる場合はr、jshint-iから抜ける場合はqを入力してください。こじんまりとして中々見やすいのではないかと思います。

Plato

最後に、講演でご紹介しようかどうか迷った末に、結局やめてしまったPlatoというコマンドラインツールをご紹介したいと思います。

このツールはどんなものかというと、JavaScriptを静的に解析した上でHTML上でその結果をグラフなどにして見せてくれるというツールです。

こちらもNode.js製のツールになりますので、npmでインストールします。
npm install -g plato

インストール後にソースを解析したいプロジェクトに移動して以下のようなコマンドを打ってください。
plato -r -d report path/to/js

コマンドオプションはGitHubのREADMEをご覽いだきたいですが、このコマンドの意味としてはjsファイルがあるディレクトリを再帰してreportという名前でplatoのレポートを作成するディレクトリを作るというものになります。

その後report/index.htmlをブラウザで開くと以下のような画面で、メンテナンスの容易さやコード行数、潜在的なバグが発生する確率などが各JavaScriptファイル毎にグラフで表示されます。(例は自分が作ったライブラリなので1つのファイルですが)

$1 pixel|サイバーエージェント公式クリエイターズブログ

またグラフの中のファイル名をクリックすると各JavaScriptファイルの詳細画面に遷移してこのようになります。円グラフは各関数の行数や複雑さなどを表示しており、円グラフをクリックすると該当の関数を表示してくれます。

該当関数にはjshintでのエラー内容などが確認できるようになっています。

$1 pixel|サイバーエージェント公式クリエイターズブログ

このツールの使い所としては、リファクタリングする際などにどのファイルから取りかかれば良いか?などの指針を決める時や、コードレビューなんかに使うことができます。ビジュアルもキレイなので、分かりやすいかと思います。

まとめ

今回はFrontrend Vol.4でお話させていただいた、JavaScript開発環境についての補足を書かせていただきました。

講演のまとめの繰り返しになるのですが、JavaScriptは開発に際して敷居が低く取っつきやすい言語ですが、本格的な開発をする時には逆にJavaのIDEであるEclipseなどのように「これがあれば必要十分」というようなツールが少ないです。(IDEとしてWebStormのような優秀なツールも出てきてはいますが…)

このために、色々なツールを必要に応じて組み合わせることにより、開発効率を格段にアップさせることが必要不可欠になると思います。例えば、はてなブックマーク、や、Qiitaなどのサイトのウォッチや、英語にはなりますがDailyJSJSProJavaScript Weeklyの購読などしておくと色々と自分の知らない便利なツールが見つかり、幸せなJavaScript開発ができるようになるかもしれません。

ここまで読んでいただき、ありがとうございました。

こんにちは!スマートフォン版Ameba(通称:デカグラフ)でフロントエンドの開発をしている2012年度新卒入社のオオシモと申します。

デカグラフでは、主にSNS機能(サークル・掲示板・メッセージ・写真など)のNode.jsからフロントエンドのJavaScriptを担当しています。ちなみにエディタはSublime Text 2を使用しています。とっても軽くて高機能でオススメです。

さて、皆さんはフロントエンドのJavaScriptのライブラリは何を使っていますか?多くの方がjQueryを使用しているのではないでしょうか。

デカグラフでは、chikuwa.jsと呼ばれるスマートフォンに特化した軽量ライブラリを使用しています。このライブラリは弊社の首席エンジニアが開発したものです。名前の由来は彼のペットのチワワのチクワちゃんらしいです!?

chikuwa.jsは以下のような特徴があります。

  • ファイルサイズが小さい
  • jQueryと比べて、スマートフォンに必要のないクロスブラウザ対策などの余計な記述がない
  • RouterやViewを使って簡単に画面遷移やコンテンツの出し分けができる
  • モジュール別の大規模開発を前提に作られている

今回はchikuwa.jsの使い方と、それを用いた大規模ウェブサイトのフロントエンドの開発手法を少しばかりお伝えしたいと思います。

DOM操作

デカグラフのほとんどのDOMは、JavaScriptによって生成・操作されています。

セレクタ

セレクタはjQueryとほぼ同様に書くことができます。

// containerというIDを持つdiv要素を選択
$('div#container')
// textというクラスを持つp要素を選択
$('p.text')

DOM生成

DOMの生成には主にtagメソッドとgatメソッドを使用します。

// <div class="container"></div>
$.tag('div.container')

// <div class="container">
//     <p class="text">もけけけ</p>
// </div>

$.tag('div.container')
    .tag('p.text').text('もけけけ').gat();

// また、jQueryと同様にappend/prependメソッドで上記と同様のDOMを生成することができます。
var container = $.tag('div.container');
container.append($.tag('p.text').text('もけけけ'));

イベント

イベントはonメソッドでDOMに登録することができ、第2引数のcallback関数は登録されたイベントの後に呼び出されます。これもjQueryとほぼ同様の使い方ですね。

// containerクラスを持つdiv要素にtouchstartイベントを登録。
$('div.container').on('touchstart', function (event) {
    alert('tapされたよ!');
});
// イベントの削除はoffメソッドを使用します。
$('div.container').off('touchstart');

プラグイン

デカグラフのページ遷移は、プラグインであるchikuwa-view.jsとchikuwa-dispatcher.jsを使っています。

chikuwa-view.js

chikuwa-view.jsではViewオブジェクトを定義することができます。Viewオブジェクトは、ページやページの中の細かいモジュールを作ることができます。


デカグラフのフロントエンドのJavascriptのファイル構成は機能単位で分けられています。

サークル(group.js)を例に取ると、サークルは、サークルTOP・サークル作成・サークル編集などの複数のページを持っています。それぞれのページに対してViewオブジェクトが定義されていて、さらに1つのページはモジュール単位のViewオブジェクトを組み合わせることによって作られています。


JavaScriptファイル構成とViewオブジェクトの構成

1 pixel|サイバーエージェント公式クリエイターズブログ-view

Vewオブジェクトview.group.topは複数のViewオブジェクトから成り立っています。

1 pixel|サイバーエージェント公式クリエイターズブログ-view_component

以下は、サークルのViewオブジェクトのサンプルコードです。

// ページ単位のViewオブジェクトを定義
$.views({
    'view.group.top': {
        init: function () {
            this.dom = $.tag('div').text('サークルTOP');
        },
        render: function () {
            // モジュールをappend
            $.view('view.group.top.myGroup').show(this.dom);
            $.view('view.group.top.pickup').show(this.dom);
            return this.dom;
        }
    },
    'view.group.edit': {
        init: function () {
            this.dom = $.tag('div').text('サークルの編集');
        },
        render: function () {
            return this.dom;
        }
    }
});
// モジュール単位のViewオブジェクトを定義
$.views({
    'view.group.top.myGroup': {
        init: function () {
            this.dom = $.tag('div').text('マイサークル');
        },
        render: function () {
            return this.dom;
        }
    },
    'view.group.top.pickup': {
        init: function () {
            this.dom = $.tag('div').text('ピックアップ');
        },
        render: function () {
            return this.dom;
        }
    }
});
// bodyにview.group.topをappend
// <body>
//     <div>サークルTOP
//         <div>マイサークル</div>
//         <div>ピックアップ</div>
//     </div>
// </body>
$.view('view.group.top').show($('body'));

chikuwa-dispatcher.js

プロフィールのアルバムのページ(https://s.amebame.com/#profile/photos/1234(ユーザーID)?tab=album)へ遷移するとIDが1234のユーザーのアルバムが表示されます。


1 pixel|サイバーエージェント公式クリエイターズブログ-view

ページはハッシュをもとに出し分けられて、ユーザー情報はハッシュの"1234"から、そして写真・アルバムのどちらのタブを表示するかは"?tab=album"をもとに表示されています。ハッシュに"?tab=photo"が含まれている場合は写真のタブを表示します。

このように、どのコンテンツを出すのかはハッシュによって決められていて、chikuwa-dispatcher.jsのDispatcherオブジェクトによって実現されています。


下記のコードはプロフィールのアルバムへ遷移する時のサンプルコードです。

まず、createHashメソッドでhashを作成して、excuteメソッドでhashと一致するRouterのaction関数を呼び出します。Routerはroutesメソッドによって定義されています。第3引数のparamsと第4引数のqueryはaction関数の引数(下記の例ではvars)にセットされています。paramsとqueriesの値からページを出し分けします。

// ハッシュを生成
// controller: profile
// action: photo
// params: {id: '1234'}
// query: {tab: 'album'}
var hash = $.dispatcher.createHash('profile', 'photo', {id: '1234'}, {tab: 'album'});// #profile/photo/1234?tab='album'
// ページ遷移
$.dispatcher.execute(hash);
// 下記のようにRouterが定義されていると、controller名(profile)とaction名(photo)が一致するaction関数が呼び出されます。
// Routerを定義
$.routes('profile', {
    '/photo/:id': {
        name: 'photo',
        action: function (vars) {
            var id = vars.params.id;// 1234:idによってユーザを出し分け
            var tab = vars.queries.tab;// album:どちらのタブを開くかを出し分け
            if (tab === 'photo') {
                // 写真タブを出す処理
            } else if (tab === 'album') {
                // アルバムタブを出す処理
            }
        }
    }
});

基本的には、各JavaScriptファイルにRouterを1つ定義し、ページ毎にactionが用意されています。そして、action関数内でそのページに当たるViewオブジェクトを呼び出します。

ABテスト

デカグラフでは、ユーザーに合わせてより良いコンテンツを見ていただくためにA/Bテストを行なっています。A/Bテストの説明とデザインの手法はこちらの記事の中で説明されています。

そこで、今回はchikuwa.jsを用いた、フロントエンドでのA/Bテストの出し分け方法について説明したいと思います。


下記がA/Bテストのサンプルコードです。基本的には前述したViewオブジェクトとRouterを使って出し分けをしています。まず、A用とB用の複数のViewオブジェクトからなる配列を用意します。ハッシュの中にAが含まれているのならばA用の配列を、Bが含まれているならばB用の配列をforEachでループさせてViewオブジェクトをbodyにappendしています。

$.routes('test', {
    '/:pattern': {
        name: 'top',
        action: function(vars) {
            var pattern = vars.params.pattern;// A or B
            var viewArray = [];// 複数のViewオブジェクトを入れる配列を用意
            if (pattern === 'A') {// Aパターンの場合
                // Aパターンに必要なViewオブジェクトを配列にセット
                viewArray = [
                    'view.a.header',
                    'view.a.body',
                    'view.a.footer'
                ];
            } else if (pattern === 'B') {// Bパターンの場合
                // Bパターンに必要なViewオブジェクトを配列にセット
                viewArray = [
                    'view.b.header',
                    'view.b.body',
                    'view.b.footer'
                ];
            }
            viewArray.forEach(function(item) {
                // それぞれのViewオブジェクトをbodyにappend
                $.view(item).show($('body'));
            });
        }
    }
});

上記のコードのようにRouterとViewオブジェクトを用いることにより、簡単にA/Bテストの出し分けを実現することができます。

最後に

最後までお付き合いいただきありがとうございました。

chikuwa.jsはMITライセンスの規約を守れば誰でもフリーで利用することができます。スマートフォンに特化したWebサイトを開発する際には、みなさんの使用するライブラリの選択肢の1つにいれていただければと思います。