diff --git a/dev/examples/viewport-scroll.tsx b/dev/examples/viewport-scroll.tsx
new file mode 100644
index 0000000000..9f2529c45a
--- /dev/null
+++ b/dev/examples/viewport-scroll.tsx
@@ -0,0 +1,34 @@
+import * as React from "react"
+import { motion, useViewportScroll, useTransform } from "@framer"
+
+const Item = motion.div({
+ default: {
+ width: 100,
+ height: 100,
+ marginBottom: 100,
+ },
+})
+
+const ItemColor = ({ i, scrollY }) => {
+ const base = -500 + i * 200
+
+ const backgroundColor = useTransform(
+ scrollY,
+ [base, base + 100, base + 200, base + 300],
+ ["rgba(255, 255, 255, 0.2)", "rgba(255, 255, 255, 1)", "rgba(255, 255, 255, 1)", "rgba(255, 0, 0, 0.2)"]
+ )
+
+ return
+}
+
+export const App = () => {
+ const viewportScroll = useViewportScroll()
+
+ return (
+
+ {Array.from(new Array(100), (x, i) => (
+
+ ))}
+
+ )
+}
diff --git a/package.json b/package.json
index d3d4a2b0f7..a481a1cb48 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,7 @@
"webpack-dev-server": "^3.1.10"
},
"dependencies": {
- "@popmotion/popcorn": "^0.2.0",
+ "@popmotion/popcorn": "^0.3.0",
"hey-listen": "^1.0.5",
"popmotion": "8.5.3",
"style-value-types": "^3.0.7",
diff --git a/src/hooks/use-transform.ts b/src/hooks/use-transform.ts
index 391f925b30..8a2fe27278 100644
--- a/src/hooks/use-transform.ts
+++ b/src/hooks/use-transform.ts
@@ -46,8 +46,6 @@ import { interpolate } from "@popmotion/popcorn"
* };
* ```
*
- *
- *
* @param {MotionValue} value - The `MotionValue` to transform
* @param {number[]} from - A linear numerical sequence.
* @param {string[] | number[]} to - A series of numbers, colors or
@@ -58,9 +56,9 @@ export const useTransform = (value: MotionValue, from: number[], to: string[] |
return useMemo(
() => {
if (transformedValue.current) transformedValue.current.destroy()
- transformedValue.current = value.addChild({
- transformer: interpolate(from, to),
- })
+
+ const transformer = interpolate(from, to)
+ transformedValue.current = value.addChild({ transformer })
return transformedValue.current
},
[value, ...from, ...to]
diff --git a/src/hooks/use-viewport-scroll.ts b/src/hooks/use-viewport-scroll.ts
new file mode 100644
index 0000000000..3e00ff5c80
--- /dev/null
+++ b/src/hooks/use-viewport-scroll.ts
@@ -0,0 +1,25 @@
+import { useMotionValue } from "../motion-value/use-motion-value"
+import { useEvent } from "../events/use-event"
+
+/**
+ * `useViewportScroll` provides `MotionValue`s that update when the viewport scrolls.
+ *
+ * This makes it possible to transform viewport scroll into other values.
+ *
+ * For instance, highlighting different table of contents items to correspond with page scroll.
+ */
+const useViewportScroll = () => {
+ const x = useMotionValue(0)
+ const y = useMotionValue(0)
+
+ const onScroll = () => {
+ x.set(window.pageXOffset)
+ y.set(window.pageYOffset)
+ }
+
+ useEvent("scroll", window, onScroll, { passive: true })
+
+ return { x, y }
+}
+
+export { useViewportScroll }
diff --git a/src/index.ts b/src/index.ts
index 62c5a01b7e..911059b2c2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,9 +1,10 @@
import { motion } from "./motion"
-import { useMotionValue } from "./hooks/use-motion-value"
+import { useMotionValue } from "./motion-value/use-motion-value"
import { useTransform } from "./hooks/use-transform"
import { usePose } from "./hooks/use-pose"
+import { useViewportScroll } from "./hooks/use-viewport-scroll"
-export { motion, useMotionValue, useTransform, usePose }
+export { motion, useMotionValue, useTransform, usePose, useViewportScroll }
export { useMouseEvents, useTouchEvents, usePointerEvents } from "./events"
export { usePanGesture } from "./gestures"
diff --git a/src/hooks/use-motion-value.ts b/src/motion-value/use-motion-value.ts
similarity index 95%
rename from src/hooks/use-motion-value.ts
rename to src/motion-value/use-motion-value.ts
index 4bede2a6a1..468d407835 100644
--- a/src/hooks/use-motion-value.ts
+++ b/src/motion-value/use-motion-value.ts
@@ -1,5 +1,5 @@
import { useMemo } from "react"
-import { motionValue } from "../motion-value"
+import { motionValue } from "."
/**
* For advanced use-cases, you can assume external control of the motion values used by `motion` components.
diff --git a/src/motion/__tests__/component.test.tsx b/src/motion/__tests__/component.test.tsx
index 4bd3a0a7be..18fbe7ec5f 100644
--- a/src/motion/__tests__/component.test.tsx
+++ b/src/motion/__tests__/component.test.tsx
@@ -1,7 +1,7 @@
import { fireEvent, render } from "react-testing-library"
import { motion } from "../"
import * as React from "react"
-import { useMotionValue } from "../../hooks/use-motion-value"
+import { useMotionValue } from "../../motion-value/use-motion-value"
import { useTransform } from "../../hooks/use-transform"
import { usePose } from "../../hooks/use-pose"
import styled from "styled-components"
diff --git a/yarn.lock b/yarn.lock
index d1113013dc..604017eb8b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -79,6 +79,16 @@
hey-listen "^1.0.5"
style-value-types "^3.0.7"
+"@popmotion/popcorn@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@popmotion/popcorn/-/popcorn-0.3.0.tgz#d75da7a0def2a949016d1cd3794f36514d69019f"
+ integrity sha512-iTPnu07k8mGBuJEt9NhAJ132sd0dNG9/AwqzJeKHjZA9aqtxnP9X0AFbklJrGc18HdPtdzwvGufpcJerSMGdvQ==
+ dependencies:
+ "@popmotion/easing" "^1.0.1"
+ framesync "^4.0.1"
+ hey-listen "^1.0.5"
+ style-value-types "^3.0.7"
+
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"