How to build an eye-dropper for color selection?


#1

I’ve built a custom color picker for my application, and I’d like to let users pick colors with an eye-dropper, like in Photoshop or Sketch.

So the user will click the eye-dropper icon, then they will click anywhere on any of their screens, and then the color under the mouse cursor will be inserted into the color picker.

Is it possible to build this using Electron APIs?

I’m imagining something super hacky and terrible, like…

  • Capture all desktops via Desktop Capturer (or at least capture the desktop the mouse is on)
  • Cover all screens with transparent windows so I can detect clicks even outside of the BrowserWindow
  • On click, get cursor coordinates via screen.getCursorScreenPoint() (or perhaps window.onBlur instead of click, since it’s simpler, and then I don’t need transparent windows)
  • Look at the captured desktop image to get the color at these coordinates
  • Hide all transparent windows and stop capturing desktop

I’m not even sure if that would work, but it seems overkill. Is there a better way, or has anyone done this already? :slight_smile:


#2

I’m an Electron noob myself but I don’t see any other way of making this work they way you want it (with clicks).

A cleaner option IMO would be to use a global shortcut.

  1. The user triggers a global shortcut like CMD+ALT+CONTROL+C
  2. Capture screen bitmap
  3. Extract color from pixel using mouse position screen.getCursorScreenPoint()
  4. Show some UI to let the User the color has been captured. Maybe with a portion of the screen capture bitmap enlarged and the captured pixel highlighted.

It’s faster than clicking a few times, and there’s no need for the hacky transparent layer for capturing clicks.

That’s how I’d do it, although like I said, I’m just starting out with Electron and maybe someone with more experience can show us a better way.


#3

That makes sense - seems like a reasonable compromise of UX and feasibility of implementation.

I’m probably going to try window.onBlur as a proxy for actually detecting a click… and handle click within the window separately. Not sure how well it’ll work, but doesn’t seem too bad. If it doesn’t work out, I’ll try the global shortcut approach.

It’d be nice if there were something simple like screen.captureScreenshot() or even screen.getPixelAtPoint() :slight_smile:


#4

You capture a screenshot using desktopCapturer.

Shouldn’t be too hard to make a wrapper to getPixelAtPoint().


#5

Hey @dabbott look what I’ve found.

It’s a Node module for desktop automation called RobotJS which does have what you are looking for.

//Get pixel color under the mouse. 
var robot = require("robotjs");

//Get mouse position. 
var mouse = robot.getMousePos();

//Get pixel color in hex format. 
var hex = robot.getPixelColor(mouse.x, mouse.y);
console.log("#" + hex + " at x:" + mouse.x + " y:" + mouse.y);

It’s written in C but it should work with Electron, and it’s cross platform.


#6

An alternative to @Pier’s excellent suggestion which keeps it purely JS is paper.js.

The first example on that page shows how to change the colour of an different object based on where the cursor is pointed, using the desktopCapturer mentioned before you should be able to achieve what you’re looking for.


#7

Similar to what @dabbott needed, my goal was to create an application that:

  1. is cross-platform, windowed, uses Node.js, and works on systems with multiple displays/monitors
  2. gives a continuous read-out of the current pixel color under my cursor

Electron was my choice to accomplish to #1, but it had its challenges with #2.

The main issue I ran into with desktopCapturer was that I was forced to take full-resolution screenshots/thumbnails of every display when I only needed one of them (the display with my cursor on it). This took a considerable amount of resources every polling interval. (By the way, I ended up using getImageData(0, 0, 1, 1).data instead of PaperJS to get the pixel color.)

The main issue I ran into with RobotJS was that the base code was originally developed with the assumption that the primary display would be the only screen used. I ended up having to branch off and make my own tweaks to get it to work on every display for every major platform (Windows, Mac, Linux).

In the end, I chose RobotJS over Electron’s desktopCapturer to accomplish #2 above because it was magnitudes more performant. With just 2 monitors at 1920x1080 and on a modern 3.5 GHz quad-core, RobotJS maintained <1% CPU usage at a 200ms polling interval while the desktopCapturer approach would max out a core (25%) at a 500ms interval.


#9

Hey @jareddgotte,
Did you choose RobotJS over the desktopCapturer only because of performance? Also, I was wondering whether the RobotJS fork you did, is public. I have been smashing my head on the wall over the last couple of days:)


#10

Hello @natamas, I needed a way to get the current pixel color under my cursor. That’s all I used RobotJS for. Once I got RobotJS working, it was as simple as doing what @Pier posted just above. However, the Electron-only route took 3+ times as much code to accomplish almost the same thing while—like I mentioned—also maxing out 1 of my CPU cores and not being as responsive when executed.
The fork I made of RobotJS is not public. I’m indecisive about making it so since I only made a few minor tweaks, and it’s not fully tested on all monitor configurations (nor have I tested it on Mac or Linux). Basically I just took the concept in its PR 307 and continued replacing only the necessary instances of MMPoint and size_t with MMSignedPoint and int32_t until the code finally began working. :slight_smile:


#11

@jareddgotte. Thanks a million. I will give it a try.:slight_smile: