Improve clock styles and behavior
parent
14e1f85bff
commit
45a26ea682
|
@ -4,9 +4,9 @@ module.exports = React.createClass
|
|||
|
||||
# -- States & Properties
|
||||
getDefaultProps: ->
|
||||
className : ''
|
||||
numbers : []
|
||||
radius : 0
|
||||
numbers : []
|
||||
radius : 0
|
||||
activeNumber : null
|
||||
|
||||
# -- Internal methods
|
||||
_numberStyle: (radius, num) ->
|
||||
|
@ -20,10 +20,11 @@ module.exports = React.createClass
|
|||
|
||||
# -- Render
|
||||
render: ->
|
||||
<div ref="root" className={"#{@props.className} #{css.face}"} style={@_faceStyle()}>
|
||||
<div ref="root" className={css.face} style={@_faceStyle()}>
|
||||
{ for i, k in @props.numbers
|
||||
<span className={css.number}
|
||||
key={i} style={@_numberStyle(@props.radius - @props.spacing, k + 1)}>
|
||||
<span className={css.number + (if parseInt(i) == @props.activeNumber then ' active' else '')}
|
||||
style={@_numberStyle(@props.radius - @props.spacing, k + 1)}
|
||||
key={i} >
|
||||
{i}
|
||||
</span> }
|
||||
</div>
|
||||
|
|
|
@ -5,25 +5,31 @@ module.exports = React.createClass
|
|||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
initialAngle : React.PropTypes.number
|
||||
className : React.PropTypes.string
|
||||
onHandChange : React.PropTypes.func
|
||||
onHandMoved : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
angle : 0
|
||||
className : ''
|
||||
initialAngle : 0
|
||||
length : 0
|
||||
origin : {}
|
||||
|
||||
getInitialState: ->
|
||||
angle : @props.angle
|
||||
angle : @props.initialAngle
|
||||
knobWidth : 0
|
||||
radius : 0
|
||||
|
||||
# -- Lifecycle
|
||||
componentDidMount: ->
|
||||
@setState
|
||||
knobWidth : @refs.knob.getDOMNode().offsetWidth
|
||||
@setState knobWidth: @refs.knob.getDOMNode().offsetWidth
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
@props.onHandChange(nextState.angle)
|
||||
if nextState.angle != @state.angle ||
|
||||
nextProps.length != @props.length &&
|
||||
@props.length != 0
|
||||
@props.onHandChange(nextState.angle)
|
||||
|
||||
# -- Event handlers
|
||||
_onKnobMouseDown: ->
|
||||
|
@ -40,6 +46,9 @@ module.exports = React.createClass
|
|||
newDegrees = if newDegrees == 360 then 0 else newDegrees
|
||||
@setState(angle: newDegrees) if @state.angle != newDegrees
|
||||
|
||||
onMouseUp: ->
|
||||
@_end(@_getMouseEventMap())
|
||||
|
||||
# -- Internal methods
|
||||
_getPositionRadius: (position) ->
|
||||
x = @props.origin.x - position.x
|
||||
|
@ -52,10 +61,8 @@ module.exports = React.createClass
|
|||
_positionToAngle: (position) ->
|
||||
_angle360(@props.origin.x, @props.origin.y, position.x, position.y)
|
||||
|
||||
onMouseUp: ->
|
||||
@_end(@_getMouseEventMap())
|
||||
|
||||
_end: (events) ->
|
||||
@props.onHandMoved() if @props.onHandMoved
|
||||
_removeEventsFromDocument(events)
|
||||
|
||||
# -- Render
|
||||
|
@ -63,7 +70,7 @@ module.exports = React.createClass
|
|||
style = prefixer.transform("rotate(#{@state.angle}deg)")
|
||||
style.height = @props.length - @state.knobWidth/2
|
||||
|
||||
<div className={css.hand} style={style}>
|
||||
<div className={css.hand + ' ' + @props.className} style={style}>
|
||||
<div ref='knob' className={css.knob} onMouseDown={@_onKnobMouseDown}></div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
css = require './style'
|
||||
Face = require './face'
|
||||
Hand = require './hand'
|
||||
|
||||
|
@ -6,59 +5,73 @@ module.exports = React.createClass
|
|||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
format : React.PropTypes.oneOf(['24hr', 'ampm'])
|
||||
onChange : React.PropTypes.func
|
||||
initialValue : React.PropTypes.number
|
||||
format : React.PropTypes.oneOf(['24hr', 'ampm'])
|
||||
onChange : React.PropTypes.func
|
||||
onHandMoved : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
format : '24hr'
|
||||
onChange : null
|
||||
initialValue : null
|
||||
format : '24hr'
|
||||
onChange : null
|
||||
|
||||
getInitialState: ->
|
||||
am : false
|
||||
inner : @props.format == '24hr' && 0 < @props.initialValue <= 12
|
||||
value : @props.initialValue || if @props.format == '24hr' then 0 else 12
|
||||
|
||||
# -- Lifecycle
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
@props.onChange(nextState.value) if nextState.value != @state.value && @props.onChange
|
||||
|
||||
# -- Events
|
||||
_onHandMouseMove: (radius) ->
|
||||
if @props.format == '24hr'
|
||||
currentAm = radius < @props.radius - @props.spacing * 2
|
||||
@setState am: currentAm if @state.am != currentAm
|
||||
currentInner = radius < @props.radius - @props.spacing * 2
|
||||
@setState inner: currentInner if @state.inner != currentInner
|
||||
|
||||
_onHandChange: (value) ->
|
||||
if @props.format == '24hr'
|
||||
values = if @state.am then AM_HOURS else PM_HOURS
|
||||
_onHandChange: (degrees) ->
|
||||
newValue = @_valueFromDegrees(degrees)
|
||||
@setState value: newValue if @state.value != newValue
|
||||
|
||||
# -- Internal Methods
|
||||
_valueFromDegrees: (degrees) ->
|
||||
if @props.format == 'ampm' || @props.format == '24hr' && @state.inner
|
||||
parseInt(INNER_NUMBERS[degrees/STEP])
|
||||
else
|
||||
values = AM_HOURS
|
||||
@props.onChange(parseInt(values[value/STEP])) if @props.onChange
|
||||
parseInt(OUTER_NUMBERS[degrees/STEP])
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
innerRadius = @props.radius - @props.spacing * 2
|
||||
handRadius = if @props.format == '24hr' && @state.am then innerRadius else @props.radius
|
||||
handRadius = if @state.inner then innerRadius else @props.radius
|
||||
handLength = handRadius - @props.spacing
|
||||
|
||||
<div>
|
||||
<Face
|
||||
className={css.outerSphere}
|
||||
numbers={if @props.format == '24hr' then PM_HOURS else AM_HOURS}
|
||||
numbers={if @props.format == '24hr' then OUTER_NUMBERS else INNER_NUMBERS}
|
||||
spacing={@props.spacing}
|
||||
radius={@props.radius} />
|
||||
radius={@props.radius}
|
||||
activeNumber={@state.value} />
|
||||
{
|
||||
if @props.format == '24hr'
|
||||
<Face
|
||||
className={css.innerSphere}
|
||||
numbers={AM_HOURS}
|
||||
numbers={INNER_NUMBERS}
|
||||
spacing={@props.spacing}
|
||||
radius={innerRadius} />
|
||||
radius={innerRadius}
|
||||
activeNumber={@state.value} />
|
||||
}
|
||||
<Hand
|
||||
degrees={0}
|
||||
degrees={@state.degrees}
|
||||
initialAngle={@props.initialValue * STEP}
|
||||
length={handLength}
|
||||
onHandMouseMove={@_onHandMouseMove}
|
||||
onHandMoved={@props.onHandMoved}
|
||||
onHandChange={@_onHandChange}
|
||||
origin={@props.center}
|
||||
step={STEP} />
|
||||
</div>
|
||||
|
||||
# -- Private constants
|
||||
AM_HOURS = [12].concat([1..11])
|
||||
PM_HOURS = ['00'].concat([13..23])
|
||||
STEP = 360/12
|
||||
INNER_NUMBERS = [12].concat([1..11])
|
||||
OUTER_NUMBERS = ['00'].concat([13..23])
|
||||
STEP = 360/12
|
||||
|
|
|
@ -7,13 +7,14 @@ module.exports = React.createClass
|
|||
# -- States & Properties
|
||||
propTypes:
|
||||
className : React.PropTypes.string
|
||||
mode : React.PropTypes.oneOf(['hours', 'minutes'])
|
||||
startMode : React.PropTypes.oneOf(['hours', 'minutes'])
|
||||
|
||||
getDefaultProps: ->
|
||||
className : ''
|
||||
mode : 'hours'
|
||||
startMode : 'hours'
|
||||
|
||||
getInitialState: ->
|
||||
mode : @props.startMode
|
||||
radius : 0
|
||||
|
||||
# -- Lifecycle
|
||||
|
@ -57,14 +58,16 @@ module.exports = React.createClass
|
|||
<div className={css.root}>
|
||||
<div ref="wrapper" className={css.wrapper} style={height: @state.radius * 2} >
|
||||
{
|
||||
if @props.mode == 'minutes'
|
||||
if @state.mode == 'minutes'
|
||||
<Minutes
|
||||
center={@state.center}
|
||||
onChange={@onMinuteChange}
|
||||
radius={@state.radius}
|
||||
spacing={@state.radius * 0.16} />
|
||||
else if @props.mode == 'hours'
|
||||
else if @state.mode == 'hours'
|
||||
<Hours
|
||||
format={'24hr'}
|
||||
initialValue={16}
|
||||
center={@state.center}
|
||||
onChange={@onHourChange}
|
||||
radius={@state.radius}
|
||||
|
|
|
@ -6,11 +6,23 @@ module.exports = React.createClass
|
|||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
onChange : React.PropTypes.func
|
||||
initialValue : React.PropTypes.number
|
||||
onChange : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
initialValue : 0
|
||||
onChange : null
|
||||
|
||||
getInitialState: ->
|
||||
value : @props.initialValue
|
||||
|
||||
# -- Events
|
||||
_onHandChange: (value) ->
|
||||
@props.onChange(parseInt(value/STEP)) if @props.onChange
|
||||
_onHandChange: (degrees) ->
|
||||
@setState value: parseInt(degrees/STEP)
|
||||
|
||||
# -- Internal methods
|
||||
_valueIsExactMinute: ->
|
||||
MINUTES.indexOf(('0' + @state.value).slice(-2)) != -1
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
|
@ -19,9 +31,12 @@ module.exports = React.createClass
|
|||
className={css.outerSphere}
|
||||
numbers={MINUTES}
|
||||
spacing={@props.spacing}
|
||||
radius={@props.radius} />
|
||||
radius={@props.radius}
|
||||
activeNumber={@state.value} />
|
||||
<Hand
|
||||
degrees={0}
|
||||
className={'small-knob' unless @_valueIsExactMinute()}
|
||||
initialAngle={@props.initialValue * STEP}
|
||||
length={@props.radius - @props.spacing}
|
||||
onHandChange={@_onHandChange}
|
||||
origin={@props.center}
|
||||
|
|
|
@ -1,68 +1,88 @@
|
|||
handWidth = 4px
|
||||
handDotSize = 6px
|
||||
knobSize = 34px
|
||||
@import '../constants'
|
||||
|
||||
NUMBER_SIZE = 20px
|
||||
HAND_WIDTH = 4px
|
||||
HAND_DOT_SIZE = 6px
|
||||
KNOB_SIZE = 34px
|
||||
SMALL_KNOB_SIZE = 14px
|
||||
|
||||
:local(.root)
|
||||
border: 1px solid pink
|
||||
padding: 20px
|
||||
padding : 20px
|
||||
|
||||
:local(.wrapper)
|
||||
position: relative
|
||||
background-color : DIVIDER
|
||||
border-radius : 50%
|
||||
position : relative
|
||||
|
||||
:local(.face)
|
||||
border-radius: 50%
|
||||
position: relative
|
||||
border-radius : 50%
|
||||
pointer-events : none
|
||||
position : relative
|
||||
z-index : Z_INDEX_HIGH
|
||||
|
||||
:local(.number)
|
||||
width: 20px
|
||||
text-align: center
|
||||
pointer-events: none
|
||||
height: 20px
|
||||
margin-left: -10px
|
||||
margin-top: -10px
|
||||
height : NUMBER_SIZE
|
||||
margin-left : -(NUMBER_SIZE/2)
|
||||
margin-top : -(NUMBER_SIZE/2)
|
||||
position : relative
|
||||
text-align : center
|
||||
width : NUMBER_SIZE
|
||||
|
||||
:local(.outerSphere), :local(.innerSphere)
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translateX(-50%) translateY(-50%)
|
||||
&.active
|
||||
color : WHITE
|
||||
|
||||
:local(.outerSphere)
|
||||
background: gray
|
||||
|
||||
:local(.innerSphere)
|
||||
background: yellow
|
||||
:local(.face)
|
||||
position : absolute
|
||||
top : 50%
|
||||
left : 50%
|
||||
transform : translateX(-50%) translateY(-50%)
|
||||
|
||||
:local(.hand)
|
||||
background-color : magenta
|
||||
background-color : ACCENT
|
||||
bottom : 50%
|
||||
display : block
|
||||
left : 50%
|
||||
margin-left : -(handWidth/2)
|
||||
opacity : .6
|
||||
margin-left : -(HAND_WIDTH/2)
|
||||
position : absolute
|
||||
transform-origin : 50% 100%
|
||||
width : handWidth
|
||||
width : HAND_WIDTH
|
||||
|
||||
&:before
|
||||
background-color : magenta
|
||||
background-color : ACCENT
|
||||
border-radius : 50%
|
||||
bottom : 0
|
||||
content : ''
|
||||
height : handDotSize
|
||||
height : HAND_DOT_SIZE
|
||||
left : 50%
|
||||
margin-bottom : -(handDotSize/2)
|
||||
margin-left : -(handDotSize/2)
|
||||
margin-bottom : -(HAND_DOT_SIZE/2)
|
||||
margin-left : -(HAND_DOT_SIZE/2)
|
||||
position : absolute
|
||||
width : handDotSize
|
||||
width : HAND_DOT_SIZE
|
||||
|
||||
&.small-knob :local(.knob)
|
||||
top : -(KNOB_SIZE/2)
|
||||
margin-top : -(SMALL_KNOB_SIZE/2)
|
||||
margin-left : -(SMALL_KNOB_SIZE/2)
|
||||
height : SMALL_KNOB_SIZE
|
||||
width : SMALL_KNOB_SIZE
|
||||
|
||||
&:before
|
||||
background : ACCENT
|
||||
bottom : -((KNOB_SIZE - SMALL_KNOB_SIZE)/2)
|
||||
content : ''
|
||||
height : ((KNOB_SIZE - SMALL_KNOB_SIZE)/2)
|
||||
left : 50%
|
||||
margin-left : -(HAND_WIDTH/2)
|
||||
position : absolute
|
||||
width : HAND_WIDTH
|
||||
|
||||
:local(.knob)
|
||||
background-color : magenta
|
||||
background-color : ACCENT
|
||||
border-radius : 50%
|
||||
cursor : pointer
|
||||
height : knobSize
|
||||
height : KNOB_SIZE
|
||||
left : 50%
|
||||
margin-left : -(knobSize/2)
|
||||
margin-left : -(KNOB_SIZE/2)
|
||||
position : absolute
|
||||
top : -(knobSize)
|
||||
width : knobSize
|
||||
top : -(KNOB_SIZE)
|
||||
width : KNOB_SIZE
|
||||
|
|
|
@ -50,3 +50,10 @@ ZDEPTH_SHADOW_5 = 0 19px 60px rgba(0,0,0,0.30), 0 15px 20px rgba(0,0
|
|||
ANIMATION_DURATION = 450ms
|
||||
ANIMATION_EASE = cubic-bezier(.55,0,.1,1)
|
||||
ANIMATION_DELAY = (ANIMATION_DURATION / 5)
|
||||
|
||||
// -- Z Indexes
|
||||
Z_INDEX_HIGHER = 200
|
||||
Z_INDEX_HIGH = 100
|
||||
Z_INDEX_NORMAL = 1
|
||||
Z_INDEX_LOW = -100
|
||||
Z_INDEX_LOWER = -200
|
||||
|
|
|
@ -3,6 +3,6 @@ Clock = require '../../components/clock'
|
|||
module.exports = React.createClass
|
||||
render: ->
|
||||
<div>
|
||||
<Clock mode="hours" />
|
||||
<Clock mode="minutes" />
|
||||
<Clock startMode="hours" />
|
||||
<Clock startMode="minutes" />
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue