直接用 cubic-bezier(0.25, 0.1, 0.25, 1) 不够用,因人脑难凭数值想象曲线;需拖动控制点、实时预览并生成合法 css 的交互工具,且 canvas 中坐标须归一化到 [0,1] 并约束在单位正方形内。

为什么直接用 cubic-bezier(0.25, 0.1, 0.25, 1) 不够用
因为 CSS 的 cubic-bezier() 函数只接受四个数值(两个控制点坐标),但人脑很难凭数字想象出曲线形状。你调了十次 cubic-bezier(0.4, 0.8, 0.6, 0.2),发现 easing 效果还是“卡顿”或“突兀”,问题不在语法,而在缺乏实时视觉反馈。
真正需要的不是写死的函数,而是一个能拖动控制点、同步生成代码、并预览动画效果的交互界面。这类工具核心不在于“画曲线”,而在于把 P1(x1, y1) 和 P2(x2, y2) 的坐标约束在单位正方形 [0, 1] × [0, 1] 内,并实时映射到 CSS 函数字符串。
canvas 绘制贝塞尔曲线时控制点坐标必须归一化
Canvas 的 bezierCurveTo() 接收的是绝对像素坐标,但 CSS 的 cubic-bezier() 要求所有值在 [0, 1] 区间。如果 canvas 宽高是 400×400 像素,你得把鼠标位置 clientX 映射为 (x - left) / 400,再 clamp 到 [0, 1] —— 否则拖出画布就会生成非法 CSS 值,比如 cubic-bezier(-0.3, 1.5, 2.1, -0.7),浏览器会静默忽略整个 transition-timing-function。
- 监听
mousedown时判断是否靠近控制点(用Math.hypot(dx, dy) ) - 拖动中用
mousemove更新点坐标,但每次更新后强制执行:x = Math.max(0, Math.min(1, x)) - 绘制曲线前,把归一化后的
x1, y1, x2, y2乘以 canvas 尺寸转回像素坐标
实时预览动画必须用独立元素 + reset animation
很多人把预览框做成一个固定 div,每次改曲线就改它的 style.transitionTimingFunction。但这样有个坑:如果上一次动画还没结束,新 timing function 会被忽略,导致“看起来没变化”。更糟的是,连续快速拖动时,动画状态混乱,甚至卡住。
正确做法是每次更新都重置动画状态:
- 用一个专用的
<div id="preview-box"></div>,初始无 transition - 更新曲线后,先移除旧 class:
el.classList.remove('animate') - 触发 reflow(例如读取
el.offsetHeight) - 再添加 class 并设置
transition-timing-function,最后加el.classList.add('animate')
否则你会看到预览“延迟响应”或“跳帧”,误以为曲线生成逻辑有 bug。
导出代码要处理小数精度和空格兼容性
用户复制的代码要能直接粘贴进 CSS 文件里,不能有运行时错误。常见翻车点:
-
y1或y2是负数或大于 1?必须拦截并提示“超出 CSS 允许范围”,不能默默截断后导出 - 保留 2 位小数足够(
toFixed(2)),太多位(如0.25000000000000006)反而干扰阅读 - 输出格式严格为:
cubic-bezier(<code>x1,y1,x2,y2),逗号后必须有一个空格,这是 CSS 规范要求,少空格会导致部分旧版 Safari 解析失败 - 别忘了提供“复制到剪贴板”按钮,调用
navigator.clipboard.writeText(),并做降级提示(如不支持则document.execCommand('copy'))
最易被忽略的是:控制点坐标虽然归一化了,但用户可能手动输入非法值覆盖——所以输入框要绑定 input 事件做实时校验,而不是只依赖拖拽逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











