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