cursor 不支持 css transition——因其为离散状态,无中间值可插值;可通过隐藏原生光标、用绝对定位元素模拟并绑定 js 状态切换实现视觉过渡效果。

transition 无法直接作用于 cursor
CSS 的 transition 属性对 cursor **完全无效**——无论你写 transition: cursor 0.3s 还是 all 0.3s,浏览器都会静默忽略。这不是兼容性问题,而是规范明确不支持:cursor 是离散状态(pointer、text、not-allowed 等),没有中间值可插值,过渡引擎无从下手。
实现“视觉上”的 cursor 动态变化效果
虽然不能真过渡 cursor,但可以通过覆盖层 + 动画模拟出类似体验。核心思路是:隐藏原生光标,用绝对定位的自定义元素(如 <div class="custom-cursor">)跟随鼠标移动,并在状态切换时触发动画。
<ul>
<li>需禁用原生光标:<code>body { cursor: none; }
pointer-events: none 避免干扰交互data-state 属性,再用 CSS 控制其缩放/颜色/尺寸动画transform 触发重排body {
cursor: none;
}
.custom-cursor {
position: fixed;
width: 24px;
height: 24px;
border-radius: 50%;
background: #3b82f6;
transform: translate(-50%, -50%);
pointer-events: none;
z-index: 9999;
transition: transform 0.2s, background 0.2s;
}
.custom-cursor[data-state="hover"] {
transform: translate(-50%, -50%) scale(1.4);
background: #ef4444;
}
.custom-cursor[data-state="text"] {
width: 32px;
height: 16px;
background: #10b981;
border-radius: 2px;
}
cursor 变化时机与 JS 绑定策略
真实项目中,cursor 状态往往取决于元素语义(按钮禁用、文本可选、拖拽区域等)。不要靠全局 mouseenter 盲切,应结合具体场景精准控制:
- 对
button[disabled]或[aria-disabled="true"],统一设为not-allowed - 对
input[type="text"]、textarea、带contenteditable的元素,进入时设text - 悬停可拖拽容器(如
[draggable="true"])时,设grab;拖拽中改grabbing - 避免在滚动容器内监听
mousemove,优先用事件委托到父级
移动端与无障碍的取舍提醒
自定义光标在触摸设备上毫无意义(无 hover、无 cursor 概念),且会破坏屏幕阅读器的焦点指示。上线前务必加检测:
立即学习“前端免费学习笔记(深入)”;
- 用
matchMedia('(hover: hover) and (pointer: fine)')判断是否支持悬停 - 在
prefers-reduced-motion: reduce下应禁用所有光标动画 - 永远保留原生 cursor 作为降级方案,比如只在支持精细指针且启用动画的桌面端启用自定义层
最易被忽略的是:自定义光标元素若未设置 aria-hidden="true",可能被读屏器误读为页面内容。这点常被跳过,但直接影响无障碍合规性。










