ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Cursor 光标类型集合:用 CSS 与 JavaScript 打造可复制的自定义光标方案

2026/9/28 19:54:53 拓冰建站 浏览量
Cursor 光标类型集合:用 CSS 与 JavaScript 打造可复制的自定义光标方案 1. 光标类型到底有多少种为什么你总记不全做前端页面时cursor这个属性几乎天天见但真要一口气说出所有关键字多数人只能想起pointer、text、move这几个。等到要做一个拖拽排序、表格列宽调整、或者画布缩放功能时才发现浏览器其实内置了一整套光标类型只是平时没系统整理过。cursor是 CSS 里负责“鼠标指针长什么样”的属性它既能用关键字指定系统内置形状也能用图片 URL 加载自定义光标。它适合所有需要跟鼠标交互的前端场景按钮、链接、可拖拽元素、文本输入区、加载状态、帮助提示、以及各种 resize 边缘。换句话说只要用户会用鼠标点你的页面cursor就有用武之地。这篇内容我会把 CSS 关键字光标、JavaScript 动态切换、以及自定义图片光标三块串起来给你一份可以直接复制进项目的配置。核心思路是先用 CSS 变量把光标类型集中管理再用一个 JS 函数按状态切换最后在浏览器里逐项验证。整个过程不需要装任何依赖打开 DevTools 就能跟做。需要说明的是浏览器内置光标的具体外观由操作系统和浏览器共同决定同一关键字在 Windows、macOS、Linux 上长得不完全一样这是正常现象不影响功能。2. 前置准备用 TaoToken 补齐文档与代码生成环节写这类偏“查表 试错”的前端内容最耗时间的往往不是写代码而是查每个关键字的准确拼写和适用场景。我自己的习惯是开一个模型对话窗口把光标关键字列表、CSS 变量写法、JS 切换逻辑一次性问清楚再粘到编辑器里改。如果你也想用这种方式提速可以先把 TaoToken 的 API Key 配好。它的接入地址是https://taotoken.net/api控制台里能创建和管理密钥。拿到 Key 之后无论是接进编辑器插件做代码补全还是接进自己的脚本做批量生成都走同一个入口。具体操作上先打开控制台创建 API Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite创建完在密钥管理页复制出来放到环境变量里别硬编码进前端代码export TAOTOKEN_API_KEY你的key如果你更想先验证模型对光标关键字的理解是否准确可以直接用模型对话页测试https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite把“列出 CSS cursor 所有关键字并说明适用场景”丢进去对比它给的列表和你项目里实际用到的很快就能发现遗漏。接入文档在https://taotoken.net/doc里面有完整的请求格式和参数说明遇到 401 或 429 时先翻这一页。注意API Key 属于敏感凭证只放在服务端或本地环境变量里不要提交到 Git 仓库也不要写进浏览器可见的 JS 文件。3. 可复制配置CSS 变量集中管理光标类型先把光标类型用 CSS 自定义属性收拢到一处。这样做的好处是后面 JS 切换时只需要改一个变量名不用到处写死字符串。:root { /* 基础交互 */ --cursor-default: default; --cursor-auto: auto; --cursor-pointer: pointer; --cursor-text: text; --cursor-move: move; --cursor-help: help; --cursor-wait: wait; --cursor-crosshair: crosshair; /* 方向缩放n 北 s 南 e 东 w 西 */ --cursor-n-resize: n-resize; --cursor-s-resize: s-resize; --cursor-e-resize: e-resize; --cursor-w-resize: w-resize; --cursor-ne-resize: ne-resize; --cursor-nw-resize: nw-resize; --cursor-se-resize: se-resize; --cursor-sw-resize: sw-resize; /* 双向缩放 */ --cursor-ew-resize: ew-resize; --cursor-ns-resize: ns-resize; --cursor-nesw-resize: nesw-resize; --cursor-nwse-resize: nwse-resize; /* 其他 */ --cursor-not-allowed: not-allowed; --cursor-grab: grab; --cursor-grabbing: grabbing; --cursor-zoom-in: zoom-in; --cursor-zoom-out: zoom-out; --cursor-none: none; }这里有几个容易混的点值得单独说。e-resize、w-resize、n-resize、s-resize里的字母分别代表东、西、北、南也就是右、左、上、下。而ew-resize和ns-resize是双向的分别表示水平方向和垂直方向可缩放。nesw-resize和nwse-resize则是两条对角线方向常用于右下角和左上角的缩放手柄。grab和grabbing是一对前者是“可以抓”后者是“正在抓”做拖拽列表时体验很自然。not-allowed适合禁用态按钮zoom-in/zoom-out适合图片预览。定义好变量后在具体元素上引用.btn { cursor: var(--cursor-pointer); } .input-area { cursor: var(--cursor-text); } .drag-handle { cursor: var(--cursor-grab); } .drag-handle:active { cursor: var(--cursor-grabbing); } .resize-handle-se { cursor: var(--cursor-se-resize); } .disabled-btn { cursor: var(--cursor-not-allowed); }如果你要的是自定义图片光标写法是在关键字前面加url()并且必须在末尾保留一个普通关键字兜底否则图片加载失败时浏览器可能不显示任何光标.custom-cursor { cursor: url(./cursors/pen.png) 4 4, var(--cursor-pointer); }url()后面的两个数字是热点坐标也就是光标“实际点击位置”相对图片左上角的偏移。不写的话默认是0 0也就是图片左上角。做画笔类光标时热点通常设在笔尖位置。4. JavaScript 动态切换一个函数搞定状态映射CSS 变量解决了“定义”JS 解决“切换”。下面这个函数接收元素和状态名从映射表里取出对应的 CSS 变量值并写回style.cursor。const cursorMap { default: var(--cursor-default), pointer: var(--cursor-pointer), text: var(--cursor-text), move: var(--cursor-move), help: var(--cursor-help), wait: var(--cursor-wait), crosshair: var(--cursor-crosshair), n-resize: var(--cursor-n-resize), s-resize: var(--cursor-s-resize), e-resize: var(--cursor-e-resize), w-resize: var(--cursor-w-resize), ne-resize: var(--cursor-ne-resize), nw-resize: var(--cursor-nw-resize), se-resize: var(--cursor-se-resize), sw-resize: var(--cursor-sw-resize), ew-resize: var(--cursor-ew-resize), ns-resize: var(--cursor-ns-resize), nesw-resize: var(--cursor-nesw-resize), nwse-resize: var(--cursor-nwse-resize), not-allowed: var(--cursor-not-allowed), grab: var(--cursor-grab), grabbing: var(--cursor-grabbing), zoom-in: var(--cursor-zoom-in), zoom-out: var(--cursor-zoom-out), none: var(--cursor-none), }; function setCursor(el, type) { if (!el) return; const value cursorMap[type]; if (!value) { console.warn([setCursor] 未知光标类型: ${type}); return; } el.style.cursor value; } function resetCursor(el) { if (!el) return; el.style.cursor ; }调用方式很直接const box document.querySelector(#box); setCursor(box, grab); box.addEventListener(mousedown, () setCursor(box, grabbing)); box.addEventListener(mouseup, () setCursor(box, grab)); box.addEventListener(mouseleave, () resetCursor(box));这里有个细节resetCursor把style.cursor置空元素会回退到 CSS 里定义的值而不是强制变成default。这样你在 CSS 里给父级设的光标不会被意外覆盖。如果你要做“根据鼠标位置判断缩放方向”的效果可以结合getBoundingClientRect算距离边缘的像素function handleResizeCursor(e, el, edge 8) { const rect el.getBoundingClientRect(); const x e.clientX - rect.left; const y e.clientY - rect.top; const nearRight x rect.width - edge; const nearBottom y rect.height - edge; const nearLeft x edge; const nearTop y edge; if (nearRight nearBottom) return setCursor(el, nwse-resize); if (nearLeft nearTop) return setCursor(el, nwse-resize); if (nearRight nearTop) return setCursor(el, nesw-resize); if (nearLeft nearBottom) return setCursor(el, nesw-resize); if (nearRight || nearLeft) return setCursor(el, ew-resize); if (nearTop || nearBottom) return setCursor(el, ns-resize); resetCursor(el); }这段逻辑在表格列宽拖拽、画布缩放里很常用实测下来比单纯用 CSS 的resize属性灵活得多。5. 逐项验证在浏览器里确认每种光标都生效配置写完了怎么确认真的生效最稳的办法是做一个临时验证页把关键字逐个渲染出来鼠标移上去看形状变化。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / titlecursor 类型验证/title style :root { --cursor-pointer: pointer; --cursor-text: text; --cursor-move: move; --cursor-help: help; --cursor-wait: wait; --cursor-crosshair: crosshair; --cursor-grab: grab; --cursor-grabbing: grabbing; --cursor-not-allowed: not-allowed; --cursor-zoom-in: zoom-in; --cursor-zoom-out: zoom-out; --cursor-se-resize: se-resize; --cursor-ew-resize: ew-resize; --cursor-ns-resize: ns-resize; } .grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; padding: 24px; } .cell { border: 1px solid #ccc; padding: 20px; text-align: center; font-size: 14px; user-select: none; } /style /head body div classgrid idgrid/div script const types [ pointer, text, move, help, wait, crosshair, grab, grabbing, not-allowed, zoom-in, zoom-out, se-resize, ew-resize, ns-resize ]; const grid document.getElementById(grid); types.forEach((t) { const div document.createElement(div); div.className cell; div.textContent t; div.style.cursor var(--cursor-${t}); grid.appendChild(div); }); /script /body /html打开这个页面后把鼠标依次移到每个格子上观察指针形状是否和文字描述一致。如果某个格子没变化先检查:root里对应的变量名有没有拼错再看 DevTools 的 Computed 面板里cursor最终解析成了什么值。验证自定义图片光标时把图片路径换成真实存在的文件并在 Network 面板确认图片返回 200。如果图片 404浏览器会回退到url()后面那个关键字这也是为什么末尾一定要写兜底值。提示cursor: none会完全隐藏光标验证时容易“找不到鼠标”建议单独放一个格子并做好标记验证完立刻移开。6. 常见报错与排查清单光标完全不生效先确认元素本身有尺寸宽高为 0 的元素鼠标根本进不去。再看是不是被父级的pointer-events: none挡住了这种情况下子元素也收不到鼠标事件。自定义图片光标不显示最常见的原因是图片路径错误或跨域。url()里的路径是相对于 CSS 文件的位置不是 HTML 文件。如果图片在别的域名下需要对方允许跨域否则浏览器会静默回退到兜底关键字。热点坐标对不上url()后面的两个数字是相对图片左上角的偏移单位是像素。图片尺寸建议控制在 32×32 以内太大在部分系统上会被忽略。JS 设置后又被 CSS 覆盖element.style.cursor是行内样式优先级高于普通 CSS 选择器但如果 CSS 里用了!important行内样式也会被压过去。排查时在 DevTools 里看cursor那一行有没有被划掉。移动端没反应cursor是桌面端概念触屏设备上大多数光标关键字没有视觉效果这是预期行为不用当成 bug。wait和progress的区别wait表示程序忙、用户需要等通常是沙漏或转圈progress表示程序在后台忙但用户仍可操作。两者外观接近语义不同按场景选。如果你在接入模型辅助生成这些配置时遇到 401先检查 API Key 是否带上了Bearer前缀遇到 429 则是触发限流降低请求频率即可。密钥管理和文档入口分别是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite7. 把光标方案接进你的工作流光标类型本身不复杂难的是记全、用对、并且在不同状态下切换自然。我现在的做法是项目里建一个cursor.css专门放变量再建一个cursor.js放映射表和切换函数任何组件需要改光标就 import 这两个文件不再散落各处写cursor: pointer。如果你经常做拖拽、缩放、画布类交互建议把第 4 节那个handleResizeCursor封装成可复用的工具函数传入元素和边缘阈值就能自动判断方向。长期写这类交互代码的话用 Coding Plan 把常用片段沉淀成模板会更省事https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite最后留一个实用技巧验证光标时不要只盯着一个浏览器Chrome 和 Firefox 对grab、zoom-in这类关键字的渲染细节有差异跨浏览器项目里最好两个都过一遍。把验证页存成书签下次改光标配置直接打开对照比凭记忆靠谱得多。