CanvasアニメーションをHerokuで公開しようよ!
ブログを下記に移転しました。デザイン変更により移転先では記事が一層読みやすくなっていますので、よろしければ移動をお願い致します。
CanvasアニメーションをHerokuで公開しようよ! : melborne.github.com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
もしあなたが暇で暇でしようがなくて
一日中時計をぼーっと眺めるのも悪くない
と考えているのなら
次のリンクをクリックしてください
3分くらいならあなたの時間をつぶせるかもしれません
もしあなたがRubyを使っていて
JavaScriptのことはよく知らないけれども
HTML5のCanvasに興味がでてきて
その成果物をネットで簡単に公開できればうれしいかも
と考えているのなら
以下の記事を読む価値があるかもしれません
もちろん何の保証もありませんが..
Canvasを使ったWebアプリケーションの構築
この記事は先のリンクで示した
接近する時計のWebアプリケーションを構築する手順を書いています
HTMLはhamlとscssを使って
JavaScriptはjQueryを使って記述しています
WebフレームワークSinatraを使ってHerokuにデプロイしています
OSはMac OSX Tiger..です
ディレクトリ構成
最終的なファイル構成は以下のようになります
. ├── Gemfile ├── Gemfile.lock ├── clock.rb ├── config.ru ├── public │   └── javascripts │   └── clock.js └── views ├── index.haml ├── layout.haml └── style.scss
Sinatraのために
clock.rb layout.haml index.haml style.scssが必要になります
時計を描画するJavaScriptはclock.jsに記述します
Herokuにデプロイするために更にconfig.ru Gemfileが必要になります
Gemfile.lockはbundler installコマンドで自動生成されます
以下では一つずつファイルを用意する必要がありますが
僕のような無精者のために
Sinatra版scaffold ease_sinatra.rbを用意しました
Scaffolding Sinatra Basic Template · GitHub
カレントディレクトリでWebApp::ease_sinatraすれば
Sinatraのテンプレートファイルが得られます
かなりいい加減な作りであることをご了承下さい..
clock.rb
まずWebフレームワークのコントローラとなるclock.rbを書きます
require 'sinatra' require 'haml' require 'sass' configure do APP_TITLE = "Approaching Clock" CREDIT = ['hp12c', "http://d.hatena.ne.jp/keyesberry"] end get '/' do haml :index end get '/style.css' do scss :style end
configureブロックはアプリ立ち上げ時に一度だけ呼ばれます
get '/'でルートが呼ばれた(GETされた)ときの挙動を記述します
ここでは
hamlで記述されたviews/index.hamlが返されるよう指定しています
get '/style.css'でlink属性でstyle.cssが呼ばれたときに
scssで記述されたvews/style.scssが返されるよう指定しています
layout.haml
次にlayout.hamlを記述します
Sinatraではlayoutという名のテンプレートが存在する場合
各テンプレートの読み出しに先立ってそれが自動で読み出されます
!!! 5 %html %head %meta{:'http-equiv' => 'Content-type', :content => 'text/html', :charset => 'utf-8'} %title= APP_TITLE %link{:rel => 'stylesheet', :href => '/style.css', :type => 'text/css'} %script{:type => "text/javascript", :src => "https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js", :charset => "utf-8" } %script{:type => "text/javascript", :src => "/javascripts/clock.js", :charset => "utf-8" } %body = yield
titleタグに先ほどのconfigureで定義したAPP_TITLEを指定します
hamlでは=(イコール)以降をRubyのコードとして評価します
jQueryはGoogleが提供するものを使っています
時計を記述するclock.jsを指定します
bodyタグの中身はyieldで実体ファイル(index.haml)に委ねます
index.haml
次にindex.hamlを記述します
%header #main %canvas#clock{:width => '1000px', :height => '500px'}Only for HTML5 adapted browsers %footer %a{:href => CREDIT[1]}= CREDIT[0]
mainのcanvasタグにclockというid名を付けサイズを指定します
HTML5非対応ブラウザのためのメッセージを記述します
footerタグにCREDITのリンクを貼ります
style.scss
次にstyle.scssを記述します
scssはsassy css(sassライクなcss)を意味するcssの拡張言語です*1
scssを使用することによりcssの文法に沿って
sassの拡張を取り入れることができます
$font_color: #D0FFD0; $bg_color: #325F82; $canvas_color: #FFFFFF; @mixin rounded($topl:32px, $topr:32px, $btmr:32px, $btml:32px) { border-radius: $topl $topr $btmr $btml; -moz-border-radius: $topl $topr $btmr $btml; } * { margin: 0; padding: 0; font-family: Trebuchet ms, Verdana, Myriad Web, Syntax, sans-serif } body { color: $font_color; background-color: $bg_color; width: 1000px; margin: 60px auto; } header { display:block; } #main { canvas#clock { border: thin solid #444; background-color: $canvas_color; @include rounded(); } } footer { display: block; height: 30px; text-align: center; margin-top: 20px; a { text-decoration: none; color: $font_color; &:visited { color: $font_color; } } }
$varnameでグローバル変数を定義できます
セレクタをネストできます
@mixin-@includeでセレクタブロックを関数ライクに使えます
ここではrounded()で角丸にミックスインを使っています
clock.js
メインとなるclock.jsを記述します
JavaScript初学者なので書き方に問題があるかもしれません
間違いをご指摘頂けるとうれしいです
少し長いので分けて説明します
var canvas = {}; $(document).ready(function(){ canvas.c = $("canvas#clock"); canvas.ctx = canvas.c[0].getContext('2d'); canvas.width = canvas.c.width(); canvas.height = canvas.c.height(); canvas.ctx.translate(canvas.width/2, canvas.height/2); const min = 60; var x = min; var sp = 2; clock(x); setInterval( function(){ clock(x); x += sp; if (x > canvas.width*0.6 || x < min) { sp = -sp }; }, 500 ); })
グローバルに参照できるcanvasオブジェクトを定義します
$(document).ready..はHTMLドキュメントの読み込みの完了を待って
その引数の関数が実行されることを保証します
そのなかで最初にcanvasオブジェクトにcanvasの情報を
オブジェクト・プロパティとしてセットします
JavaScriptではオブジェクト・プロパティは先行する宣言が不要です
canvasへの描画はcanvasオブジェクトの2Dコンテキストに対し行います
そのためgetContext('2d')しますが
jQueryではcanvasオブジェクトはArrayを返すので注意が必要です
時計の描画は中心点を基準に行うほうがやり易いので
ctx.translateで座標軸をcanvasの中心に移動します
Canvasにおけるアニメーションの描画にはsetIntervalを使います
setIntervalは第2引数に指定した周期で第1引数に渡した関数を
関数の実行スタックに繰り返し登録します
setIntervalの第1引数にはclock関数を包んだ匿名関数を渡します
時計を描画するclock関数はそのサイズxを引数にとります
サイズxは匿名関数が呼ばれる度にspだけ増分されてclockに渡されるので
呼び出しの度に時計のサイズは大きくなっていきます
サイズxが任意の値を超えると(ここではcanvas.width*0.6)
時計は今度は徐々に小さくなっていきます
続いてclock関数の中身を見ていきます
function clock (radius) { canvas.ctx.clearRect(-canvas.width/2,-canvas.height/2,canvas.width,canvas.height); var unit = radius/75; drawFrame(radius, '#325FA2'); canvas.ctx.save(); canvas.ctx.rotate(-Math.PI/2); //set start angle at twelve o'clock drawHand('hr', radius*0.5, unit*3, unit*5, 'black'); drawHand('min', radius*0.9, unit*2, unit*10, 'black'); drawHand('sec', radius*0.9, unit, unit*5, 'red'); canvas.ctx.restore(); }
ctx.clearRectでキャンバスをクリアします
drawFrame関数で時計の文字盤を描画し
drawHand関数で針を描画します
針の描画はctx.rotateでキャンバスの座標系を回転させながら行うので
最初に初期位置を12時の位置に合わせています
ctx.save() ctx.restore()は動かした座標系を元に戻すために使います*2
saveでそれ以前の状態を保存し座標系を動かして描画を行った後
restoreで元に戻します
各描画サイズは
clockに渡されるサイズxに対する比で規定することによって
時計のサイズが変わってもそのバランスが崩れないようにします
次にdrawFrame関数の中身をみます
function drawFrame (radius, color) { drawCircle(radius, radius*0.1, color, false); drawPitchLines(radius*0.9, 2, 1); drawNumbers(radius/5, radius*0.7, color); } function drawCircle (distance, width, color, filly) { var ctx = canvas.ctx; ctx.beginPath(); ctx.lineWidth = width; ctx.strokeStyle = color; ctx.arc(0, 0, distance, 0, 2*Math.PI, true); filly ? ctx.fill() : ctx.stroke(); } function drawPitchLines (distance, length, width) { var ctx = canvas.ctx; ctx.save(); for (var i=0; i < 60; i++) { ctx.beginPath(); ctx.strokeStyle = "black"; ctx.lineWidth = width; ctx.lineCap = 'round'; var len = i%5==0 ? length*3 : length; ctx.moveTo(0, -distance); ctx.lineTo(0, -(distance-len)); ctx.stroke(); ctx.rotate(2*Math.PI/60); }; ctx.restore(); } function drawNumbers (size, distance, color) { var ctx = canvas.ctx; ctx.font = size + "px Helvetica"; ctx.fillStyle = color; for (var i=1; i <= 12; i++) { ctx.save(); ctx.translate(distance*Math.sin(Math.PI/6*i), -distance*Math.cos(Math.PI/6*i)); ctx.fillText(i, -size/3, size/3); ctx.restore(); }; }
drawFrame関数では外円とインデックスバーと数字を描画する
drawCircle drawPitchLines drawNumbersを呼びます
線の描画はbeginPathで開始宣言し
moveToで開始点lineToで終了点を決めて
strokeで実際に描画します
円はarcで描画します
引数には中心座標 半径 描画角始点終点
および描画方向を指定します
数字の描画はfillTextで行います
次に時計の針を描画するdrawHand関数をみます
function drawHand (unit, length, width, offset, color) { var now = new Date(); var hr = now.getHours(), min = now.getMinutes(), sec = now.getSeconds(); hr = hr >= 12 ? hr-12 : hr; var _360 = 2*Math.PI; var angle; if (unit=='hr') { angle = hr*_360/12 + min*_360/(12*60) + sec*_360/(12*60*60); } else if (unit=='min') { angle = min*_360/60 // + sec*_360/(60*60); } else { angle = sec*_360/60; }; var ctx = canvas.ctx; ctx.save(); ctx.strokeStyle = color; ctx.lineWidth = width; ctx.rotate(angle); ctx.beginPath(); ctx.moveTo(-offset,0); ctx.lineTo(length,0); ctx.stroke(); ctx.restore(); }
Dateオブジェクトを使って現在の時・分・秒を取得します
針の種類によって一度に進む量angleが異なるので場合分けします
ここでは時針は時刻の進行で少しずつ移動しますが
分針は60秒ごとに一気に1つ進むようにしています
clock.jsは以上です
ローカルでの起動
これでローカルで実行する環境が整いました
早々立ち上げてみましょう
/Users/keyes/aclock% ruby clock.rb == Sinatra/1.1.2 has taken the stage on 4567 for development with backup from Thin >> Thin web server (v1.2.7 codename No Hup) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:4567, CTRL+C to stop
Ruby1.9.2でshotgunを利用する場合
カレントパスをロードする必要があるかもしれません
/Users/keyes/aclock% shotgun -I. clock.rb == Shotgun/WEBrick on http://127.0.0.1:9393/ [2011-02-18 18:28:47] INFO WEBrick 1.3.1 [2011-02-18 18:28:47] INFO ruby 1.9.2 (2010-12-25) [i386-darwin8.11.1] [2011-02-18 18:28:47] INFO WEBrick::HTTPServer#start: pid=1613 port=9393
Herokuへのデプロイ
http://localhost:4567で問題なくアプリケーションが起動したら
Herokuにデプロイするためにconfig.ruとGemfileを用意します
config.ru
require "bundler" Bundler.require $LOAD_PATH << File.expand_path(File.dirname(__FILE__)) require 'clock' run Sinatra::Application
Gemfile
source :rubygems gem "sinatra" gem "haml"
Herokuに必要なgemsをインストールするために
BundlerというGem管理ツールを使います
Gemfileに必要なgemsを羅列し
config.ruではbundlerをrequireしてこれらを読み込むよう指定します
Bundlerをインストールして
installコマンドを実行します
/Users/keyes/aclock% gem install bundler /Users/keyes/aclock% bundle install
これでGemfileに記述したgemsが
アプリケーションで使えるようになります
同時にGemfile.lockが生成され
ローカルとHerokuで使われる
gemsのバージョンの一致が保証されます*3
HerokuへのデプロイはgitとHeroku gemを使います
初回はSSHキーのセットアップなどが必要になりますが
説明は他サイトに譲ります*4
/Users/keyes/aclock% git init /Users/keyes/aclock% git add . /Users/keyes/aclock% git commit -m 'initial'
Heroku側にアプリケーションのレポジトリを用意し
git pushでデプロイします
/Users/keyes/aclock% heroku create myclock /Users/keyes/aclock% git push heroku master
早々アプリケーションを立ち上げましょう
/Users/keyes/aclock% heroku open
うまくいかない場合はlogを見てみましょう
/Users/keyes/aclock% heroku logs
さあ
あなたもCanvasを使ったサイトを立ち上げましょう!
enjoy your Canvas life!
ソースコードは以下にあります
GitHub - melborne/Approaching-Clock: HTML5 CANVAS DEMO
*1:Sass、そしてSassy CSS (SCSS) http://hail2u.net/documents/sass-and-sassy-css.html
*2:save,restoreが保持するのは座標系だけに限らない
*3:http://devcenter.heroku.com/articles/bundler
*4:http://devcenter.heroku.com/articles/quickstart, http://d.hatena.ne.jp/ruedap/20110128/ruby_heroku_sinatra_hello_world