Permalink
Browse files

Merge pull request #434 from koenbok/feature/text-layers

TextLayer
  • Loading branch information...
benjamindenboer committed Mar 7, 2017
2 parents db7aea1 + d40c3ab commit 4408f8943930c2d4d31fabe9d05e42bcd7a5737f
View
@@ -61,3 +61,4 @@ _projects
Icon
/extras/resources.framerjs.com/static/DeviceResources/.*
/yarn.lock
+/extras/Studio.framer/framer/.bookmark
Binary file not shown.
Binary file not shown.
Binary file not shown.
View
@@ -11,6 +11,7 @@ Framer._Layer = Framer.Layer # So it won't be overridden by MobileScrollFix
Framer.BackgroundLayer = (require "./BackgroundLayer").BackgroundLayer
Framer.VideoLayer = (require "./VideoLayer").VideoLayer
Framer.SVGLayer = (require "./SVGLayer").SVGLayer
+Framer.TextLayer = (require "./TextLayer").TextLayer
Framer.Events = (require "./Events").Events
Framer.Gestures = (require "./Gestures").Gestures
Framer.Animation = (require "./Animation").Animation
View
@@ -47,7 +47,10 @@ layerProperty = (obj, name, cssProperty, fallback, validator, transformer, optio
@_properties[name] = value
if cssProperty isnt null
- @_element.style[cssProperty] = LayerStyle[cssProperty](@)
+ if name is cssProperty and not LayerStyle[cssProperty]?
+ @_element.style[cssProperty] = @_properties[name]
+ else
+ @_element.style[cssProperty] = LayerStyle[cssProperty](@)
set?(@, value)
@@ -63,6 +66,8 @@ layerProperty = (obj, name, cssProperty, fallback, validator, transformer, optio
result = _.extend(result, options)
+exports.layerProperty = layerProperty
+
layerPropertyPointTransformer = (value, layer, property) ->
if _.isFunction(value)
value = value(layer, property)
View
@@ -168,6 +168,16 @@ exports.LayerStyle =
return "#{layer._properties.shadowX}px #{layer._properties.shadowY}px #{layer._properties.shadowBlur}px #{layer._properties.shadowSpread}px #{layer._properties.shadowColor}"
+ textShadow: (layer) ->
+
+ props = layer._properties
+
+ if not props.shadowColor
+ return ""
+ else if props.shadowX is 0 and props.shadowY is 0 and props.shadowBlur is 0 and props.shadowSpread is 0
+ return ""
+
+ return "#{layer._properties.shadowX}px #{layer._properties.shadowY}px #{layer._properties.shadowBlur}px #{layer._properties.shadowColor}"
backgroundColor: (layer) ->
return layer._properties.backgroundColor
@@ -185,3 +195,33 @@ exports.LayerStyle =
border: (layer) ->
return "#{layer._properties.borderWidth}px solid #{layer._properties.borderColor}"
+
+ fontSize: (layer) ->
+ return layer._properties.fontSize + "px"
+
+ letterSpacing: (layer) ->
+ return layer._properties.letterSpacing + "px"
+
+ wordSpacing: (layer) ->
+ return layer._properties.wordSpacing + "px"
+
+ textIndent: (layer) ->
+ return layer._properties.textIndent + "px"
+
+ textAlign: (layer) ->
+ value = layer._properties.textAlign
+ if value is Align.left
+ return "left"
+ if value is Align.center
+ return "center"
+ if value is Align.right
+ return "right"
+ else
+ return value
+
+ direction: (layer) ->
+ value = layer._properties.direction
+ switch value
+ when "right-to-left" then return "rtl"
+ when "left-to-right" then return "ltr"
+ else return value
View
@@ -0,0 +1,154 @@
+{Layer, layerProperty} = require "./Layer"
+{LayerStyle} = require "./LayerStyle"
+{Color} = require "./Color"
+{Events} = require "./Events"
+Utils = require "./Utils"
+
+class exports.TextLayer extends Layer
+
+ @_textProperties = [
+ "text"
+ "fontFamily"
+ "fontSize"
+ "fontWeight"
+ "fontStyle"
+ "lineHeight"
+ "letterSpacing"
+ "wordSpacing"
+ "textAlign"
+ "textTransform"
+ "textIndent"
+ "textDecoration"
+ "direction"
+ "font"
+ ]
+
+ @_textStyleProperties = _.pull(_.clone(TextLayer._textProperties), "text").concat(["color", "shadowX", "shadowY", "shadowBlur", "shadowColor"])
+
+ explicitWidth: false
+
+ constructor: (options={}) ->
+
+ _.defaults options, options.textStyle,
+ backgroundColor: "transparent"
+ html: "Add text"
+ color: "#888"
+ fontSize: 40
+ fontWeight: 400
+ lineHeight: 1.25
+
+ super options
+
+ # Set padding
+ @_padding = options.padding or Utils.rectZero()
+
+ # Keeps track if the width or height are explicitly set, so we shouldn't update it afterwards
+ @explicitWidth = options.width?
+ @explicitHeight = options.height?
+
+ # Reset width and height
+ @autoSize()
+
+ # Calculate new height on font property changes
+
+ for property in @constructor._textProperties
+ @on "change:#{property}", =>
+ @autoSize()
+
+ @on "change:size", @autoSize
+ @on "change:parent", @autoSize
+
+ @on "change:width", @updateExplicitWidth
+ @on "change:height", @updateExplicitHeight
+
+ @defaultFont: ->
+ # Android Device: Roboto
+ if Utils.isAndroid()
+ return "Roboto, Helvetica Neue"
+ # Edge Device: Segoe UI
+ if Utils.isEdge()
+ return "Segoe UI, Helvetica Neue"
+ # General default: macOS, SF UI
+ return "-apple-system, SF UI Text, Helvetica Neue"
+
+ autoSize: =>
+ constraints =
+ max: true
+ if @explicitWidth
+ constraints.width = @width
+ else
+ constraints.width = if @parent? then @parent.width else Screen.width
+
+ style = _.pick @style, @constructor._textProperties
+ size = Utils.textSize(@text, style, constraints)
+ newWidth = Math.ceil(size.width)
+ newHeight = Math.ceil(size.height)
+ @disableExplicitUpdating = true
+ @width = newWidth if @width isnt newWidth and not @explicitWidth
+ @height = newHeight if @height isnt newHeight and not @explicitHeight
+ @disableExplicitUpdating = false
+
+
+ updateExplicitWidth: (value) =>
+ return if @enableExplicitUpdating
+ @explicitWidth = true
+
+ updateExplicitHeight: (value) =>
+ return if @disableExplicitUpdating
+ @explicitHeight = true
+
+ @define "text",
+ get: -> @_element.textContent
+ set: (value) ->
+ @_element.textContent = value
+ @emit("change:text", value)
+
+ @define "padding",
+ get: ->
+ _.clone(@_padding)
+
+ set: (padding) ->
+ @_padding = Utils.rectZero(Utils.parseRect(padding))
+
+ # Top, Right, Bottom, Left
+ @style.padding =
+ "#{@_padding.top}px #{@_padding.right}px #{@_padding.bottom}px #{@_padding.left}px"
+
+ @define "fontFamily", layerProperty(@, "fontFamily", "fontFamily", @defaultFont(), _.isString)
+ @define "fontSize", layerProperty(@, "fontSize", "fontSize", null, _.isNumber)
+ @define "fontWeight", layerProperty(@, "fontWeight", "fontWeight")
+ @define "fontStyle", layerProperty(@, "fontStyle", "fontStyle", "normal", _.isString)
+ @define "lineHeight", layerProperty(@, "lineHeight", "lineHeight", null, _.isNumber)
+ @define "letterSpacing", layerProperty(@, "letterSpacing", "letterSpacing", null, _.isNumber)
+ @define "wordSpacing", layerProperty(@, "wordSpacing", "wordSpacing", null, _.isNumber)
+ @define "textAlign", layerProperty(@, "textAlign", "textAlign")
+ @define "textTransform", layerProperty(@, "textTransform", "textTransform", "none", _.isString)
+ @define "textIndent", layerProperty(@, "textIndent", "textIndent", null, _.isNumber)
+ @define "textDecoration", layerProperty(@, "textDecoration", "textDecoration", null, _.isString)
+ @define "direction", layerProperty(@, "direction", "direction", null, _.isString)
+
+ @define "font", layerProperty @, "font", null, null, _.isString, null, {}, (layer, value) ->
+ # Check if value contains number. We then assume proper use of font.
+ # Otherwise, we default to setting the fontFamily.
+ if /\d/.test(value)
+ layer.style.font = value
+ else
+ layer.fontFamily = value
+
+ @define "textStyle",
+ get: ->
+ _.pick @, TextLayer._textStyleProperties
+ set: (values) ->
+ for key, prop in _.pick values, TextLayer._textStyleProperties
+ @[key] = prop
+
+
+ @define "textDirection",
+ get: -> @direction
+ set: (value) -> @direction = value
+
+ # Map shadow properties to text shadow
+ @define "shadowX", layerProperty(@, "shadowX", "textShadow", 0, _.isNumber)
+ @define "shadowY", layerProperty(@, "shadowY", "textShadow", 0, _.isNumber)
+ @define "shadowBlur", layerProperty(@, "shadowBlur", "textShadow", 0, _.isNumber)
+ @define "shadowColor", layerProperty(@, "shadowColor", "textShadow", "", Color.validColorValue, Color.toColor)
View
@@ -444,6 +444,28 @@ Utils.deviceFont = (os) ->
return "Segoe UI" if os is "windows"
return "Helvetica"
+# Load fonts from Google Web Fonts
+_loadedFonts = []
+
+Utils.loadWebFont = (font, weight) ->
+
+ if font in _loadedFonts
+ return font
+
+ link = document.createElement("link")
+
+ if weight
+ link.href = "https://fonts.googleapis.com/css?family=#{font}:#{weight}"
+ else
+ link.href = "https://fonts.googleapis.com/css?family=#{font}"
+
+ link.rel = "stylesheet"
+ document.getElementsByTagName("head")[0].appendChild(link)
+
+ _loadedFonts.push(font)
+
+ return font
+
######################################################
# MATH FUNCTIONS
@@ -1077,7 +1099,7 @@ Utils.boundingFrame = (layer, rootContext=true) ->
Utils.perspectiveProjectionMatrix = (element) ->
p = element.perspective
m = new Matrix()
- m.m34 = -1/p if p? and p isnt 0
+ m.m34 = -1 / p if p? and p isnt 0
return m
# matrix of perspective projection with perspective origin applied
@@ -1143,9 +1165,12 @@ Utils.textSize = (text, style={}, constraints={}) ->
delete style.bottom
delete style.right
- style.width = "#{constraints.width}px" if constraints.width
- style.height = "#{constraints.height}px" if constraints.height
-
+ if constraints.max
+ style.maxWidth = "#{constraints.width}px" if constraints.width
+ style.maxHeight = "#{constraints.height}px" if constraints.height
+ else
+ style.width = "#{constraints.width}px" if constraints.width
+ style.height = "#{constraints.height}px" if constraints.height
_.extend(_textSizeNode.style, style)
if shouldCreateNode
View
@@ -39,6 +39,7 @@ require "./tests/LayerAnimationTest"
require "./tests/LayerDraggableTest"
require "./tests/ContextTest"
require "./tests/ScrollComponentTest"
+require "./tests/TextLayerTest"
require "./tests/PageComponentTest"
require "./tests/VersionTest"
require "./tests/ColorTest"
Oops, something went wrong.

0 comments on commit 4408f89

Please sign in to comment.