読者です 読者をやめる 読者になる 読者になる

読み書きプログラミング

日常のプログラミングで気づいたことを綴っています

Siphonアップデート

CSS CoffeeScript

Siphonのキートップデザインにこだわってみました。
http://safari-park.herokuapp.com/siphon/index.html

CSSでここまでできるんですね。

"STEVE JOBS"の中で、JobsさんがMacintoshのウィンドウの角を丸くすることをチームにすごい迫力で説明するシーンがありました。「そこから始まった」感を改めて感じます。

参考


キーの状態遷移のリファクタリングも行いました。
MVC, Observer, Choosing Method使うと、ステートを持つ可視オブジェクトは気持ちよく書けますね。

# software key with upper flick
#
# usage:
#  1. prepare keys in an HTML file like the below
#    <div title="+">+
#        <div title="-">-</div>
#    </div>
#    "title" property is used for output at key release.
#    The child element is a second key, so basically invisible.
#    you need to setup such things with layout in CSS.
#  2. create instances of KeyFSM.
#     Lazy assignment may be good.
#  3. call touchStart, touchMove, and touchEnd in corresponding
#     EventListeners respectively.
#
# parameters:
#  KeyFSM.holdTime: hold time to activate subkey
#
# implementation:
#  Using MVC pattern, KeyFSM is Model, DOM is V, and KeyState is C.
#  KeyState uses "Choosing Method."
#  KeyFSM uses Observer pattern.
#  There is a single controller using four instances of KeyState.
#  So updating method for each key is one kind. that is kind of restriction.

# model class
class KeyFSM
    constructor: (@state, @observer) ->
        @startX = 0
        @startY = 0
        @timer = null

    holdTime: 400 # ms

    setState: (state) ->
        @state = state
        @changed()

    subkey: -> if @observer.childNodes.length >= 2
            @observer.childNodes[1]
        else
            null

    changed: ->
        @state.update(@observer, @subkey())

    clearTimer: ->
        clearTimeout @timer if @timer?
        @timer = null

    touchStart: (startX, startY) ->
        keySound.play()
        @startX = startX
        @startY = startY
        @setState keyActive
        context = this
        @timer = setTimeout(=>
                @setState keySubActive
            , @holdTime) if @subkey()?

    touchMove: (event) ->
        touchPoint = event.targetTouches[0]
        moveX = touchPoint.pageX - @startX
        moveY = touchPoint.pageY - @startY
        @state.touchMove(this, moveX, moveY)

    touchEnd: -> @state.touchEnd this

# controller class to instantiate each state of a key.
class KeyState
    constructor: ->

    update: (main, sub) ->

    touchMove: (fsm, moveX, moveY) ->

    touchEnd: (fsm) ->

    inRange: (moveX, moveY) -> -58 < moveX < 58 and -58*2 < moveY < 58

# inactive state
keyInactive = new KeyState()

keyInactive.update = (main, sub) ->
    sub.style.display = 'none' if sub?

# active state
keyActive = new KeyState()

keyActive.update = (main, sub) ->
    main.style.backgroundColor = '#a0a0a0'

keyActive.touchMove = (fsm, moveX, moveY) ->
    if fsm.subkey()? and moveY < -30 and -30 < moveX < 30
        fsm.clearTimer()
        fsm.setState keySubActive
    else if not @inRange(moveX, moveY)
        fsm.clearTimer()
        fsm.setState keyInactive

keyActive.touchEnd = (fsm) ->
    fsm.clearTimer()
    stringInput fsm.observer.title
    fsm.setState keyInactive
    compileSource()

# subkey active state
keySubActive = new KeyState()

keySubActive.update = (main, sub) ->
    $(sub).css('color', '#fff')
    $(sub).css('background-image', '-webkit-gradient(linear, left top, left bottom, from(rgb(65,134,245)), to(rgb(25,79,220)))')
    sub.style.display = 'block'

keySubActive.touchMove = (fsm, moveX, moveY) ->
    if not @inRange(moveX, moveY)
        fsm.setState keySubInactive

keySubActive.touchEnd = (fsm) ->
    stringInput fsm.subkey().title
    fsm.setState keyInactive
    compileSource()

# subkey inactive state
keySubInactive = new KeyState()

keySubInactive.update = (main, sub) ->
    $(sub).css('color', '#000')
    $(sub).css('background-image', '-webkit-gradient(linear, left top, left bottom, from(#EEEEF0), to(#D2D2D8))')

keySubInactive.touchMove = (fsm, moveX, moveY) ->
    fsm.setState keySubActive if @inRange(moveX, moveY)

keySubInactive.touchEnd = (fsm) ->
    fsm.setState keyInactive