Skip to content
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
@@ -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.
@@ -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
@@ -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)
@@ -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
@@ -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)
@@ -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
@@ -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"

0 comments on commit 4408f89

Please sign in to comment.
You can’t perform that action at this time.