Permalink
Browse files

Merge pull request #558 from koenbok/feature/path-targeting

Feature/path targeting
  • Loading branch information...
nvh committed Jan 15, 2018
2 parents 206fb0e + 0d07e16 commit 939b4df7a8db2b846775c69ba6a06bec58c926be
View
@@ -7,7 +7,7 @@ Utils = require "./Utils"
{BaseClass} = require "./BaseClass"
{Animator} = require "./Animators/Animator"
{LinearAnimator} = require "./Animators/LinearAnimator"
{SVG, SVGPath} = require "./SVG"
{SVG} = require "./SVG"
Curves = require "./Animators/Curves"
numberRE = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/
@@ -280,8 +280,8 @@ class exports.Animation extends BaseClass
_prepareUpdateValues: =>
@_valueUpdaters = {}
for k, v of @_stateB
if SVGPath.isPath(v)
path = new SVGPath(v)
if SVG.isPath(v)
path = v
direction = null
start = null
end = null
@@ -380,10 +380,11 @@ class exports.Animation extends BaseClass
type = toShadow?.type ? fromShadow?.type ? Framer.Defaults.Shadow.type
fromShadow ?= _.defaults {color: null, type: type}, Framer.Defaults.Shadow
toShadow ?= _.defaults {color: null, type: type}, Framer.Defaults.Shadow
_.defaults fromShadow, Framer.Defaults.Shadow
_.defaults toShadow, Framer.Defaults.Shadow
result[index] = @_interpolateNumericObjectValues(["x", "y", "blur", "spread"], fromShadow, toShadow, value, false)
result[index].color = Color.mix(fromShadow.color, toShadow.color, value, false, @options.colorModel)
result[index].type = type
@_target[key] = result
@@ -417,7 +418,7 @@ class exports.Animation extends BaseClass
return _.pick(@layer, _.keys(@properties))
@isAnimatable = (v) ->
_.isNumber(v) or _.isFunction(v) or isRelativeProperty(v) or Color.isColorObject(v) or Gradient.isGradientObject(v) or SVGPath.isPath(v)
_.isNumber(v) or _.isFunction(v) or isRelativeProperty(v) or Color.isColorObject(v) or Gradient.isGradientObject(v) or SVG.isPath(v)
# Special cases that animate with different types of objects
@isAnimatableKey = (k) ->
@@ -429,13 +430,14 @@ class exports.Animation extends BaseClass
# Only animate numeric properties for now
for k, v of properties
if k in ["frame", "size", "point"] # Derived properties
if k in ["frame", "size", "point", "midPoint"] # Derived properties
switch k
when "frame" then derivedKeys = ["x", "y", "width", "height"]
when "size" then derivedKeys = ["width", "height"]
when "point" then derivedKeys = ["x", "y"]
when "midPoint" then derivedKeys = ["midX", "midY"]
else derivedKeys = []
if SVGPath.isPath(v)
if SVG.isPath(v)
for derivedKey in derivedKeys
animatableProperties[derivedKey] = v
else if _.isObject(v)
View
@@ -31,6 +31,16 @@ class exports.BaseClass extends EventEmitter
# Define the property on the prototype
Object.defineProperty(@prototype, propertyName, descriptor)
@undefine = (propertyName) ->
if _.isArray propertyName
propertyName.map((prop) => @undefine(prop))
else
@define propertyName, @simpleProperty propertyName, undefined,
importable: false
exportable: false
enumerable: false
@_addDescriptor: (propertyName, descriptor) ->
@@ -100,7 +110,7 @@ class exports.BaseClass extends EventEmitter
@_getPropertyDefaultValue k
_getPropertyDefaultValue: (k) ->
@_propertyList()[k]["default"]
@_propertyList()[k]?["default"]
_propertyList: ->
if not @_propertyListCache or ObjectDescriptorsChanged
View
@@ -12,6 +12,8 @@ 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.SVGPath = (require "./SVGPath").SVGPath
Framer.SVGGroup = (require "./SVGGroup").SVGGroup
Framer.TextLayer = (require "./TextLayer").TextLayer
Framer.Events = (require "./Events").Events
Framer.Gestures = (require "./Gestures").Gestures
View
@@ -174,19 +174,11 @@ class exports.Layer extends BaseClass
@_createHTMLElementIfNeeded()
# Create border element
@_elementBorder = document.createElement("div")
@_elementBorder.style.position = "absolute"
@_elementBorder.style.top = "0"
@_elementBorder.style.bottom = "0"
@_elementBorder.style.left = "0"
@_elementBorder.style.right = "0"
@_elementBorder.style.boxSizing = "border-box"
@_elementBorder.style.zIndex = "1000"
@_elementBorder.style.pointerEvents = "none"
@_element.appendChild(@_elementBorder)
@_createBorderElement()
# Sanitize calculated property setters so direct properties always win
layerPropertyIgnore(options, "point", ["x", "y"])
layerPropertyIgnore(options, "midPoint", ["midX", "midY"])
layerPropertyIgnore(options, "size", ["width", "height"])
layerPropertyIgnore(options, "frame", ["x", "y", "width", "height"])
@@ -520,6 +512,23 @@ class exports.Layer extends BaseClass
input = layerPropertyPointTransformer(input, @, "point")
@_setGeometryValues(input, ["x", "y"])
@define "midPoint",
importable: true
exportable: false
depends: ["width", "height", "size", "parent", "point"]
get: ->
x: @midX
y: @midY
set: (input) ->
if input.x?
input.midX = input.x
delete input.x
if input.y?
input.midY = input.y
delete input.y
input = layerPropertyPointTransformer(input, @, "midPoint")
@_setGeometryValues(input, ["midX", "midY"])
@define "size",
importable: true
exportable: false
@@ -864,6 +873,19 @@ class exports.Layer extends BaseClass
@_element = document.createElement "div"
@_element.classList.add("framerLayer")
_createBorderElement: ->
return if @_elementBorder?
@_elementBorder = document.createElement "div"
@_elementBorder.style.position = "absolute"
@_elementBorder.style.top = "0"
@_elementBorder.style.bottom = "0"
@_elementBorder.style.left = "0"
@_elementBorder.style.right = "0"
@_elementBorder.style.boxSizing = "border-box"
@_elementBorder.style.zIndex = "1000"
@_elementBorder.style.pointerEvents = "none"
@_element.appendChild(@_elementBorder)
_insertElement: ->
@bringToFront()
@_context.element.appendChild(@_element)
View
@@ -243,7 +243,11 @@ exports.LayerStyle =
return layer._properties.fill
strokeWidth: (layer) ->
return layer._properties.strokeWidth * layer.strokeWidthMultiplier
factor = layer.strokeWidthMultiplier ? 1
return layer._properties.strokeWidth * factor
strokeDasharray: (layer) ->
return layer._properties.strokeDasharray.join(",")
color: (layer) ->
return layer._properties.color
View
@@ -1,70 +1,62 @@
{BaseClass} = require "./BaseClass"
class SVG
@isSVG: (svg) ->
svg instanceof SVGElement
class SVGPath extends BaseClass
@define "length",
get: ->
@_length
@define "start",
get: ->
@pointAtFraction(0)
@define "end",
get: ->
@pointAtFraction(1)
@define "path", @simpleProperty("path", null)
constructor: (path) ->
return null if not SVGPath.isPath(path)
super
if path instanceof SVGPath
path = path.path
@path = path
@_length = path.getTotalLength()
pointAtFraction: (fraction) ->
@path.getPointAtLength(@length * fraction)
valueUpdater: (axis, target, offset) =>
switch axis
when "horizontal"
offset -= @start.x
return (key, value) =>
target[key] = offset + @pointAtFraction(value).x
when "vertical"
offset -= @start.y
return (key, value) =>
target[key] = offset + @pointAtFraction(value).y
when "angle"
return (key, value, delta = 0) =>
return if delta is 0
fromPoint = @pointAtFraction(Math.max(value - delta, 0))
toPoint = @pointAtFraction(Math.min(value + delta, 1))
angle = Math.atan2(fromPoint.y - toPoint.y, fromPoint.x - toPoint.x) * 180 / Math.PI - 90
target[key] = angle
{_} = require "./Underscore"
{Color} = require "./Color"
class exports.SVG
@validFill = (value) ->
Color.validColorValue(value) or _.startsWith(value, "url(")
@toFill = (value) ->
if _.startsWith(value, "url(")
return value
else
Color.toColor(value)
@updateGradientSVG: (svgLayer) ->
return if svgLayer.__constructor
if not Gradient.isGradient(svgLayer.gradient)
svgLayer._elementGradientSVG?.innerHTML = ""
return
if not svgLayer._elementGradientSVG
svgLayer._elementGradientSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgLayer._element.appendChild svgLayer._elementGradientSVG
id = "#{svgLayer.id}-gradient"
svgLayer._elementGradientSVG.innerHTML = """
<linearGradient id='#{id}' gradientTransform='rotate(#{svgLayer.gradient.angle - 90}, 0.5, 0.5)' >
<stop offset="0" stop-color='##{svgLayer.gradient.start.toHex()}' stop-opacity='#{svgLayer.gradient.start.a}' />
<stop offset="1" stop-color='##{svgLayer.gradient.end.toHex()}' stop-opacity='#{svgLayer.gradient.end.a}' />
</linearGradient>
"""
svgLayer.fill = "url(##{id})"
@constructSVGElements: (root, elements, PathClass, GroupClass) ->
targets = {}
children = []
if elements?
for element in elements
isTarget = element.id?
options = {}
options.name = element.id if isTarget
options.parent = root
if element instanceof SVGGElement
group = new GroupClass(element, options)
children.push(group)
_.extend(targets, group.elements)
if isTarget then targets[element.id] = group
continue
if element instanceof SVGPathElement
path = new PathClass(element, options)
children.push(path)
if isTarget then targets[element.id] = path
continue
return {targets, children}
@isPath: (path) ->
path instanceof SVGPathElement or path instanceof SVGPath
@getStart: (path) ->
@getPointAtFraction(path, 0)
@getPointAtFraction: (path, fraction) ->
return null if not @isPath(path)
length = path.getTotalLength() * fraction
path.getPointAtLength(length)
@getEnd: (path) ->
@getPointAtFraction(path, 1)
exports.SVG = SVG
exports.SVGPath = SVGPath
path instanceof Framer.SVGPath
View
@@ -0,0 +1,84 @@
{Layer, layerProperty} = require "./Layer"
{Color} = require "./Color"
class exports.SVGBaseLayer extends Layer
# Overridden Layer properties
@define "parent",
enumerable: false
exportable: false
importable: false
get: ->
@_parent or null
@define "html", get: -> @_element.outerHTML or ""
@define "width", get: -> @_width
@define "height", get: -> @_height
# Disabled properties
@undefine ["label", "blending", "image"]
@undefine ["blur", "brightness", "saturate", "hueRotate", "contrast", "invert", "grayscale", "sepia"] # webkitFilter properties
@undefine ["backgroundBlur", "backgroundBrightness", "backgroundSaturate", "backgroundHueRotate", "backgroundContrast", "backgroundInvert", "backgroundGrayscale", "backgroundSepia"] # webkitBackdropFilter properties
for i in [0..8]
do (i) =>
@undefine "shadow#{i+1}"
@undefine "shadows"
@undefine ["borderRadius", "cornerRadius", "borderStyle"]
@undefine ["constraintValues", "htmlIntrinsicSize"]
# Proxied helpers
@proxy = (propertyName, proxiedName) ->
@define propertyName,
get: ->
@[proxiedName]
set: (value) ->
return if @__applyingDefaults
@[proxiedName] = value
@proxy "borderColor", "stroke"
@proxy "strokeColor", "stroke"
@proxy "borderWidth", "strokeWidth"
# Overridden functions from Layer
_insertElement: ->
updateForSizeChange: ->
updateForDevicePixelRatioChange: =>
for cssProperty in ["width", "height", "webkitTransform"]
@_element.style[cssProperty] = LayerStyle[cssProperty](@)
copy: undefined
copySingle: undefined
addChild: undefined
removeChild: undefined
addSubLayer: undefined
removeSubLayer: undefined
bringToFront: undefined
sendToBack: undefined
placeBefore: undefined
placeBehind: undefined
@attributesFromElement: (attributes, element) ->
options = {}
for attribute in attributes
key = _.camelCase attribute
options[key] = element.getAttribute(attribute)
return options
constructor: (options) ->
element = options.element
@_element = element
@_elementBorder = element
@_elementHTML = element
@_parent = options.parent
delete options.parent
delete options.element
pathProperties = ["fill", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-dasharray", "stroke-dashoffset"]
_.defaults options, @constructor.attributesFromElement(pathProperties, element)
super(options)
@define "gradient",
get: ->
console.warn "The gradient property is currently not supported on shapes"
return undefined
set: (value) ->
console.warn "The gradient property is currently not supported on shapes"
Oops, something went wrong.

0 comments on commit 939b4df

Please sign in to comment.