在Chrome浏览器中最快速实现取色器(颜色吸管)的方法

6,429 阅读4分钟

说到网页拾色器(颜色吸管工具)在前端的实现方法,通常我们会想到先进行网页截图,然后通过Canvas绘制截图,此时利用Canvas所提供的的API即可解析出图片每个像素点颜色,最后通过获取鼠标定位,得到当前的坐标来进行颜色选择。

在这种方式下通常问题出在第一个步骤,就是进行网页截图的方法,在前端网页截图常见的工具有:html2canvasdom-to-image 等,然而这些库的实现原理都是通过解析DOM然后转换语法(CanvasSvg)绘制再导出图片,虽然这是目前实现截图操作的唯一出路,但我们要清楚的是这样做不可避免会出现以下问题:

  1. 对于复杂网页场景解析可能会出现缺失或错误
  2. 无法避免 CanvasSvg 的渲染结果与网页呈现效果存在差异的问题
  3. 生成效率太慢,解析绘制过程会造成卡顿,非常影响用户体验

所以基于此思路做出来的颜色吸色器尽管在设计初期就考虑可用性,也仍然无法避免上述问题,最多只能应用于简单网页的场景当中,但这通常又与事实相违背——因为简单的网页往往极少有使用到吸色器的业务场景。

原生方案

如果说 JS 的实现方法是令人绝望的,那么把问题交给浏览器原生方法可能是未来最好的解决方案之一,事实上原生 HTML 早已提供了一种实现颜色选择的元素,它就是我们所熟悉的 input 标签。

<input type="color">元素<input>元素中的一个特定种类,用来创建一个允许用户使用颜色选择器,或输入兼容 CSS 语法的颜色代码的区域。(不支持 Alpha 通道)

此元素的外观会因浏览器不同而不同,可能是简单的验证颜色输入格式的文本框,也可能使用平台原生或自定义样式的颜色选择器。其 UI 除要能接收文本格式的颜色外,未要求其他特性。(更多信息

关于"元素的外观会因浏览器不同而不同",先给大家感受下 Safari 下显示效果,需要额外点击“显示颜色”调出系统调色盘,非常的别扭:

image.pngimage.png

但是谷歌浏览器下的实现体验已经非常好了:

2022-09-06 13.43.45.gif

可以看到原生方式下的吸管工具都是系统级别的,也就是说除了网页上的颜色以外,整个屏幕的任何可见元素都可以吸取,而且启动速度非常快,使用流畅不卡顿。

我在第一次了解该元素时就在想:如果这个原生吸管工具可以单独拿出来用就好了,这样可以通过自己开发一个颜色选择器来弥补原生颜色选择器不支持 Alpha 通道的缺点,而原生吸管工具则可以解决目前 JS 很难实现吸色的问题。于是搜索一番后发现真的有一个 BOM 的构造函数:EyeDropper(),它支持创建吸管工具的实例,以提供用户可使用的颜色选择工具。

注意:该功能仍是一项实验性技术,请谨慎在生产环境中使用,另外如果部署到线上发现失效,请检查站点是否已开启 HTTPS,如不在安全上下文环境中,无论浏览器是否支持该方法都不会生效(本地开发环境似乎不受影响)。

小试牛刀

以 Vue 为例,我们可以像这样编写一个最简单的 Demo

<template>
    <button v-if="hasEyeDrop" @click="nativePick">点击取色</button>
    <input v-else type="color" @input="nativePick" />
</template>

<script>
export default {
  data() {
    return {
      hasEyeDrop: 'EyeDropper' in window
    };
  },
  methods: {
    async nativePick (e){
     const val =  e ? e.target.value : null
      if (val) {
        console.log('获得颜色: ' + val)
      } else {
        const eyeDropper = new window.EyeDropper() // 初始化一个EyeDropper对象
        console.log('按Esc可退出')
        try {
          const result = await eyeDropper.open() // 开始拾取颜色
          console.log('获得颜色: ' + result.sRGBHex)
        } catch (e) {
          console.log('用户取消了取色')
        }
      }
    }
  },
};
</script>
支持吸色器时不支持则调用原生input
image.pngimage.png

亲自体验

当然我们还可以让原生吸色器补足取色功能的同时,自定义一个透明度滑块来支持改变色值 Alpha 通道,实现更复杂的应用场景:

2022-09-06 15.35.49.gif

适用场景

目前该方法在兼容性方面只有 ChromeEdgeOpera 这三个浏览器在全力支持,所以适用范围并不广泛,但是由于其在谷歌浏览器上体验实在优秀,还是非常推荐在必要时尽可能使用它,真的太香了!

image.png

另外提一嘴,我觉得在 Electron 中应当是非常契合的,虽然 Electron 有打包体积过大的缺点,但这个缺点也意味着它拥有绝对控制浏览器版本的权利,所以能放心地使用新特性,通过它就可以在桌面程序中用三两行代码便利地实现一个流畅的屏幕取色器了!