slowjet

is a part of a carburetor

genymotionとADB(Android Debug Bridge)でAndroidのWebページデバッグ

長らくAndroid対応をしてなかったので浦島状態だったのだけど、Genymotioinっていうのを使うと、さくさく動いてデバッグしやすいというのを聞きまして。

結論から言うと、4.x系だけでした。2.3.x系以前のOSはなかったので、つまり実機か、バーチャルテスト端末みたいなの使うしか。

Genymotioinを使うにはアカウントが必要なので、取っとく。
VirtualBoxがインストールされてない場合は、インストールしておく。
ついでに、AndroidのSDKもダウンロードして、適切な場所に展開しておく。

さて、準備ができたところで、Genymotioinをインストールしまして、エミュレートしたい端末を選んで、ダウンロードして、立ち上げると、確かにさくさく動く!

お次は、ADBでコンソールを表示しよう。

これを見ればOK。Genymotioinで立ち上げてる端末は、

$ ./adb devices

に表示されるんで、エミュレート端末のシリアルを指定すればOK

$ ./adb -s <serial number> logcat | grep browser

すると

[nori]$ ./adb -s 192.168.56.101:5555 logcat | grep http://10.0.1.5:8080
E/browser (  930): Console: Uncaught TypeError: Object #<FileReader> has no method 'addEventListener' http://10.0.1.5:8080/scripts/app.js:217

こんな感じでログが出るだよ。

簡単楽々、動作もさくさく

まあこれだと、consoleは見られるんだけど、Objectは見られないし、ちょっと不便だよね・・・そういうわけで、Adobe Edge Inspectからweinre試してます。また今度

パズドラ風アイコンジェネレーター ver.2 #ここでハマったよiOS

今回はiOSでハマった場所を中心にお送りします。
Android・・・?そんな(うんこ)端末知らねぇぜ・・・

パズドラ風アイコンジェネレーターはinput[type="file"]で選択した画像をFile APIを使って、読み出してCanvasに書き出してるわけですが、

その筋の人にはおそらく有名なこの問題に、例外なく遭遇しまして、事例が事例なもんで、検索しても出てこず四苦八苦してました。その中でもなにやら

  • フォトストリームに上がった写真ならOK
  • カメラを傾けて撮った写真で発生
  • どうやら写真の大きさとEXIFに問題があるようだ

というところまで、分かったところで、上のリンクを教えてもらった。

そういうわけで、このリンクを教えてもらったときにライフが限りなく0だったので、自分で実装することはせずに、stomitaさんの

を使いました。

ただまあパズドラ風アイコンジェネレーターみたいな実装にしていると、画像をCanvasに描く位置とかサイズまで指定してたりするので、ひとまずmegapixel-imagefileで、正しい画像に変換してから、その画像ソースをアイコンCanvasに渡すようにしました。

500KB超えたらnew MegaPixImageで処理する。終わったらアイコンの処理へ移動。new MegaPixImagerenderを実行してから、渡した画像オブジェクトにsrcが渡るまでには少しラグがあるので、待つようにしてる。この辺りdeferredとかイベント周りが整備されていればもうちょっと使いやすかったので、後でやってみる。

_prepareIcon: ->
  img = new Image()
  file = @model.get('file')

  # iPhoneで撮影した画像で500KBを超えているもの
  # 別途処理する、ついでにOrientationも正しい値にしておく
  if ( 500000 < parseInt(file?.size, 10) )
    new MegaPixImage(file).render(img, { maxWidth: 1280, orientation: @model.get('orientation') });

    (chk = =>
      if ( img.src )
        return @_loadImage(img.src, 'icon')
      setTimeout(chk, 100)
    )()
  else
    @_loadImage(@model.get('iconSrc'), 'icon')

途中にある、orientationは、

を使って、前もって取得してあるものを使ってます。アイコンに使う画像ファイルが選択されたら、実行されるメソッド内で取得して、modelにセットしておく。

_onChangeFile: (ev) ->
  $file = $(ev.target).closest('input')
  file = $file.get(0).files[0]

  # EXIFから正しいOrientationを取得しておく
  EXIF.getData(file, =>
    @model.set('orientation', EXIF.getTag(file, 'Orientation'))
  )

  @model.set('selected', true)
  @model.set('file', file)
  @reader.readAsDataURL(file)

ところで、orientationの値は数値になってて、何を意味してるのかさっぱり分からなかったんだけど、

このようになっているようです。

ひとまず画像がえらいちっさくなってしまうバグにも対応できて、無事リリースできました。まだちょっと既知のバグはあるのと、しょっぱなうんことか言ってしまったAndroidも保証はしないけど、ある程度までは動くようにしないとだな・・・手元の2.3でさっぱり動かない\(^o^)/

追記

そもそもAndroid2.3系はFile APIに対応してなかったのでどうしようもなかった。

関連エントリー

パズドラ風アイコンジェネレーター ver.2 #Webフォント編

7月時点ではiPhoneアプリにしたいなと思ってたんだけども、Canvas使ってプラスとレベル表記乗せたらおもろいということに気づきまして、結局Webアプリのパズドラ風アイコンジェネレーターをアップデートしました。

変更点は以下の通りでやんす。

  • UIの改善、デザインの変更
  • iOS6以降に最適化(SafariFirefoxの最新版にも対応
  • 画像のズーム、移動に対応
  • プラス、レベル表記を追加可能に
  • FONTPLUSのWebフォントを使ってみた

以前のアプリは、なんだかんだいって@rewishが作ったものを、ちょちょっと変えただけのもので、@rewishが作ったといっても過言ではない。というか@rewishが作ったんだし。

今回はUIも大幅に変えたいなと思っていたので、デザインは@shimaelにお願いして、フロントエンドは(サーバーそもそもないけど)全部作り直した。

先に自分である程度まで動くように作ってたんだけど、もらったデザインだと全然適用できなくて、 結局またUI部分は作り直して、2回作った感ある。これは僕の作り方がよくなかったので、反省すべき。

目玉機能は、プラスとレベルの表記を載せられるようになったこと。普通にCanvasのfillText()で乗せてるだけなんだけど、フォントはパズドラで使われているKurokane EBを使いたかったので、FONTPLUSのKurokaneStd-EBを使ってる。

とまあ普通にWebフォント使ってますけど、このアプリは画像にWebフォントを載せて、ユーザーがダウンロードできる、という物なので、利用規約に反してないかというところが気になったのでした。

FONTPLUSに確認したところ、Webフォントを使ったインタラクティブなコンテンツという扱いになるようで、全然問題ないということでした。

ところで、FONTPLUSにあるKurokaneStd-EBは、パズドラで使われているものとは少し違って、よく見るとウェイトやカーニングが違うんですけど、その辺はまあ普通気が付かないし、これでいいや的な感じです。KurokaneStd-EBしかないし。

(とか思ってたら、さっそくきょうすけさんに気づかれたw)

FONTPLUSのスマートライセンスで契約すると、デフォルトでフォントのサブセットを動的に行ってくれるんだけど、Canvasで使うような場合、初期化時に、テキストデータがないのでサブセットに含まれてなくて、ハハハハみたいな状態になる。

なのでFONTPLUSにはAPIが用意されていて、これを使うと、必要なタイミングで、サブセットを作ったりできて大変便利でございました。

データ読み込み後に、非同期モードにしておいて、

<script src="http://webfont.fontplus.jp/accessor/script/fontplus.js?El7q9AJq3dU%3D" charset="utf-8"></script>
<script>
  (function(FONTPLUS) {
    // 非同期モード
    FONTPLUS.async()
  }(this.FONTPLUS));
</script>

こんな風に初期化時に、必要なテキストでサブセット作ってます。

webfontText = 'Lv.0123456789最大+ダウンロード'

# KurokaneStd-EBサブセットの読み込み
FONTPLUS.load([
  {
    fontname: 'KurokaneStd-EB'
    # font-family: kurokane で利用できる
    nickname: 'kurokane'
    # 'aAあ': http://pr.fontplus.jp/api/
    text: webfontText + 'aAあ'
  }
], ->
  view = new PIG.View.App()
)

そういう感じで、FONTPLUSけっこうイケてる。みんなも使おう!
あ、有料です。僕が契約したのは、月or10万PVまで1000円のやつ。

次は実装面でハマったところなど書けたらかく。

関連エントリー

Backbone.jsでタッチイベント周りの処理

eventsの遅延評価で、イベント部分を分ける。対象メソッドは共通にしておく。メソッド内で必要になる位置関連のメソッドも用意しておいて、どちらであっても問題なく取得できるようにしておく。

タッチイベント周りと書いたけど、下記のコードはタッチで何らかの対象をドラッグできるようにする想定のコード。

class TouchView extends Backbone.View
  events: do ->
    # モバイル振り分けの処理を用意しておく
    if ( isMobile() )
      events = {
        'touchstart canvas' : '_onDragStart'
        'touchmove  canvas' : '_onDragMove'
        'touchend   canvas' : '_onDragEnd'
      }
    else
      events = {
        'mousedown  canvas' : '_onDragStart'
        'mousemove  canvas' : '_onDragMove'
        'mouseup    canvas' : '_onDragEnd'
      }

    # 共通のイベント
    return _.extend(events, {
      'click' : '_onClick'
    })

  initialize: ->
    @dragstart = false

  _getClientPos: (ev) ->
    ev = ev.originalEvent
    touches = ev.touches
    # タッチイベントの場合
    if ( touches )
      return {
        x: touches[0].clientX
        y: touches[0].clientY
      }
    # 通常のイベントの場合
    else
      return {
        x: ev.clientX
        y: ev.clientY
      }

  _onDragStart: (ev) ->
    ev.preventDefault()

    @dragStart = true
    @dragStartPos = {
      x: 0
      y: 0
    }
    @dragStartEv = @_getClientPos(ev)

  _onDragMove: (ev) ->
    ev.preventDefault()

    if ( not @dragStart )
      return

    dragEndPos = @_getClientPos(ev)

   # これが前回との差
    dragDiff = {
      x: dragEndPos.x - @dragStartEv.x
      y: dragEndPos.y - @dragStartEv.y
    }

    # dragDiffなどを使って何らかの処理をする

    @dragStartEv = dragEndPos

  _onDragEnd: (ev) ->
    ev.preventDefault()

    if ( not @dragStart )
      return

    @dragStart = false

CoffeeScriptではNamed functionが使えない

ふーむなるほどー使えないんですなー
検索したら出てきた笑ったやつ↓

Named function in CoffeeScript - Gists - GitHub

こういうのどう書くんだろうと思ったら

(function foo() {
  bar();
  setTimeout(foo, 100);
}());

CoffeeScriptだと、こうするようだ

(foo = ->
  bar()
  setTimeout foo, 100
)()

ちなみに戻すとこうなる

var foo;

(foo = function() {
  bar();
  return setTimeout(foo, 100);
})();

なるほどのー

jQueryのajaxError

気がつけば2ヶ月以上ブログを書いていなかった・・・
マメ男にはなれなさそうです

ずっとバカの一つ覚えみたいに

$('body').ajaxError(function() {
  foo();
});

としてましたけど、ajaxErrorが1.8系からdocumentにしか使えなくなっていた

$(document).ajaxError(function() {
  foo();
});

同じプロジェクトでやってるとそういうの追わなくなるな・・・