官网API地址

Frame

Layout(大小位置)

  1. width={100}
  2. height={100}
  3. size={100} //同时设置宽度和高度的快捷方式。
  4. top={100}
  5. left={100}
  6. right={100}
  7. bottom={100}
  8. center=“x”/center=“y”/center //快速居中
  9. position={“relative”}

Visual(样式)

style={{width:100px}} //样式也可以用styled-components
visible={false}
opacity={0.5}

//以下是background
    background=“#09F” //色值
    backgroundColor="#09F"
    background={Color({r: 255, g: 0, b: 102})} //RGB
    backgroundColor={Color({r: 255, g: 0, b: 102})} 
    background={{ alpha: 1, angle: 75, start: "#09F", end: "#F09"}}  //渐变
    background={{ src: "https://example.com/logo.png"}} //URL
    image="https://source.unsplash.com/random"

border="1px solid #09F"
radius={10}
shadow="10px 5px 5px black"
overflow="hidden"

Transfrom (变化)

x={100}
y={100}
z={100}
rotate={45}
rotateX={45}
rotateY={45}
rotateZ={45}
scale={1.5}
scaleX={1.5}
scaleY={2}
skew={15}
skewX={15}
skewY={15}
originX={0.5}
originY={0.5}
originZ={100}
perspective={500} //镜头距离元素表面的位置
preserve3d={true}

交互事件

交互事件.mindnode.zip

Animation

支持动画的类型

Value

Numbers
Strings
All Unit Types (px, %, calc(), etc.)
Colors (hex, rgba, hsla)
Complex Values (Strings with numbers and colors)

//animate Complex Values
For instance, "5px 10px #333" can be animated to "0px 0px #333" 
but not "0 0 #333".

transfroms

x, y, z
rotate, rotateX, rotateY, rotateZ
scale, scaleX, scaleY, scaleZ
skewX, skewY
originX, originY
perspective

Value Conversion

x
y
width
height
top
left
right
bottom
//数值运算
calc()最大的好处就是用在流体布局上,可以通过calc()计算得到元素的宽度。著作权归作者所有。
语法:width: calc(expression);
calc()语法非常简单,就像我们小时候学加 (+)、减(-)、乘(*)、除(/)一样,使用数学表达式来表示:著作权归作者所有。



vw:视窗宽度的百分比(1vw 代表视窗的宽度为 1%)
vh:视窗高度的百分比
vmin:当前 vw 和 vh 中较小的一个值
vmax:当前 vw 和 vh 中较大的一个值

2、vw、vh与%百分比的区别
(1)% 是相对于父元素的大小设定的比率,vw、vh 是视窗大小决定的。
(2)vw、vh 优势在于能够直接获取高度,而用 % 在没有设置 body 高度的情况下,是无法正确获得可视区域的高度的,所以这是挺不错的优势。
3、vmin、vmax用处
做移动页面开发时,如果使用 vw、wh 设置字体大小(比如 5vw),在竖屏和横屏状态下显示的字体大小是不一样的。
由于 vmin 和 vmax 是当前较小的 vw 和 vh 和当前较大的 vw 和 vh。这里就可以用到 vmin 和 vmax。使得文字大小在横竖屏下保持一致。

Transition-动画曲线

transition.mindnode.zip

Easing-curve

Tween-ease在线演示

截屏2019-12-05上午10.59.56.png

custom cubic

官网链接

截屏2019-12-05上午11.05.07.png
这个可以自定义ease曲线哦

Spring-curves

在线实时演示链接

截屏2019-12-05上午11.00.47.png

Page

Page.mindnode.zip

stack

这里的话,stack主要是可以自动间距。在画布中设计好,我仔细看了一下,可以通过Override控制的属性是stack里面的单个元素的visible,某一个元素删除之后,其他的元素会跟上来。
应用场景:列表删除


import { Override, Data } from "framer"

const data = Data({
    visible:true
})
export function None():Override{
    return{
        visible:data.visible,
    }
}
export function Button():Override{
    return{
        onTap(){data.visible = false}
    }
}

Utilities

useAnimation

用这个可以创建一组连续的动画
_2019_12_05_2_11_9.gif

import { Override, useAnimation } from "framer"

// Override Docs: https://framer.com/docs/overrides

export function Event_Sequence(): Override {
    const animation = useAnimation()

    async function sequence() {
        await animation.start({ rotate: -90 })
        await animation.start({ scale: 1.5 })
        await animation.start({ rotate: 0 })
        await animation.start({ scale: 1 })
    }

    return {
        animate: animation,
        onTap() {
            sequence()
        },
    }
}

案例1:微信好友列表效果模仿

好友列表主要通过Drag事件,来控制消息提醒和删除效果。

主要难点:

  • controls组件外声明,这样其他组件也可以参与控制。类似Data,但是强于Data的操作。
  • property = condition ? valueWhenTrue : valueWhenFalse 通过布尔状态控制,来达到效果。消息提示用到这个了

源文件:微信列表拖动效果.framerx.zip

视频展示:

微信菜单.mov (4.19MB) 代码展示:

import { Override, useTransform, useAnimation, motionValue, Data } from "framer"

//定义设计组件的三个隐藏功能模块的宽度
const data = Data({
    redWidth: "",
    blueWidth: "",
    greyWidth: "", //功能菜单的总宽度
    remindOpacity: true,
    redDeleteX: 0,
    listVisiable: true,
})
//因为对象在被拖动时,X的值是motionValue(这个是object,不是数字)
//X向右移动为正,向左移动为负
const x = motionValue(0)
let controls
//这里用let声明controls,其他任何override都可以控制controls的状态,妙啊
export function Drag(props): Override {
    // console.log(data.dragOpened)

    controls = useAnimation()
    function handleDragToRight() {
        //功能菜单隐藏
        controls.start({ x: 0, transition: { duration: 0.25 } })
        data.redDeleteX = 0
    }
    function handleDragToLeft() {
        //功能菜单显示
        controls.start({ x: -data.greyWidth, transition: { duration: 0.25 } })
    }
    return {
        drag: "x",
        dragConstraints: {
            right: 0,
            left: -data.greyWidth,
        },
        x: x,
        animate: controls,
        onDragEnd(event, info) {
            //获取X的值,Math的方法可以使获得的值(浮点数:有很多小数)变为整数
            //因为浮点数(1.353453)和整数(4234)无法做比较
            const positionX = Math.floor(info.point.x * 1) / 1
            // console.log(positionX)
            //获取拖动object时的速度值
            const velocityX = Math.floor(info.velocity.x * 1) / 1
            // console.log(velocityX)
            if (positionX < 0 && positionX > -data.greyWidth * 0.5) {
                handleDragToRight()
            } else if (
                positionX < -data.greyWidth * 0.5 &&
                positionX > -data.greyWidth
            ) {
                handleDragToLeft()
            }
            if (velocityX > 10) {
                handleDragToRight()
            } else if (velocityX < -10) {
                handleDragToLeft()
            }
        },
        onTap(event, info) {
            const pointX = Math.floor(info.point.x * 1) / 1
            if (pointX < 118) {
                handleDragToRight()
            }
        },
    }
}

export function Grey(props): Override {
    //获取设计组件greyWidth的宽度
    data.greyWidth = props.width
    const dragX = useTransform(x, [-data.greyWidth, 0], [-props.width, 0], {
        clamp: false,
    })
    return {
        x: dragX,
        onTap() {
            ;(data.remindOpacity = !data.remindOpacity),
                controls.start({
                    x: 0,
                    transition: { duration: 0.25 },
                })
        },
        text: data.remindOpacity ? "标记已读" : "标为未读",
        //property = condition ? valueWhenTrue : valueWhenFalse
    }
}
export function Blue(props): Override {
    //获取设计组件blueWidth的宽度
    data.blueWidth = props.width
    const dragX = useTransform(x, [-data.greyWidth, 0], [-props.width, 0], {
        clamp: false,
    })
    return {
        x: dragX,
    }
}
export function Red(props): Override {
    //获取设计组件redWidth的宽度
    data.redWidth = props.width
    const dragX = useTransform(x, [-data.greyWidth, 0], [-62, 0], {
        clamp: false,
    })
    return {
        onTap() {
            data.redDeleteX = -157
        },
        x: dragX,
    }
}
//redDeleteX
export function RedDelete(): Override {
    return {
        animate: { x: data.redDeleteX, transition: { duration: 0.3 } },
        onTap() {
            data.listVisiable = false
        },
    }
}
//列表整体

export function List(): Override {
    return {
        visible: data.listVisiable,
    }
}
//remind的透明度
export function Remind(props): Override {
    return {
        opacity: data.remindOpacity ? 1 : 0,
        //property = condition ? valueWhenTrue : valueWhenFalse
        animate: { transition: { duration: 0.2 } },
    }
}

useTransform

案例1

这个是由useMotionValue, useTransform,一起弄的。应用场景应该是drag,scroll 这种能手动控制实时变化的元素。核心要义就是可以把一组变化的数值的效果,应用到另一种要变化的数值。听起来可能有点绕口,就比如下面的这个这个效果。通过拖动,来改变小方块的x值,然后通过X值的改变再改变,rotate 和
scale的值。这种是实时的。
1.gif

import { Override, useMotionValue, useTransform } from "framer"

// Override Docs: https://framer.com/docs/overrides

export function Drag_3D(): Override {
    const x = useMotionValue(0)
    const y = useMotionValue(0)
    const rotateX = useTransform(x, [-100, 100], [-60, 60])
    const rotateY = useTransform(y, [-100, 100], [60, -60])

    return {
        x: x,
        y: y,
        drag: true,
        dragConstraints: { left: 0, top: 0, right: 0, bottom: 0 },
        rotateY: rotateX,
        rotateX: rotateY,
        dragElastic: 0.6,
    }
}

export function Radial_Gradient(): Override {
    return {
        background:
            "radial-gradient(rgba(255,255,255,0), rgba(255,255,255,0.3))",
    }
}

案例2

通过左右拖动,实时改变背景颜色

屏幕录制2019-12-05下午5.40.24.mov (1.37MB)

import { Override, useTransform, motionValue, transform } from "framer"

// Override Docs: https://framer.com/docs/overrides

const x = motionValue(0)

export function Background(): Override {
    const background = useTransform(x, [-100, 0, 100], ["#80F", "#40F", "#0BF"])
    return {
        background: background,
    }
}

export function Frame(): Override {
    return {
        drag: "x",
        dragConstraints: {
            right: 0,
            left: 0,
        },
        x: x,
    }
}

案例3

我们来大概看一下这个例子:

知识点:

在Drag的过程中,通过Drag的X值来改变其他的X值。

Framer 库:

Override, useTransform, useAnimation, motionValue, Data

动效分析:

  • 当从右往左拖动时,拖动的速度值达到一定值的时候,Drag的X值会变为最大值(灰色色块的width),此时功能栏会全部显示。
  • 当从右往左拖动时,拖动的速度值没有达到一定值的时候,且距离小于特定X值时,Drag的X值会变为0,功能栏隐藏;X大于特定的值的时候,Drag的X值会变为最大,功能栏显示。
  • 从右往左拖动,同理。


    源文件:
    支付宝拖动删除的效果.zip

    在线演示链接 🙈

1.mov (2.81MB) 截屏2019-12-06下午6.11.19.png

import { Override, useTransform, useAnimation, motionValue, Data } from "framer"

//定义设计组件的三个隐藏功能模块的宽度
const data = Data({
    redWidth: "",
    blueWidth: "",
    greyWidth: "", //功能菜单的总宽度
})
//因为对象在被拖动时,X的值是motionValue(这个是object,不是数字)
//X像右移动为正,向左移动为负
const x = motionValue(0)

export function Drag(): Override {
    const controls = useAnimation()
    function handleDragToRight() {
        //功能菜单隐藏
        controls.start({ x: 0, transition: { duration: 0.25 } })
    }
    function handleDragToLeft() {
        //功能菜单显示
        controls.start({ x: -data.greyWidth, transition: { duration: 0.25 } })
    }
    return {
        drag: "x",
        dragConstraints: {
            right: 0,
            left: -data.greyWidth,
        },
        x: x,
        animate: controls,
        onDragEnd(event, info) {
            //获取X的值,Math的方法可以使获得的值(浮点数:有很多小数)变为整数
            //因为浮点数(1.353453)和整数(4234)无法做比较
            const positionX = Math.floor(info.point.x * 1) / 1
            //获取拖动object时的速度值
            const velocityX = Math.floor(info.velocity.x * 1) / 1
            // console.log(velocityX)
            if (positionX < 0 && positionX > -data.redWidth * 1.5) {
                handleDragToRight()
            } else if (
                positionX < -data.redWidth * 1.5 &&
                positionX > -data.greyWidth
            ) {
                handleDragToLeft()
            }
            if (velocityX > 10) {
                handleDragToRight()
            } else if (velocityX < -10) {
                handleDragToLeft()
            }
        },
    }
}

export function Grey(props): Override {
    //获取设计组件greyWidth的宽度
    data.greyWidth = props.width
    const dragX = useTransform(x, [-data.greyWidth, 0], [-props.width, 0])
    return {
        x: dragX,
    }
}
export function Blue(props): Override {
    //获取设计组件blueWidth的宽度
    data.blueWidth = props.width
    const dragX = useTransform(x, [-data.greyWidth, 0], [-props.width, 0])
    return {
        x: dragX,
    }
}
export function Red(props): Override {
    //获取设计组件redWidth的宽度
    data.redWidth = props.width
    const dragX = useTransform(x, [-data.greyWidth, 0], [-props.width, 0])
    return {
        x: dragX,
    }
}

案例4:卡片拖拽删除效果

这个案例中大量的使用了联动操作,

视频演示:

卡片拖动删除效果.mov (6.11MB)

源文件:card.framerx.zip

代码展示:

import { Override, Data, motionValue, useTransform, useAnimation } from "framer"

const data = Data({
    //card1
    boxShadow: "0px 12px 24px 300px rgba(148, 148, 148, 0.25)",
    isDeleted: false,
    visible: true,
    isDeletedRight: false,
})

//card 1

const x = motionValue(0)
let controls1
export function Card1(): Override {
    const rotate = useTransform(x, [-70, 0, 70], [-8, 0, 8])
    function MoveToRight() {
        controls1.start({ x: 400 })
        controls2.start({ opacity: 1, width: 327, y: 18 })
        animation.start({ width: 294, y: 18 })
        //我这里是对的,但是他会不定时报错,不过,不影响使用。
    }
    function MoveToLeft() {
        controls1.start({ x: -400 })
        controls2.start({ opacity: 1, width: 327, y: 18 })
        animation.start({ width: 294, y: 18 })
    }
    //useAnimation
    controls1 = useAnimation()
    return {
        drag: "x",
        dragConstraints: {
            right: 0,
            left: 0,
        },
        x: x,
        rotate: rotate,
        animate: controls1,
        boxShadow: data.boxShadow,
        //    boxShadow:data.boxShadow,
        onDragEnd(event, info) {
            const positionX = Math.floor(info.point.x * 1) / 1
            const velocityX = Math.floor(info.velocity.x * 1) / 1
            //位置条件判断
            if (positionX > 100) {
                ; (data.isDeleted = true), (data.isDeletedRight =true), MoveToRight()
            } else if (positionX < -100) {
                ;(data.isDeleted = true), MoveToLeft()
            }
            //速度条件判断
            if (velocityX > 40) {
                MoveToRight()
            } else if (velocityX < -40) {
                MoveToLeft()
            }
        },
        onDrag(event, info) {
            const positionX1 = Math.floor(info.point.x * 1) / 1
            console.log(positionX1)
            if (positionX1 > 0) {
                data.boxShadow =
                    "-24px 12px 24px 300px rgba(148, 148, 148, 0.55)"
            } else if (positionX1 < 0) {
                data.boxShadow =
                    "24px 12px 24px 300px rgba(148, 148, 148, 0.55)"
            }
        },
    }
}
//card 2
let controls2
export function Card2(): Override {
    //useTransfrom
    const moveX = 400
    const opacity = useTransform(x, [-0.3 * moveX, 0, 0.3 * moveX], [1, 0.6, 1])
    const width = useTransform(x, [-moveX, 0, moveX], [327, 294, 327])
    const y = useTransform(x, [-moveX, -200, 0, 200, moveX], [18, 0, 0, 0, 18])
    //useAnimation
    controls2 = useAnimation()
    return {
        opacity: opacity,
        width: width,
        y: y,
        aniamte: controls2,
        transition: { duration: 0.75 },
    }
}

//card 3
let animation
export function Card3(): Override {
    //useTransfrom
    const moveX = 400
    const width = useTransform(x, [-moveX, 0, moveX], [294, 262, 294])
    const y = useTransform(x, [-moveX, -200, 0, 200, moveX], [18, 0, 0, 0, 18])
    //useAnimation
    const animation = useAnimation()
    return {
        width: width,
        y: y,
        aniamte: animation,
        transition: { duration: 0.75 },
    }
}
//Card 4
export function Card4(): Override {
    return {
        y: 0,
        opacity: 0,
        animate: data.isDeleted
            ? { y: -18, opacity: 0.6 }
            : { y: 0, opacity: 0 },
        transition: { duration: 0.35 },
    }
}

//以下是下面两个小按钮的动效部分
const x0 = 3
const x1 = 5
const x2 = 6
const x3 = 9 //白色like出现
const x4 = 60 
const x5 = 85
const x6 = 104
const x7 = 115
const x8 = 127
const x9 = 320
// const x10 = 30

//以下是右边的按钮的部分

//whiteContent
export function whiteContent(): Override {
    const scale = useTransform(x, [0, x6,x9], [1, 1.4,1])
    return {
        scale: scale,
    }
}
//likeIconGreen
export function likeIconGreen(): Override {
    const scale = useTransform(x, [0, x1,x6+1], [1, 0,1])
    return {
        scale: scale,
    }
}
//whiteLike

export function whiteLike(): Override {
    //useTransform
    const opacity = useTransform(x, [x2, x3,x7,x8], [0, 1, 1,0])
    const scale = useTransform(x, [x1,x4, x5], [0.2, 1.1, 1.5])
    //useAnimation
    return {
        opacity: opacity,
        scale: scale,
    }
}
//greenCircle

export function greenCircle(): Override {
    const scale = useTransform(x, [0, x0, x1, x6,x9], [0, 0, 0.1, 1.4,1])
    return {
        scale: scale,
    }
}
//whiteCircle

export function whiteCircle(): Override {
    const opacity = useTransform(x, [x6,x6+1], [0, 1])
    const scale = useTransform(x,[x6,x9],[0,1])
    return {
        opacity: opacity,
        scale:scale
    }
}


//Plus
export function Plus(): Override {
    return {
        animate: {
            y: data.isDeletedRight ? -60 : 0,
            opacity: data.isDeletedRight ? 0 : 1,
        },
        transiton: {
            duration: 0.35,
        },
    }
}

//以下是左边的按钮的部分


//whiteContent
export function whiteContentLeft(): Override {
    const scale = useTransform(x, [0, -x6,-x9], [1, 1.4,1])
    return {
        scale: scale,
    }
}
//likeIconGreen
export function DeleteIconRed(): Override {
    const scale = useTransform(x, [0, -x1,-x6-1], [1, 0,1])
    return {
        scale: scale,
    }
}
//whiteLike

export function whiteDelete(): Override {
    //useTransform
    const opacity = useTransform(x, [-x2, -x3,-x7,-x8], [0, 1, 1,0])
    const scale = useTransform(x, [-x1,-x4, -x5], [0.2, 1.1, 1.5])
    //useAnimation
    return {
        opacity: opacity,
        scale: scale,
    }
}
//greenCircle

export function RedCircle(): Override {
    const scale = useTransform(x, [0, -x0, -x1, -x6,-x9], [0, 0, 0.1, 1.4,1])
    return {
        scale: scale,
    }
}
//whiteCircle

export function whiteCircleLeft(): Override {
    const opacity = useTransform(x, [-x6,-x6-1], [0, 1])
    const scale = useTransform(x,[-x6,-x9],[0,1])
    return {
        opacity: opacity,
        scale:scale
    }
}

useViewportScroll

  • scrollX — Horizontal scroll distance in pixels. X轴滑动距离原点的位置(通常是负值)
  • scrollY — Vertical scroll distance in pixels. Y轴滑动距离原点的位置(通常是负值)!!这个用的多一点
  • scrollXProgress — Horizontal scroll progress between 0 and 1.
  • scrollYProgress — Vertical scroll progress between 0 and 1.
  • Note:If the returned scroll MotionValues don’t seem to be updating, double check if the body tag styles are set to width: 100%; height: 100% or similar, as this can break accurate measurement of viewport scroll.

仅使用motionValue来驱动

_2019_12_03_11_09_31.gif

源文件:

import { Override, useTransform,motionValue } from "framer"

//定义一个移动变量,这个motionValue其实就是,ScrollY的值。这个值是负的。
const scrollY = motionValue(0)

export function Scroll():Override {
    return{
        contentOffsetY : scrollY
    }
}

export function Header():Override {
    //此处说一下useTransform。这个就相当于乾坤大挪移,可以把一组正在变的值的情况应用于另外一组要变的值
    //现在这个就是把scrollY的值变成opacity
    //就是scollY从0到150,然后opacity就是0-1.
    //这种对应关系是实时的。不像其他override,达到触发条件才执行。
    const opacity = useTransform(scrollY,[-150,0],[1,0])
    return{
        opacity:opacity
    }
}

仅使用motionValue和Data来条件驱动

这里和下面的onScroll的info.point.y其实是差不多的,功能是一样的,只是代码的写法不一样。下面那种会报错,但是不影响使用。按理说,还是这种优雅,这个是在framer 官网看到的。也分享一下

视频展示:

scroll_listen_onchange.mov (1018.4KB) 源文件:scroll_listen_onchange.framerx.zip

代码:

import { Override, Data, motionValue, useTransform } from "framer"

// Keep track of the state of our application
const data = Data({ opacity: 0 })

// Create a MotionValue to track contentOffsetY
const contentOffsetY = motionValue(0)

// Listen for changes to contentOffsetY

// contentOffsetY.onChange(offset => (data.isPastLimit = offset < -52))
// 有个大神是上面这样写的,我觉得不适合多条件判断。特意改成下面这种
contentOffsetY.onChange(point => {
    if (point < -52) {
        data.opacity = 1
    } else {
        data.opacity = 0
    }
})

// Apply this override to your scroll component
export function TrackScroll(): Override {
    return { 
      contentOffsetY: contentOffsetY,
              onScroll() {
            // console.log(contentOffsetY.get());
            //这个可以实时查看scrollY的滚动值
        },
    }
}

// Apply this override to a frame containing your title
export function ShowTitleIfPastLimit(): Override {
    return {
        animate: {
            opacity: data.opacity,
            transition: {
                duration: 0.25,
            },
        },
    }
}

仅使用onScroll的info.point.y来驱动

3.gif
这种滚动动画就是达到某种条件才会触发
源文件:
useViewportScroll.framerx.zip

import { Override, Data } from "framer"

//定义一个移动变量,这个motionValue其实就是,ScrollY的值。这个值是负的。
const data = Data({
    opacity:0
})

export function Scroll():Override {
    return{
        onScroll(info){
            //info.point.y :  Relative to the device or page. 也就是向下滚动的值
            const scrollY = Math.floor(info.point.y * 1) / 1
            //此处point会报错,但是不影响使用
            //info.point.y的值是浮点数,也就是会有很多小数点,不适合后面的条件判断
            //math的方法可以使info.point.y变成整数
            const newScrollY = -scrollY
            if (newScrollY > 0 && newScrollY <150){
                data.opacity = 0
            } else if (newScrollY >= 150){
                data.opacity = 1
            }else{
                data.opacity = 0
            }
        }
    }
}

export function Header():Override {
    return{
        animate:{
            opacity: data.opacity,
            transition:{
                duration:0.2
            }
        }
    }
}

motionValue和onScroll的info.point.y混合使用

这是一个知识星球内容打卡页的交互效果,做了滚动时对页面各个元素的影响,以及下拉刷新的效果。

屏幕录制2019-12-08下午6.25.28.mov (3.68MB)源文件:
知识星球-内容.framerx.zip

源代码:

import { Override, useTransform, motionValue, Data } from "framer"

const data = Data({
    HeaderNameOpacity: 0,
    menuBorderOpacity:0,
    length:0,
    circlePathOpacity:0
})
//定义一个变量,这个scroll是负值
const scrollY = motionValue(0)

//滚动对象
export function Scroll(props): Override {
    return {
        contentOffsetY: scrollY,
        onScroll(info) {
            const ScrollY = Math.floor(info.point.y * 1) / 1
            const NewScrollY = -ScrollY
            console.log(NewScrollY)
            //header的name的透明度的条件判断
            if (NewScrollY > 58) {
                data.HeaderNameOpacity = 1
            } else {
                data.HeaderNameOpacity = 0
            };
            //menu菜单borderColor的条件判断
            if (NewScrollY > 239){
                data.menuBorderOpacity = 1
                console.log("ok")
            }else{
                data.menuBorderOpacity = 0
            }
            //ciclePathLength的条件判断
            if (ScrollY > 0 && ScrollY <= 64){
                data.length = ScrollY*246/64
            } else if (ScrollY > 64){
                data.length = 246
            }
            //ciclePathOpacity
            if (ScrollY >0){
                data.circlePathOpacity = 1
            }else{
                data.circlePathOpacity = 0,

            }
        },
    }
}
//header的bg的透明度变化
export function HeaderBg(): Override {
    const opacity = useTransform(scrollY, [-59, 0], [1, 0])
    return {
        opacity: opacity,
    }
}
//header的name的透明度变化
export function HeaderName(): Override {
    return {
        opacity: data.HeaderNameOpacity,
    }
}
//menu的Y轴的变化
export function Menu(props):Override {
    const y = useTransform(scrollY, [-215, 0, 1000], [-215, 0, 1000])
    return{
        y:y
    }
}
//Menu的border
export function MenuBorder():Override {
    return{
        opacity:data.menuBorderOpacity
    }
}
//photo的大小和移动
export function Photo():Override {
    const y = useTransform(scrollY,[0,78],[0,78]) 
    const scale = useTransform(scrollY,[78,300],[1,1.62]) 
    return{
        y:y,
        scale:scale
    }
}
//刷新圆圈的长度
export function CirclePath():Override {
    return{
        length:data.length,
        opacity:data.circlePathOpacity
    }
}
//刷新圆圈的透明度
export function Circle():Override {
    const opacity = useTransform(scrollY, [0, 64], [0, 1]) 
    return{
        opacity:opacity
    }
}

喜马拉雅的一个例子
源文件:喜马拉雅banner.framerx.zip

喜马拉雅.mov (6.42MB)

import { Override, Data,useTransform,motionValue } from "framer"
import { colors } from "./canvas"

//设定变量current的初始状态
const data = Data({
    current: 0,
    background: "",
    circleX: 0,
    bgFrontOpacity: 0,
    appearance: "light",
    topMenuBg: colors.primary,
    sortButtonBG: colors.primary,
    inputBg: colors.primary,
    sortButtonBorder: "1px solid #fff",
})

//page组件的改变传递current值
export function Page(): Override {
    return {
        onChangePage(current) {
            console.log(current)
            ;(data.current = current), (data.circleX = current * 10)
            if (data.current == 0) {
                data.background = "#0f2b42"
            }
            if (data.current == 1) {
                data.background = "#295150"
            }
            if (data.current == 2) {
                data.background = "#0f2b42"
            }
            if (data.current == 3) {
                data.background = "#ba662d"
            }
            if (data.current == 4) {
                data.background = "#9185b9"
            }
            if (data.current == 5) {
                data.background = "#4b4264"
            }
            if (data.current == 6) {
                data.background = "#bcc0c5"
            }
            if (data.current == 7) {
                data.background = "#0f2b42"
            }
            if (data.current == 8) {
                data.background = "#295150"
            }
        },
    }
}
//定义全局变量scrollY,监听滚动的Y值。
const scrollY = motionValue(0)
export function ScrollContent(): Override {
    return {
        contentOffsetY:scrollY,
        onScroll(info: point) {
            //此处point报错了,但是不影响使用
            const scrollY = Math.floor(info.point.y * 1) / 1
            const newScrollY = -scrollY
            if (newScrollY > 155) {
                ;(data.bgFrontOpacity = 1),
                    (data.appearance = "dark"),
                    (data.topMenuBg = colors.dark_primary),
                    (data.sortButtonBG = colors.menu),
                    (data.inputBg = colors.two),
                    (data.sortButtonBorder = "1px solid #F86643")
            } else {
                ;(data.bgFrontOpacity = 0),
                    (data.appearance = "light"),
                    (data.topMenuBg = colors.primary),
                    (data.sortButtonBG = colors.primary),
                    (data.inputBg = colors.primary),
                    (data.sortButtonBorder = "1px solid #fff")
            }
        },
    }
}
//根据Page组件传递的current值,来判断小圆点的位置
export function Circle1(): Override {
    return {
        animate: { x: data.circleX, transition: { duration: 0.35 } },
    }
}

//通过Page组件传递的状态值来判断背景颜色
export function BgBannerBack(): Override {
    const y = useTransform(scrollY,[-26,-155],[0,-129])
    return {
        background: data.background,
        y:y
    }
}
export function BgBannerFront(): Override {
    return {
        background: data.background,
    }
}
export function WhiteSolid(): Override {
    return {
        animate: {
            opacity: data.bgFrontOpacity,
            transition: { duration: 0.35 },
        },
    }
}
export function StatusBar(): Override {
    return {
        appearance: data.appearance,
    }
}
//顶部导航栏颜色的变化
export function TopMenu(): Override {
    return {
        background: data.topMenuBg,
    }
}
//分类按钮的变化
export function sortButton(): Override {
    return {
        background: data.sortButtonBG,
    }
}
//input区的颜色变化
export function inputBg(): Override {
    return {
        background: data.inputBg,
    }
}
export function sortButtonBorder(): Override {
    return {
        border: data.sortButtonBorder,
    }
}

useCycle

这个主要是用来循环一组值,或者循环两种状态。

循环一组值

import { useCycle,Override } from "framer"

export function MyComponent(): Override {
    const [x, cycleX] = useCycle(0, 50, 100)
    return {
        animate: { x: x },
        onTap(){
            cycleX()
        }
    }
}
//再点击时候,x的值会在0,50,100之间循环。

循环多种状态

export function MultiVariants():Override {
    const variants = {
        green: { background: "#1ea463" },
        yellow: { background: "#fecd45" },
        red: { background: "#de5347" },
    }

    const [current, cycle] = useCycle(
        "green",
        "yellow",
        "red"
    )
    return{
        variants:variants,
        animate:current,
        onTap(){
            cycle()
        },
        onmousedown(){
            cycle(0)
        },
        onMouseLeave(){
            cycle(2)
        }
    }
}