Permalink
Browse files

Merge pull request #475 from koenbok/slider-update

RangedSliderComponent
  • Loading branch information...
benjamindenboer committed Mar 2, 2017
2 parents b9bddcb + e939f19 commit 82f1159b15b765551f6decfe3fa954b107544ffd
@@ -0,0 +1,348 @@
Utils = require "../Utils"
{Layer} = require "../Layer"
{Events} = require "../Events"
"""
RangedSliderComponent
minKnob <layer>
maxKnob <layer>
knobSize <width, height>
fill <layer>
min <number>
max <number>
minValue <number>
maxValue <number>
pointForValue(<n>)
valueForPoint(<n>)
animateToMinValue(value, animationOptions={})
animateToMaxValue(value, animationOptions={})
"""
Events.SliderValueChange = "sliderValueChange"
Events.SliderMinValueChange = "sliderMinValueChange"
Events.SliderMaxValueChange = "sliderMaxValueChange"
class Knob extends Layer
constructor: (options={}) ->
_.defaults options,
backgroundColor: "#fff"
shadowY: 2
shadowBlur: 4
shadowColor: "rgba(0, 0, 0, 0.3)"
super options
class exports.RangedSliderComponent extends Layer
constructor: (options={}) ->
_.defaults options,
backgroundColor: "#ccc"
borderRadius: 50
clip: false
width: 300
height: 10
value: 0
knobSize: 30
# Set some sensible default for the hit area
options.hitArea ?= options.knobSize
@minKnob = new Knob
name: "minKnob"
size: @knobSize or 30
@maxKnob = new Knob
name: "maxKnob"
size: @knobSize or 30
@fill = new Layer
backgroundColor: "#333"
width: 0
force2d: true
name: "fill"
@sliderOverlay = new Layer
backgroundColor: null
name: "sliderOverlay"
super options
# Set fill initially
if @width > @height
@fill.height = @height
else
@fill.width = @width
@fill.borderRadius = @sliderOverlay.borderRadius = @borderRadius
@knobSize = options.knobSize
@_styleKnob(@minKnob)
@_styleKnob(@maxKnob)
@_updateFrame()
@_updateFill()
@_updateKnob()
@on("change:frame", @_updateFrame)
@on("change:borderRadius", @_setRadius)
for knob in [@minKnob, @maxKnob]
knob.on("change:size", @_updateKnob)
knob.on("change:frame", @_updateFill)
knob.on("change:frame", @_knobDidMove)
knob.on("change:frame", @_updateFrame)
@sliderOverlay.on(Events.TapStart, @_touchStart)
@sliderOverlay.on(Events.TapEnd, @_touchEnd)
_touchStart: (event) =>
event.preventDefault()
offsetX = (@min / @canvasScaleX()) - @min
offsetY = (@min / @canvasScaleY()) - @min
if @width > @height
clickedValue = @valueForPoint(Events.touchEvent(event).clientX - @screenScaledFrame().x) / @canvasScaleX() - offsetX
if clickedValue > @maxValue
@maxValue = clickedValue
@maxKnob.draggable._touchStart(event)
if clickedValue < @minValue
@minValue = clickedValue
@minKnob.draggable._touchStart(event)
else
clickedValue = @valueForPoint(Events.touchEvent(event).clientY - @screenScaledFrame().y) / @canvasScaleY() - offsetY
if clickedValue > @maxValue
@maxValue = clickedValue
@maxKnob.draggable._touchStart(event)
if clickedValue < @minValue
@minValue = clickedValue
@minKnob.draggable._touchStart(event)
@_updateValue()
_touchEnd: (event) =>
@_updateValue()
_styleKnob: (knob) =>
knob.parent = @fill.parent = @sliderOverlay.parent = @
knob.borderRadius = @knobSize / 2
_.extend knob.draggable,
enabled: true
overdrag: false
momentum: true
bounce: false
momentumOptions: {friction: 5, tolerance: 0.25}
_updateFill: =>
if @width > @height
@fill.x = @minKnob.midX
@fill.width = @maxKnob.midX - @minKnob.midX
else
@fill.y = @minKnob.midY
@fill.height = @maxKnob.midY - @minKnob.midY
_updateKnob: =>
if @width > @height
@minKnob.midX = @fill.x
@minKnob.centerY()
@maxKnob.midX = @fill.x + @fill.width
@maxKnob.centerY()
else
@minKnob.midY = @fill.y
@minKnob.centerX()
@maxKnob.midY = @fill.y + @fill.height
@maxKnob.centerX()
_updateFrame: =>
@minKnob.draggable.constraints =
x: -@minKnob.width / 2
y: -@minKnob.height / 2
width: @maxKnob.midX
height: @maxKnob.midY
@maxKnob.draggable.constraints =
x: @minKnob.maxX
y: @minKnob.maxY
width: @width + @maxKnob.width
height: @height + @maxKnob.height
@hitArea = @hitArea
if @width > @height
@fill.height = @height
@minKnob.midX = @pointForValue(@minValue)
@maxKnob.midX = @pointForValue(@maxValue)
@minKnob.centerY()
else
@fill.width = @width
@minKnob.midY = @pointForValue(@minValue)
@maxKnob.midY = @pointForValue(@maxValue)
@minKnob.centerX()
if @width > @height
for knob in [@minKnob, @maxKnob]
knob.draggable.speedY = 0
knob.draggable.speedX = 1
else
for knob in [@minKnob, @maxKnob]
knob.draggable.speedX = 0
knob.draggable.speedY = 1
@sliderOverlay.center()
_setRadius: =>
radius = @borderRadius
@fill.style.borderRadius = "#{radius}px 0 0 #{radius}px"
@define "constrained", @simpleProperty("constrained", false)
@define "knobSize",
get: -> @_knobSize
set: (value) ->
for knob in [@minKnob, @maxKnob]
isRound = knob.borderRadius * 2 is @_knobSize
@_knobSize = value
knob.size = @_knobSize
knob.borderRadius = @_knobSize / 2 if isRound
@_updateFrame()
@define "hitArea",
get: ->
@_hitArea
set: (value) ->
@_hitArea = value
if @width > @height
@sliderOverlay.width = @width + @hitArea
@sliderOverlay.height = @hitArea
else
@sliderOverlay.width = @hitArea
@sliderOverlay.height = @height + @hitArea
@define "min",
get: -> @_min or 0
set: (value) -> @_min = value if _.isFinite(value)
@define "max",
get: -> @_max or 1
set: (value) -> @_max = value if _.isFinite(value)
@define "minValue",
get: -> @_minValue or 0
set: (value) ->
return unless _.isFinite(value)
@_minValue = value
if @width > @height
@minKnob.midX = @pointForValue(value)
else
@minKnob.midY = @pointForValue(value)
@_updateFill()
@_updateValue()
@define "maxValue",
get: -> @_maxValue or 0.5
set: (value) ->
return unless _.isFinite(value)
@_maxValue = value
if @width > @height
@maxKnob.midX = @pointForValue(value)
else
@maxKnob.midY = @pointForValue(value)
@_updateFill()
@_updateValue()
_knobDidMove: =>
if @width > @height
@minValue = @valueForPoint(@minKnob.midX)
@maxValue = @valueForPoint(@maxKnob.midX)
else
@minValue = @valueForPoint(@minKnob.midY)
@maxValue = @valueForPoint(@maxKnob.midY)
_updateValue: =>
@emit(Events.SliderValueChange)
@emit(Events.SliderMinValueChange, @minValue)
@emit(Events.SliderMaxValueChange, @maxValue)
# Retrieve the point (x or y coordinate) of a certain numeric value.
pointForValue: (value) ->
for knob in [@minKnob, @maxKnob]
# For horizontal (default) sliders.
if @width > @height
if @constrained
return Utils.modulate(value, [@min, @max], [0 + (knob.width / 2), @width - (knob.width / 2)], true)
else
return Utils.modulate(value, [@min, @max], [0 , @width], true)
# For vertical sliders.
else
if @constrained
return Utils.modulate(value, [@min, @max], [0 + (knob.height / 2), @height - (knob.height / 2)], true)
else
return Utils.modulate(value, [@min, @max], [0, @height], true)
# Retrieve the numeric value of a certain point (x or y coordinate).
valueForPoint: (value) ->
for knob in [@minKnob, @maxKnob]
# For horizontal (default) sliders.
if @width > @height
if @constrained
return Utils.modulate(value, [0 + (knob.width / 2), @width - (knob.width / 2)], [@min, @max], true)
else
return Utils.modulate(value, [0, @width], [@min, @max], true)
# For vertical sliders.
else
if @constrained
return Utils.modulate(value, [0 + (knob.height / 2), @height - (knob.height / 2)], [@min, @max], true)
else
return Utils.modulate(value, [0, @height], [@min, @max], true)
animateToMinValue: (value, animationOptions={curve: "spring(250, 25, 0)"}) ->
return unless _.isFinite(value)
if @width > @height
animationOptions.properties = {x: @pointForValue(value) - (@minKnob.width/2)}
else
animationOptions.properties = {y: @pointForValue(value) - (@minKnob.height/2)}
@minKnob.animate(animationOptions)
animateToMaxValue: (value, animationOptions={curve: "spring(250, 25, 0)"}) ->
return unless _.isFinite(value)
if @width > @height
animationOptions.properties = {x: @pointForValue(value) - (@maxKnob.width/2)}
else
animationOptions.properties = {y: @pointForValue(value) - (@maxKnob.height/2)}
@maxKnob.animate(animationOptions)
##############################################################
## EVENT HELPERS
onValueChange: (cb) -> @on(Events.SliderValueChange, cb)
onMinValueChange: (cb) -> @on(Events.SliderMinValueChange, cb)
onMaxValueChange: (cb) -> @on(Events.SliderMaxValueChange, cb)
@@ -45,8 +45,9 @@ class exports.SliderComponent extends Layer
@knob = new Knob
backgroundColor: "#fff"
shadowY: 1, shadowBlur: 3
shadowColor: "rgba(0, 0, 0, 0.35)"
shadowY: 2
shadowBlur: 4
shadowColor: "rgba(0, 0, 0, 0.3)"
name: "knob"
@fill = new Layer
View
@@ -24,6 +24,7 @@ Framer.print = (require "./Print").print
Framer.ScrollComponent = (require "./Components/ScrollComponent").ScrollComponent
Framer.PageComponent = (require "./Components/PageComponent").PageComponent
Framer.SliderComponent = (require "./Components/SliderComponent").SliderComponent
Framer.RangedSliderComponent = (require "./Components/RangedSliderComponent").RangedSliderComponent
Framer.DeviceComponent = (require "./Components/DeviceComponent").DeviceComponent
Framer.GridComponent = (require "./Components/GridComponent").GridComponent
Framer.FlowComponent = (require "./Components/FlowComponent").FlowComponent
View
@@ -44,6 +44,7 @@ require "./tests/VersionTest"
require "./tests/ColorTest"
require "./tests/DeviceComponentTest"
require "./tests/SliderComponentTest"
require "./tests/RangedSliderComponentTest"
require "./tests/FlowComponentTest"
mocha.run()
Oops, something went wrong.

0 comments on commit 82f1159

Please sign in to comment.