# EyeDropper API

> new EyeDropper().open() 为自定义取色器拾取屏幕上任意像素的颜色。本页列出 signal 选项、open() 会以哪些异常拒绝，以及 Firefox 与 Safari 上的回退方案。

EyeDropper API 把浏览器自带的取色器交给页面使用：`new EyeDropper().open()` 暂停页面输入，让用户在屏幕任意位置（包括浏览器窗口之外）点选一个像素，并以 `#rrggbb` 字符串返回该像素的颜色。有了它，自定义取色器不必靠截图来采样颜色。

Chrome 95 与 Edge 95 仅在桌面端提供；BCD 对 Android 上的 Chrome 和 Android WebView 记录为 `version_added: false`。Firefox 与 Safari 没有实现（BCD `api.EyeDropper`）。Chrome 96 把该接口改为 `[SecureContext]`，所以在 `localhost` 以外的 `http://` 源上 `window.EyeDropper` 为 `undefined`。

## 语法

```js
const eyeDropper = new EyeDropper();

eyeDropper.open()
eyeDropper.open(options)
```

构造函数不接受参数，也不产生任何可见效果。`open()` 返回 `Promise<ColorSelectionResult>`，结果唯一的成员 `sRGBHex` 是一个 `DOMString`，值为合法的简单颜色，例如 `"#3366ff"`。Promise 挂起期间页面处于「取色模式」，收不到任何 UI 事件。`open()` 必须带瞬时用户激活调用，所以应放在 `click` 或 `keydown` 处理函数内。

## 参数

`open()` 接受一个可选的 `options` 参数，类型为只有一个成员的 `ColorSelectionOptions` 字典。

| 成员 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `signal` | `AbortSignal` | 否 | 中止该信号会退出取色模式、关闭浏览器 UI，并以信号的中止原因拒绝挂起的 Promise（未给 `abort()` 传其他原因时为 `AbortError` `DOMException`）。 |

兑现值 `ColorSelectionResult` 字典只有一个成员 `sRGBHex`，是六位的 `#rrggbb` 字符串。规范不暴露 alpha 通道，半透明像素按屏幕上合成后的颜色返回。

## 异常

`open()` 以下列之一拒绝其 Promise。前两项在任何 UI 出现之前同步检查。

| 异常 | 条件 |
|---|---|
| `NotAllowedError` | 窗口没有瞬时用户激活。 |
| `InvalidStateError` | 本窗口已有另一个取色器处于打开状态。 |
| `AbortError` | 用户在选定像素前取消了选择（Chrome 中按 Escape）。 |
| `OperationError` | 浏览器无法呈现其 UI 或无法读取屏幕内容；Chrome 在该功能被策略禁用时也用它。 |
| 信号的中止原因 | 调用 `open()` 时 `options.signal` 已中止，或 Promise 挂起期间被中止。 |

:::observed
在 Chrome 中，从 `setTimeout` 回调调用 `open()` 会以 `NotAllowedError: EyeDropper::open() requires user gesture.` 拒绝；第一次 `open()` 尚未完结时再次调用会以 `InvalidStateError: EyeDropper is already open.` 拒绝；按 Escape 取消会以 `AbortError: The user canceled the selection.` 拒绝；取色器不可用时以 `OperationError: EyeDropper is not available.` 拒绝。四条字符串都在 Chromium 的 [`eye_dropper.cc`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/eyedropper/eye_dropper.cc) 中（chromium.googlesource.com）。
:::

## 示例

两个示例都先检测 `"EyeDropper" in window` 再显示取色按钮，并在页面里保留一个 `<input type="color">`，作为 Firefox、Safari 和所有移动端浏览器的路径。

### 取色，不支持时回退到原生颜色输入框

构造函数缺失时隐藏取色按钮、保留颜色输入框；输入框让每个浏览器都有可用的颜色选择器，只是不能采样页面之外的像素。

```js
const pickButton = document.querySelector("#pick");
const colorInput = document.querySelector("#color");

if (!("EyeDropper" in window)) {
  pickButton.hidden = true;
  colorInput.hidden = false;
} else {
  pickButton.addEventListener("click", async () => {
    const eyeDropper = new EyeDropper();
    try {
      const { sRGBHex } = await eyeDropper.open();
      colorInput.value = sRGBHex;
    } catch (err) {
      if (err.name !== "AbortError") console.error(`${err.name}: ${err.message}`);
    }
  });
}
```

用户按 Escape 时 `AbortError` 是正常结果，所以处理函数忽略它，只上报其他名称。

### 用超时或第二个按钮取消取色

取色不该无限期停留时，传入 `AbortSignal`。`AbortSignal.timeout()` 以 `TimeoutError` 作为原因拒绝，`catch` 分支据此区分用户自己的取消。

```js
async function pickWithTimeout(ms) {
  if (!("EyeDropper" in window)) return null;

  const eyeDropper = new EyeDropper();
  try {
    const { sRGBHex } = await eyeDropper.open({ signal: AbortSignal.timeout(ms) });
    return sRGBHex;
  } catch (err) {
    if (err.name === "TimeoutError") console.info("取色器超时关闭");
    return null;
  }
}

document.querySelector("#pick").addEventListener("click", async () => {
  const hex = await pickWithTimeout(10_000);
  if (hex) document.documentElement.style.setProperty("--accent", hex);
});
```

返回 `null` 同时覆盖了不支持的浏览器、超时和用户取消，调用方只需一个分支而不是三个。

## 另请参阅

- [Async Clipboard API](/zh/reference/capabilities/clipboard/)，把采样到的值复制出页面
- [Local Font Access API](/zh/reference/capabilities/local-font-access/)，另一个仅桌面 Chromium 的创作工具类 API
- [Screen Capture API](/zh/reference/capabilities/screen-capture/)，读取页面之外像素的更重的方式
- [EyeDropper API](https://wicg.github.io/eyedropper-api/)（wicg.github.io）
- [Picking colors of any pixel on the screen with the EyeDropper API](https://developer.chrome.com/docs/capabilities/web-apis/eyedropper)（developer.chrome.com）