How to quickly implement a color picker

Table of contents

color model

The difference between HSV and HSL

Implement the selector

hue

Compute the slider position of the hue column

Saturation and Brightness

Calculate these two values

hsv to rgb

transparency

HSL based color picker

Use the canvas settings panel


When doing front-end interface development, if you need to change the color, you need to use the color selector. In response to this problem, the first thought is naturally that H5 provides input color, which can be realized. But not surprisingly, IE does not support it. Moreover, the implementation of chrome is not the same as that of firefox, as shown in the figure below:

The picture on the left is from chrome, a color picker control implemented by the browser itself; the picture on the right is from firefox, which introduces the color picker control that comes with the operating system.

In view of these differences, it is necessary to implement a unified color selector component, for example, let's implement a selector similar to the one that comes with the chrome browser.

To make this component, one of the first things you need to know is the color model.

color model

In our front-end development, the most common color model is RGB, that is, the red, green and blue three-primary color model, and there are only two color models that the front-end browser can support color display: RGB and HSL.

Among them, the RGB model is to mix the values ​​of the three colors within the range of 0-255 into one color, which is more friendly to the machine, but it is not very suitable for people to choose colors intuitively. Therefore, the usual color picker generally uses two other models that are more visually oriented: HSV and HSL. For detailed knowledge of the color model, see the previous article: Basic knowledge of the color model .

The color picker that comes with the chrome browser is based on HSV.

The difference between HSV and HSL

HSV is based on hue H (hue), saturation S (saturation), and lightness V (Value); HSL is based on hue H (hue), saturation S (saturation), and brightness L (lightness).

Among them , the hue is the same, and the essence is a color value.

In these two models, the hue is based on the six main colors, which are arranged on a circle at intervals of 60 degrees.

The six main colors are: 0° red, 60° yellow, 120° green, 180° cyan, 240° blue, 300° magenta, 360° red .

When designing the layout at the front end, the rings are often treated as rectangular color blocks, and the linear gradient of the color is used for segmentation processing, and the angle is converted into a certain ratio, as shown below:

There is a difference between saturation and brightness . In order to facilitate the understanding of the color selector, this article summarizes a simple description, as follows:

  • The S saturation in HSV reflects the value of white mixed in the hue color, showing the change from white to hue color;
  • The S saturation in HSL reflects the value of gray mixed in the hue color, showing a change from gray to hue color.
  • The V lightness in HSV reflects the transition from black to hue (H) color.
  • The L brightness in HSL reflects the transition from black to hue (H) color and then to white.

In addition, it needs to be clear that the value range of each part: Hue hue: 0 - 360; saturation and brightness: 0 ~ 100%.

The difference in specific implementation, we can continue to look down.

Implement the selector

hue

The principle of hue has been introduced above, HSV is the same as HSL. It is also very easy to implement at the code level. In the way of css and div, only the linear gradient of the color is required:

<div class="div-bar">
  <!-- 色相柱 -->
  <div id="colorBar" class="color-bar"></div>
  <!-- 滑动块 -->
  <div id="divSlider" class="div-slider"></div>
</div>
.div-bar {
  position: relative;
  width: 10px;
  height: 200px;
  margin-left:15px;
  cursor: pointer;
}
.color-bar {
  background: linear-gradient(180deg, #f00, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00);
  width: 10px;
  height: 200px;
  margin-left: 20px;
}
.div-slider {
  ...
  width: 8px;
  height: 8px;
}

Here is the vertical hue column, width and height 10 * 200, if you need to change the width and height layout horizontally.

Compute the slider position of the hue column

To move the slider to obtain position information, you first need to monitor the mouse event on the hue column, as follows:

  colorBar.addEventListener('mousedown', function(e) {
    setHueSlider(e)
    document.addEventListener('mousemove', setHueSlider)
  })

  document.addEventListener('mouseup', function() {
    document.removeEventListener('mousemove', setHueSlider)
  })

What needs to be processed in the setHueSlider function is the position of the slider point on the hue column, and then a hue value is obtained by calculating the ratio:

  const clientRect = colorBar.getBoundingClientRect()
  // 获取位置信息,色柱高度-colorBarHEIGHT
  let yDiff = event.clientY - clientRect.top
  yDiff = yDiff > colorBarHEIGHT ? colorBarHEIGHT : (yDiff < 0 ? 0 : yDiff)
  // 设置滑块的位置,防止滑块脱离色柱
  const yTop = yDiff < 9 ? 0 : (yDiff - 9)
  divSlider.style.top = yTop + 'px'
  // 色相在360度以内,计算对应的比例值, 0 - 360
  color.hue = Math.round((yDiff / colorBarHEIGHT) * 360 * 100) / 100 | 0

Through the calculation of the position ratio of the slider of the hue column, we can get the corresponding hue value.

Saturation and Brightness

In addition to the hue column, we also need to set a color panel based on saturation and brightness. At this time, the above introduction to the difference between these two pieces will come in handy.

The color panel is generally a rectangular area. Take HSV as an example. As mentioned above, the saturation of HSV can be simplified as the change from white to hue color. If the current hue color is red, the saturation is the change from white to red; and the brightness reflects the change from black to red . When we design the panel, we use a rectangular panel area:

  • If the background color of the panel is set to the hue color - red;
  • Set the saturation from left to right, and you can set the transparency change from left to right on the white, that is, the leftmost white, to the rightmost fully transparent, linear gradient;
  • Set the brightness from bottom to top, you can have a linear gradient from black at the bottom to completely transparent at the top.

In this way, the color panel in the HSV model can be simplified. In terms of specific implementation, it can be understood through the following figure:

Of course, saturation and brightness can be swapped horizontally or vertically.

To set the color panel, just use div+css. There are many ways to implement the code. The method described below uses only one div to implement a color panel with a width and height of 300 * 200. (Other ways include using two divs or using pseudo-classes, etc.):

<div class="div-panel">
  <!-- 颜色面板 -->
  <div id ="colorPanel" class="color-panel" style="background-color: #f00;"></div>
  <!-- 滑块 -->
  <div id="divPicker" class="div-picker"></div>
</div>
.div-panel {
  position: relative;
  width: 300px;
  height: 200px;
  cursor: pointer;
  overflow: hidden;
}
.color-panel {
  position: relative;
  height: 200px;
  width: 300px;
  background: linear-gradient(to top, #000, transparent), linear-gradient(to right, #FFF, transparent);
}
.div-picker {
  ...
  width: 12px;
  height: 12px;
}

Calculate these two values

It is still necessary to monitor mouse events and obtain mouse position information. Event monitoring is basically the same as that of Hue, and they are all three mouse events, which are skipped here.

Directly see how to calculate saturation and brightness. According to the previous introduction and layout implementation, the horizontal X axis of the panel we defined is the saturation, and the vertical Y axis of the panel is the brightness , so it can be calculated as follows:

  // 计算鼠标移动位置,面板宽度panelWIDTH,高度panelHEIGHT
  const clientRect = colorPanel.getBoundingClientRect()
  let yDiff = event.clientY - clientRect.top
  let xDiff = event.clientX - clientRect.left
  xDiff = xDiff > panelWIDTH ? panelWIDTH : (xDiff < 0 ? 0 : xDiff)
  yDiff = yDiff > panelHEIGHT ? panelHEIGHT : (yDiff < 0 ? 0 : yDiff)

  // 设置滑块位置,保证滑块至少一半在面板内
  const yTop = yDiff - 6
  const xLeft = xDiff - 6
  divPicker.style.top = yTop + 'px'
  divPicker.style.left = xLeft + 'px'

  // 饱和度和明度,这里没有取百分比的值,后续转换时需要注意
  color.saturation = Math.round(xDiff / panelWIDTH * 100)
  color.value = Math.round((1 - yDiff / panelHEIGHT) * 100)

hsv to rgb

Through such calculation, the three values ​​corresponding to HSV can be obtained, and then converted into corresponding RGB color values.

const hsvToRgb = function (hue, saturation, value) {
    saturation = saturation * 255 / 100 | 0
    value = value * 255 / 100 | 0

    if (saturation === 0) {
      return [value, value, value]
    } else {
      satVal = (255 - saturation) * value / 255 | 0
      ligVal = (value - satVal) * (hue % 60) / 60 | 0
      if (hue === 360) {
        return [value, 0, 0]
      } else if (hue < 60) {
        return [value, satVal + ligVal, satVal]
      } else if (hue < 120) {
        return [value - ligVal, value, satVal]
      } else if (hue < 180) {
        return [satVal, value, satVal + ligVal]
      } else if (hue < 240) {
        return [satVal, value - ligVal, value]
      } else if (hue < 300) {
        return [satVal + ligVal, satVal, value]
      } else if (hue < 360) {
        return [value, satVal, value - ligVal]
      } else {
        return [0, 0, 0]
      }
    }
  }

transparency

In the three color models of RGB\HSV\HSL, transparency is an independent attribute, which will not affect the realization of specific colors, and this value can be processed separately.

Therefore, if we need transparency, the layout can be the same as the hue column, using a strip-shaped div to calculate the currently selected transparency value by calculating the position ratio. The layout is as follows:

  <div class="div-alpha">
    <!-- 透明柱状 -->
    <div id="alphaBar" class="alpha-bar" style="linear-gradient(180deg, #f00, transparent)"></div>
    <!-- 滑块 -->
    <div id="divAlphaSlider" class="div-slider"></div>
  </div>
  .div-alpha {
    background-image: linear-gradient(
      45deg,#c5c5c5 25%,transparent 0,transparent 75%,#c5c5c5 0,#c5c5c5),linear-gradient(
      45deg,#c5c5c5 25%,transparent 0,transparent 75%,#c5c5c5 0,#c5c5c5);
  }

In terms of calculation, it is basically the same as the hue column, except that the value of transparency is 0-100:

color.alpha = Math.round(100 - yDiff / alphaBarHEIGHT * 100) / 100

With the value of transparency, when obtaining the color value, just add transparency to RGBA or HSLA.

HSL based color picker

The difference between HSV and HSL has been explained above. According to their respective characteristics, only a few changes are required to implement the HSL selector.

First of all, Hue hue and transparency, there is no difference between the two, they are exactly the same, no need to make any changes.

Secondly, it is necessary to change the color panel of saturation and brightness. HSL only needs to change one point in the layout, that is, the css style of the panel:

  .color-panel {
    position: relative;
    height: 200px;
    width: 300px;
    /*横向X轴饱和度设置为灰的渐变,纵向Y轴设置为白到透明到黑*/
    background: linear-gradient(to bottom, #fff 0%, transparent 50%, transparent 50%, #000 100%), 
      linear-gradient(to right, #808080 0%, transparent 100%);
  }

After changing the color panel, the way to obtain the values ​​of HSL's hue, saturation, brightness, and transparency is the same as in HSV mode.

Note that if the HSLA color obtained at this time is to be used in RGBA, it needs to be converted from HSL to RGB . For the conversion function, see the basic knowledge of the color model .

In summary, a color picker is completed.

Use the canvas settings panel

In addition to laying out the color picker based on div+css, we can also use canvas to handle the color picker.

In the layout, the hue, panel, and transparency are all replaced by canvas. Since the canvas can also use linear gradients, there is no problem in setting the UI layout of the color.

The design of the slider is roughly the same, only minor changes are needed, because the way to get the color is different after using the canvas.

In the hue and panel of the canvas, the pixel at the corresponding position of the canvas can be obtained directly, and the pixel itself is an rgba color value.

So the benefit of this is that we can omit the conversion process and get the available color values ​​directly .

Of course, whether it is a div or a canvas, because the layout method and the method of obtaining the color value are different, there will be a difference in the number of color values ​​​​that can be obtained, especially the canvas method, which is related to the pixels of the canvas itself.

However, both methods can have more than a few million color values, fully meeting our needs.

Guess you like

Origin blog.csdn.net/jimojianghu/article/details/122615154