Skip to content

Instantly share code, notes, and snippets.

@vmarcosp
Last active May 10, 2022 03:51
Show Gist options
  • Save vmarcosp/866ed8770727640b7b79af6ab125da7f to your computer and use it in GitHub Desktop.
Save vmarcosp/866ed8770727640b7b79af6ab125da7f to your computer and use it in GitHub Desktop.
module FramerMotion = {
type animateValue
type controlStatus = [#visible | #hidden]
type controls = {start: (. controlStatus) => unit}
type animate = [#controlled(controls) | #visible | #hidden]
@deriving(abstract)
type transition = {
@optional duration: float,
@optional delay: float,
@optional staggeredChildren: float,
@optional delayChildren: float,
}
@deriving(abstract)
type variant = {
@optional opacity: float,
@optional scale: int,
@optional x: int,
@optional y: int,
@optional transition: transition,
}
type variants = {
hidden: variant,
visible: variant,
}
@module("framer-motion")
external useAnimation: unit => controls = "useAnimation"
}
module Components = {
module Option = Belt.Option
type motionElement
type initial = [#hidden | #visible]
@deriving(abstract)
type motionProps = {
@optional className: string,
@optional @as("ref") innerRef: ReactDOM.domRef,
@optional id: string,
@optional href: string,
@optional src: string,
@optional target: string,
@optional initial: initial,
@optional variants: FramerMotion.variants,
@optional animate: FramerMotion.animateValue,
}
external identity: 'a => FramerMotion.animateValue = "%identity"
@module("react")
external createElement: (
motionElement,
motionProps,
React.element,
) => React.element = "createElement"
@module("react")
external createVoidElement: (motionElement, motionProps) => React.element =
"createElement"
let unwrapAnimate = v =>
switch v {
| #controlled(v) => identity(v)
| v => identity(v)
}
module type IMakeElement = {
let element: motionElement
}
module MakeElement = (M: IMakeElement) => {
@react.component
let make = (
~className=?,
~initial=?,
~variants=?,
~animate: option<FramerMotion.animate>=?,
~innerRef=?,
~id=?,
~href=?,
~children,
) => {
let props = motionProps(
~className?,
~innerRef?,
~id?,
~initial?,
~animate=?animate->Option.map(unwrapAnimate),
~variants?,
~href?,
(),
)
createElement(M.element, props, children)
}
}
module MakeVoidElement = (M: IMakeElement) => {
@react.component
let make = (
~className=?,
~initial=?,
~variants=?,
~animate: option<FramerMotion.animate>=?,
~innerRef=?,
~id=?,
~src=?,
) => {
let props = motionProps(
~className?,
~innerRef?,
~id?,
~initial?,
~animate=?animate->Option.map(unwrapAnimate),
~variants?,
~src?,
(),
)
createVoidElement(M.element, props)
}
}
}
module Div = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "div"
})
module Header = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "header"
})
module Section = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "section"
})
module H1 = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "h1"
})
module H2 = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "h2"
})
module P = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "p"
})
module A = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "a"
})
module Li = Components.MakeElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "li"
})
module Img = Components.MakeVoidElement({
@module("framer-motion") @scope("motion")
external element: Components.motionElement = "img"
})
// Usage
module Usage = {
open Render
let variants = delay => {
open FramerMotion
{
hidden: variant(~opacity=0.0, ~y=20, ()),
visible: variant(
~opacity=1.0,
~y=0,
~transition=transition(~delay, ~duration=0.6, ()),
()
),
}
}
@react.component
let make = () => {
let controls = FramerMotion.useAnimation()
let (innerRef, inView) = IntersectionObserver.useInView()
React.useEffect1(() => {
if inView {
controls.start(. #visible)
}
None
}, [inView])
<section ref=innerRef>
<Motion.H1
initial=#hidden
animate=#controlled(controls)
variants={variants(1.75)}
>
{Content.heroText}
</Motion.H1>
<Motion.A
initial=#hidden
animate=#controlled(controls)
variants={variants(2.0)}
className=button>
{"SAIBA MAIS"->str}
</Motion.A>
</section>
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment