props-for-that:让CSS直接读取JS状态的开源库详解

前端交互开发的老难题
在Web开发中,有一个长期存在的痛点:CSS擅长做动画和视觉效果,但它对页面的实时状态几乎一无所知。鼠标位置在哪里、元素是否进入视口、当前的滚动速度是多少、FPS如何——这些信息全都掌握在JavaScript手里。想让CSS用上它们,开发者往往需要手动编写事件监听器、IntersectionObserver、requestAnimationFrame循环,再把结果一个个写进CSS自定义属性中。
这里提到的CSS自定义属性(也称CSS变量)是CSS3引入的一项重要特性,允许开发者通过 -- 前缀定义可复用的值,并通过 var() 函数引用。与Sass、Less等预处理器的变量不同,CSS自定义属性是运行时的、可继承的、可通过JavaScript动态修改的。JS可以通过 element.style.setProperty('--my-var', value) 实时更新变量值,而CSS中所有引用该变量的样式会自动重新计算。这一机制正是 props-for-that 库的技术基石——它本质上是一个高效的「JS到CSS的数据桥梁」。
而IntersectionObserver是浏览器提供的异步观察API,用于检测目标元素与视口之间的交叉状态变化;requestAnimationFrame则是浏览器的帧同步回调机制,确保动画逻辑在每一帧渲染前执行。相比传统的scroll事件监听配合 getBoundingClientRect() 计算,这些原生API在浏览器内部实现,不会阻塞主线程,性能优势显著。但正确管理它们的生命周期(创建、销毁、去抖动)仍然是一件繁琐的事。
这套流程不仅繁琐,还容易踩性能坑。正如原视频作者所说,上周他还专门讨论过"尽可能把更多信息传递给CSS是个好主意"的理念,但真正实践起来"有些东西做起来很烦人"。而现在,Adam的开源库 props-for-that 几乎一举解决了这个问题。
props-for-that 的核心功能
简单来说,这个库把大量原本只有JS才知道的信息,自动同步成CSS可以直接读取的自定义属性(CSS Custom Properties)。用作者的话说,这是"让CSS知道JS现在知道的东西"。
它能暴露的数据非常丰富:
- FPS(帧率)
- 视口尺寸(viewport size)
- 滚动速度(scroll velocity)
- 输入框的实时颜色
- 鼠标指针位置
- 元素的可见性状态
而且这一切的接入成本极低。安装只需一行命令:
npm install props-for-that
然后在JavaScript中导入自动模式即可:
import 'props-for-that/auto';
剩下的工作几乎全在HTML和CSS里完成,开发者不需要再手写任何观察器逻辑。
用 data 属性声明你想要的数据
真正需要做的"唯一一件事",是在HTML元素上添加 data-props-for 属性,列出你想追踪的信息。例如追踪指针的局部位置:
<div class="card" data-props-for="pointer-local"></div>
设置后,打开开发者工具就能看到该元素上出现了实时更新的自定义属性,比如 --live-local-pointer-x-ratio、--live-local-pointer-y-ratio,以及一个用于判断鼠标是否悬停其上的 --pointer-inside——相当于一个内置的hover检测。

性能优化:只追踪可见的元素
很多人第一反应会担心:如果所有元素都在实时追踪鼠标位置、可见性、滚动速度,性能岂不是要崩?
这正是 props-for-that 设计巧妙的地方。作者在视频中演示:当一个元素滚出视口后,它的追踪就会自动停止。也就是说,库只会追踪当前可见、且你明确声明要追踪的属性。这避免了对屏幕外元素的无谓计算,把处理能力用在刀刃上。作者评价道,Adam在这个库里"内置了大量性能方面的考量,非常出色"。
从实现原理来看,这里很可能利用了IntersectionObserver作为「开关」——当元素进入视口时启动对应的追踪逻辑(如mousemove监听、RAF循环),当元素离开视口时自动注销这些监听器。这种「按需激活」的模式避免了传统方案中常见的内存泄漏和CPU空转问题,尤其在长列表或大量卡片组件的场景下效果显著。
live 与 const:两种CSS属性类型
props-for-that 提供了两类值得区分的属性,这也是它设计上的一个亮点。
live 属性:持续实时更新
以 --live-visible 为例,它会随着元素进出视口在 1 和 0 之间来回切换。只要元素在屏幕上,它就是1;离开就变回0。这非常适合需要"来回响应"的场景。
const 属性:只触发一次
而 --const-has-entered 则是常量型的。它初始为0,一旦元素首次进入视口就变为1,此后不再改变,即使你把元素滚出屏幕它依然保持1。

这个区分直接对应两种常见的动画需求:
- 滚动驱动动画(scroll-driven):随滚动位置来回变化,CSS现在已能原生实现,但浏览器支持仍不完美,用live属性可以作为过渡方案;
- 滚动触发动画(scroll-triggered):只在元素首次出现时触发一次,这是当前CSS还无法原生实现的,用const属性正好补上这块空白。
关于这两种动画模式的行业背景值得补充:CSS Scroll-Driven Animations 是W3C正在推进的一项规范(Chrome 115+ 已支持),允许开发者直接用CSS将动画进度绑定到滚动位置,无需JavaScript介入。但截至2024年底,Firefox和Safari的支持仍不完整。而「滚动触发动画」则是另一种模式——动画不随滚动位置连续变化,而是在元素首次出现时一次性播放。这种模式目前没有任何CSS原生方案,传统上完全依赖AOS、ScrollReveal等JS库或手写IntersectionObserver逻辑来实现。props-for-that 的 const 属性恰好以声明式的方式填补了这一空白。
作者也坦言,在演示中他为了方便观察一直用live值(这样动画能反复播放),但"如果是生产环境,我会用const,因为我更喜欢动画只触发一次"。
实战:鼠标跟随高光与滑入动画
视频中最精彩的部分是把这些属性真正用起来。所有的prop都是**无单位(unitless)**的,这是有意为之,方便开发者自由组合使用。
这个设计决策值得展开说明:在CSS中,不同属性需要不同的单位——角度用deg、长度用px或%、时间用s。如果库直接输出带单位的值,就限制了使用场景。而无单位的归一化数值(通常在0到1之间)可以通过 calc() 自由乘以任何单位:乘以 360deg 就是完整旋转角度,乘以 100% 就是百分比位置,乘以 200px 就是像素偏移。这种设计最大化了CSS端的组合灵活性,让同一个数据源可以驱动完全不同类型的视觉效果。
鼠标跟随的高光效果
作者预先在卡片上做了径向渐变高光,位置由 --x 和 --y 控制。接入指针属性后,只需用 calc() 把无单位的比值转成百分比:
--x: calc(var(--live-local-pointer-x-ratio) * 100%);
--y: calc(var(--live-local-pointer-y-ratio) * 100%);
高光就会精准跟随鼠标移动,配合 clamp() 还能限制移动范围,全程无需一行额外的JavaScript。
滚动时的"扭转滑入"动画
更有意思的是滚入动画。作者利用可见性属性做出卡片从倾斜角度"扭转归位"的效果。这里有个反直觉的小技巧——他构造了一个 --is-visible:
--is-visible: calc(1 - var(--live-visible));
当元素可见时,--live-visible 为1,--is-visible 就变成0;不可见时则相反。再把它乘到旋转角度上:
transform:
rotateY(calc(var(--twist-y) * var(--is-visible)))
rotateX(calc(var(--twist-x) * var(--is-visible)));
于是元素在视口外呈倾斜状态,进入视口时旋转角度归零、自然"扭正"。通过给不同卡片设置正负相反的 --twist-y 和 --twist-x,还能让它们朝相反方向翻转,视觉层次更丰富。

配合一个短暂的 transition,整套滑入动画就完成了:
.twist-in {
transition: transform 0.75s;
}

作者强调,最让他惊叹的是"完全不需要设置各种观察器、不用操心JavaScript,属性就这样被传进来并且正常工作了"。
props-for-that 对前端开发的意义
props-for-that 代表了一种值得关注的前端思路转变:让声明式的CSS承担更多交互职责,而把命令式JS的角色压缩到最小。
过去我们习惯用JS监听、计算、再操作DOM或写入样式变量;而现在,只要在HTML里声明一句"我关心什么",CSS就能拿到实时数据自己驱动动画。这不仅让代码更简洁、可维护性更高,也把性能优化的责任交给了经过打磨的库本身。
这种范式转变与更广泛的Web平台演进方向一致。近年来,CSS本身正在快速吸收曾经只能由JS实现的能力:容器查询(Container Queries)让组件能响应自身容器尺寸而非视口尺寸,:has() 选择器让CSS具备了「父选择器」能力,@layer 规则解决了样式优先级管理问题。props-for-that 可以被视为这一趋势的用户空间延伸——在规范尚未覆盖的领域,通过最小化的JS胶水层,让CSS获得更多的环境感知能力。
当然,它并非要取代CSS原生的滚动驱动动画等新特性,而是在浏览器支持尚未成熟的过渡期,以及在CSS暂时无法覆盖的场景(如一次性触发动画)中,提供了一个优雅、高性能的补充方案。对于追求交互体验又不想陷入JS样板代码的前端开发者来说,这个开源库非常值得一试。
核心要点
相关推荐

Claude自主设计蛋白质成功率35%,远超人类专家水平
Anthropic的Claude模型在自主设计靶向疾病蛋白质任务中取得35%实验成功率,远超人类专家10%-15%的平均水平。本文深入解析这一湿实验验证成果对生物医药行业的潜在影响。

Perplexity Discover多语言支持突然消失,国际用户为何不满?
Perplexity Discover新闻资讯功能突然取消多语言支持,仅保留英文内容,引发国际用户强烈不满。本文分析功能回退的可能原因,探讨AI产品国际化面临的资源权衡与用户信任挑战。
