01 代码组件

使用代码构建滚动组件。

1.1 引入 Scroll module 和内容

通过以下代码从 framer 库中引入 Scroll 模组。

  1. import {Scroll} from "framer"

引入画板中的滚动内容方式如下:先从画板中引入设计组件,需要先把画板创建为组件(create component),本例中为 Content,然后在代码中引用。

import { Content } from "./canvas"

使用时和其他 module 方法一致。

<Content/>

1.2 其他属性

import * as React from "react"
import { Frame, Scroll, useAnimation } from "framer"
import { Content } from "./canvas"

export function ScrollContainer() {
    const controls = useAnimation()
    controls.start({
        y: 44,
        transition: {
            duration: 0.001,
        },
    })

    return (
        <Scroll
            width={375}
            height={812}
            // 滚动内容的宽高
            contentWidth={375}
            contentHeight={1617}
            // content 和 <Scroll/> 组件顶部的距离
            // contentOffsetY={100}
            // content 和 <Scroll/> 组件左侧的距离
            // contentOffsetX={100}
            // 是否可以拖拽
            // dragEnabled={false}
            // 禁止使用鼠标滚轮滚动
            // wheelEnabled={false}
            // 两个方向都能滚动
            // direction="both"
            // 只能在水平方向滚动
            // direction="horizontal"
            // 只能在垂直方向滚动(默认)
            // direction="vertical"
            // 滚动开始前的默认动画
            animate={controls}
            // 滚动时触发函数
            onScroll={info => {
                console.log(info.offset)
            }}
            // 滚动开始时触发
            onScrollStart={() => {
                console.log("scrollStarted")
            }}
            // 滚动结束时触发
            onScrollEnd={() => {
                console.log("scrollEnded")
            }}
        >
            <Content />
        </Scroll>
    )
}

02 ScrollAnimate & useAnimation

ScrollAnimate 和 useAnimation 结合使得滚动组件滚动到某一特定位置。本例中我们让滚动组件默认向上移动 180 而非默认位置。

2020-03-13 23.01.05.gif

import * as React from "react"
import { Override, useAnimation } from "framer"

// 定义可以在所有函数中调用的变量 controls
let controls

export function scrollAnimate(): Override {
    controls = useAnimation()
    controls.start({
        y: -180,
        transition: {
            duration: 0.00001,
        },
    })
    return {
        scrollAnimate: controls,
    }
}

可通过判断 Data 中属性的值来控制滚动组件的动画,最终控制其位置。

2020-03-14 15.00.08.gif

import * as React from "react"
import { Override, useAnimation, Data } from "framer"

const appState = Data({
    pageIndex: 0,
})

let controls
let controlsPage1

export function TrackScroll(): Override {
    console.log("pageIndex", appState.pageIndex)
    controls = useAnimation()
    controlsPage1 = useAnimation()
    return {
        //===非常重要,为绝对等于(“1”===1 为 false),=为赋值,==为转换属性之后判断(“1”==1为 true),此例用==也可
        scrollAnimate: appState.pageIndex === 1 ? controlsPage1 : controls,
    }
}

export function button1(): Override {
    return {
        onTap() {
            appState.pageIndex = 1
            console.log("cc", appState.pageIndex)
            controls.start({
                y: -280,
            })
        },
    }
}

export function button2(): Override {
    return {
        onTap() {
            controls.start({
                y: 0,
            })
            appState.pageIndex = 0
            console.log(appState.pageIndex)
        },
    }
}

02 物体属性和滚动距离关联

使用 motionValue 和 useTransform 来绑定物体属性和滚动距离。

2020-03-03 10.19.13.gif

import * as React from "react"
import { Frame, Override, motionValue, useTransform } from "framer"
import { Content } from "./canvas"

const contentOffsetY = motionValue(0)

//将此 override 赋予自带 scroll 组件
export function TrackScroll(): Override {
    return {
        contentOffsetY: contentOffsetY,
    }
}

//将此 override 赋予要改变属性的物体
export function card(): Override {
      //滚动距离在[-100, 0, 100]区间变化时,物体缩放在 [0.5, 1, 1.5] 对应变动
    const scale = useTransform(contentOffsetY, [-100, 0, 100], [0.5, 1, 1.5])
    return {
        scale: scale,
    }
}

2020-03-03 10.15.51.gif

import * as React from "react"
import {
    Frame,
    Scroll,
    useAnimation,
    Override,
    motionValue,
    useTransform,
} from "framer"
import { Content } from "./canvas"

const contenOffsetY = motionValue(0)

export function trackScroll(): Override {
    return {
        contentOffsetY: contenOffsetY,
    }
}

//3d变换卡片的父级 frame
export function parent(): Override {
    return {
        perspective: 150,
    }
}
//3d 变换卡片
export function child(): Override {
    const rotateX = useTransform(contenOffsetY, [-100, 0, 100], [-80, 0, 80])
    return {
        rotateX: rotateX,
        originY: 1,
    }
}

03 滚动达到阈值后改变 data 的值

当滚动距离到达设定阈值后,改变 Data() 方法设定的某一值,此值改变后,触发某一物体的动画。本例中当向上滚动距离大于 52 时,卡片开始透明度从 0 到 1 的动画。

2020-03-03 15.38.58.gif

import * as React from "react"
import { Override, Data, motionValue, useTransform } from "framer"

const data = Data({ isPastLimit: false })

const contentOffsetY = motionValue(0)

// 若向上滚动超过 52 ,则 data.isPastLimit 为 true,反之为 false
contentOffsetY.onChange(offset => (data.isPastLimit = offset < -52))

export function TrackScroll(): Override {
    return { contentOffsetY: contentOffsetY }
}

export function ShowCardIfPastLimit(): Override {
    return {
        opacity: 0, // set it to 0 initially
        animate: data.isPastLimit ? { opacity: 1 } : { opacity: 0 },
    }
}

04 视差滚动

滚动层在相同的时间向上滚动 100,是差层只对应向上滚动 50。

2020-03-03 13.34.56.gif

import * as React from "react"
import { Override, motionValue, useTransform } from "framer"

const contentOffsetY = motionValue(0)

export function TrackScroll(): Override {
    return {
        contentOffsetY: contentOffsetY,
    }
}

export function ParallaxLayer(): Override {
    // 向上滚动 100,卡片向上移动 50
    const top = useTransform(contentOffsetY, [0, -100], [125, 75], {
        clamp: false,
    })
    return {
        top: top,
    }
}

2020-06-06 15.09.21.gif
https://framer.com/projects/Drop-on-Scroll-eqP0EQ6172U8MgWIppsV?_ga=2.113808263.129919046.1591273569-105504521.1556699041

05 动态appBar

如 ios 设置页面的 Appbar 效果。

2020-03-03 15.19.01.gif

import * as React from "react"
import { Override, motionValue, useTransform } from "framer"

const contentOffsetY = motionValue(0)

export function TrackScroll(): Override {
    return {
        contentOffsetY: contentOffsetY,
    }
}

export function appBar(): Override {
    const opacity = useTransform(contentOffsetY, [-40, -52], [0, 1], {
        clamp: false,
    })
    return {
        opacity: opacity,
    }
}

06 返回顶部 滚动达到阈值触发动画

controls需要在最外面定义,以便可以跨 function 引用。

2020-03-03 18.59.06.gif

import * as React from "react"
import { Override, useAnimation } from "framer"

// 定义可以在所有函数中调用的变量 controls
let controls

export function TrackScroll(): Override {
    controls = useAnimation()
    return {
        scrollAnimate: controls,
    }
}

export function BackToTop(): Override {
    return {
        onTap() {
            controls.start({
                y: 0,
                transition: {
                    type: "spring",
                    damping: 15,
                },
            })
        },
    }
}

07 Sticky Header 悬浮

2020-03-03 19.41.11.gif

import * as React from "react"
import { Override, motionValue, useTransform } from "framer"

const contentOffsetY = motionValue(0)

export function TrackScroll(): Override {
    return {
        contentOffsetY: contentOffsetY,
    }
}

export function StickyHeader(): Override {
    // [75,0]对应[87,87]为了下拉时蓝色区域不跟着往下移动,[-75,-150]对应[12,12]为了进度条置顶后不再向上滑动
    const top = useTransform(
        contentOffsetY,
        [75, 0, -75, -150],
        [87, 87, 12, 12],
        {
            clamp: false,
        }
    )
    return {
        top: top,
    }
}

15 scroll.framerx.zip
15 scroll_20200314.framerx.zip