Skip to content

Instantly share code, notes, and snippets.

@elliottkember
Last active August 29, 2015 14:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elliottkember/a31e2cf1d131c260fa1e to your computer and use it in GitHub Desktop.
Save elliottkember/a31e2cf1d131c260fa1e to your computer and use it in GitHub Desktop.
A configurator for specifying properties on Framer layers! Example: http://elliott-prototypes.s3.amazonaws.com/Configurator.framer/index.html

This is a simple configurator widget for specifying whatever you like.

It would be nice to associate these values strongly with object attributes, but I am not smart enough to do two-way bindings.

new Configurator

  showAnimations:
    value: true

  animationType:
    value: 'a'
    options: ['a','b','c']

  x:
    object: imageLayer
    property: 'x'
  y:
    object: imageLayer
    property: 'y'
  z:
    object: imageLayer
    property: 'z'
  scale:
    object: imageLayer
    property: 'scale'
    range: [0..2]
  velocity:
    object: imageLayer
    property: 'animationOptions.spring.velocity'
    range: [1..500],
    value: 500
    change: (value) =>
      imageLayer.states.animationOptions =
        curve: "spring(#{configurator.values.velocity}, #{configurator.values.tension}, #{configurator.values.friction})"
  tension:
    range: [1..255],
    value: 12
    change: (value) =>
      imageLayer.states.animationOptions =
        curve: "spring(#{configurator.values.velocity}, #{configurator.values.tension}, #{configurator.values.friction})"
  friction:
    range: [1..255]
    value: 0
    change: (value) =>
      imageLayer.states.animationOptions =
        curve: "spring(#{configurator.values.velocity}, #{configurator.values.tension}, #{configurator.values.friction})"
Layer.prototype.destroyRecursively = ->
layer.destroyRecursively() for layer in @subLayers
@destroy()
class Configurator
constructor: (options) ->
@values = {}
@options = options
@callbacks = {}
@_createLayer()
@_draw()
destroy: ->
@layer.destroyRecursively()
delete this
# this = null
_createLayer: ->
@layer = new Layer x: 20, y: 20, width: 260, height: 400, backgroundColor: 'rgba(79, 79, 79, 0.87)'
@layer.draggable.enabled = true
@layer.on Events.TouchStart, (e) => @layer.draggable.enabled = e.offsetY < 60
@layer.on Events.DragEnd, (e) => @layer.draggable.enabled = true
@layer.style =
"border": "1px solid rgba(86, 86, 86, 0.87)"
"-webkit-border-radius": "6px"
"-moz-box-shadow": "0 2px 7px rgba(0, 0, 0, 0.81)"
"box-shadow": "0 2px 7px rgba(0, 0, 0, 0.81)"
"border-bottom": "1px solid rgba(66, 66, 66, 0.87)"
"border-top": "none"
"cursor": "default"
"pointer-events": "auto"
"max-height": "400px"
"padding-bottom": "10px"
@content = new Layer superLayer: @layer, y: 0, height: 350, width: @layer.width
@content.style =
"width": "100%"
"background-color": "transparent"
"height": "100%"
"padding-bottom": "1px"
"z-index": "1000"
"overflow-x": "none"
"overflow-y": "auto"
"pointer-events": "none"
"-webkit-border-radius": "6px"
"padding-bottom": "5px"
@header = new Layer x: 0, y: 0, width: @layer.width - 2, height: 20, superLayer: @layer
@header.style =
"position": "relative"
"top": "0px"
"box-shadow": "none"
"font": "11px/15px 'Lucida Grande'"
"border-top": "1px solid rgba(151, 151, 151, 0.92)"
"height": "20px"
"-webkit-border-top-left-radius": "6px"
"-webkit-border-top-right-radius": "6px"
"-webkit-border-bottom-left-radius": "0px"
"-webkit-border-bottom-right-radius": "0px"
"background-image": "-webkit-gradient(linear, left top, left bottom, from(rgba(108, 108, 108, 0.9)), color-stop(0.5, rgba(87, 87, 87, 0.88)), color-stop(0.5, rgba(75, 75, 75, 0.86)), to(rgba(75, 75, 75, 0.86)))"
@close = new Layer x: 4, y: 2, width: 13, height: 13, superLayer: @header, image: 'images/close.png'
@close.style =
"border-radius": "10px"
"opacity": 1
"background-color": "black"
"width": "13px"
"height": "13px"
"display": "block"
"position": "absolute"
"margin-top": "1px"
"-webkit-transition-duration": "0.1s"
"-webkit-transition-property": "opacity"
"cursor": "pointer"
@close.on Events.Click, =>
@layer.animateStop()
@close.animateStop()
if @layer.height == 20
@close.animate time: 0.1, properties: rotationZ: 0
@layer.animate time: 0.2, properties: height: @oldHeight
@_draw()
else
@oldHeight ||= @layer.height
@close.animate time: 0.1, properties: rotationZ: -45
@layer.animate time: 0.2, properties: height: 20
@layer
_line: (options, name) ->
l = new Layer width: @layer.width, height: 24, backgroundColor: 'transparent'
l.on Events.TouchStart, (e) -> e.stopPropagation()
l._element.className += " row"
l.style =
"pointer-events": "auto"
"font": 'normal 11px/18px "Lucida Grande"'
"color": "rgba(255, 255, 255, 0.85)"
"-webkit-transition-duration": "0.07s"
"-webkit-transition-property": "background"
"font-size": "12px"
"border-bottom": "1px solid rgba(255, 255, 255, 0.1)"
"padding-bottom": "4px"
"padding-left": "10px"
html = $("<div><label style='width: 100px; display: inline-block'>#{name}:</label></div>")
# Range slider
if options.range or options.object && options.property
options.range ||= [0..255]
max = options.range[options.range.length - 1]
if !options.property
if options.value
value = options.value
else
value = options.object[options.property]
max = value * 2 if value > max
html.append $ "<input step='0.01' type='range' value='#{value}' min='#{options.range[0]}' max='#{max}'>"
# Boolean
else if options.value in [true, false]
html.append $ "<input checked='#{options.value}' type='checkbox' style='position: relative; top: 0px' />"
# Select
else if options.options
html.append $ "<select>#{name}</select>"
for option in options.options
$("select", html).append $("<option>#{option}</option>")
if options.object && options.property
options.object?.on "change:#{options.property}", (value) =>
$("input, select", html).val value
_this = this
$("input, select", html).on "input change", () ->
val = parseFloat $(this).val()
options.change? _this.values[name] = val
options.object?[options.property] = val if options.object && options.property
callback?(val) for callback in _this.callbacks["change:#{name}"]?
$(l._element).append html
l
_draw: () ->
options = @options
line = false
for layer in @content.subLayers
layer.removeAllListeners()
layer.destroyRecursively()
lines = Object.keys(options)
for name, i in lines
do (name) =>
line = @_line(options[name], name)
line.y = i * line.height + @header.height + 5
line.superLayer = @content
if i == lines.length - 1
line.style.borderBottom = 'none'
@layer.height = lines.length * line.height + @header.height + 6
@content.height = lines.length * line.height + @header.height + 100
on: (events, callback) ->
events = _.flatten [events]
for eventName in events
for event in eventName.split(" ")
console.log "Adding a #{event} callback"
(@callbacks[event] ||= []).push callback
exports = window if typeof exports == 'undefined'
exports.Configurator = Configurator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment