Let me get my head around this

Although we have a ready-made HTML control, there are two reasons to abandon it.

  • Function cannot be customized
  • Different browsers have different styles

So let’s implement one ourselves

As usual, there is no way to find a project, this time I found a project called Iconfont.

Then find a predestined icon.

Well, never mind the icon itself, we can see that there is a color selector here, so let’s click on it and have a look.

You can see that this color selector has three parts

  • Color memory board
  • Color selection area
  • Input box and confirm button

Let’s start with how the color picker is implemented. After all, this is the core of the color picker.

As you can see, it is implemented in SVG, which I won’t go into here.

The color block for tag 1 is defined by two RECts of SVG.

<rect x="0" y="0" width="100%" height="100%" fill="url(#gradient-white)"></rect>
<rect x="0" y="0" width="100%" height="100%" fill="url(#gradient-black)"></rect>
Copy the code

The four arguments x, y, width, and height set the color block to the width and height of the entire parent element.

What does fill=”url(#gradient-white)” mean?

After my search, I got the following results:

To achieve the gradient effect, set the gradient of the element corresponding to the URL (#gradient-white) to rect.

#gradient-white is an SVG element that defs a linearGradient.


is a thing:

SVG allows us to define graphic elements that we need to reuse later. It is recommended that all reference elements that need to be used again be defined in the DEFS element. Doing so increases the readability and accessibility of SVG content. Graphical elements defined in defS elements are not rendered directly. You can render these elements anywhere in your viewport using the

element. – MDN

is a good thing:

The linearGradient element is used to define a linearGradient for the fill or stroke of a graphical element. – MDN

We can also find the corresponding element in Iconfont.

You can see that there are two different gradients, one starting with white and one starting with black.

Why are there two gradients here?

Under normal circumstances:

After hiding the black gradient block:

After comparing the two, we can see that the black gradient is from the top down and the white gradient is from the left to the right, and the overlap between the two is what we see.

Let’s see how its is defined.

<defs>
    <! -- Gradient 1 -->
    <linearGradient id="gradient-black" x1="0%" y1="100%" x2="0%" y2="0%">
    	<stop offset="0%" stop-color="# 000000" stop-opacity="1"></stop>
        <stop offset="100%" stop-color="#CC9A81" stop-opacity="0"></stop>
    </linearGradient>
    <! -- Gradient 2 -->
    <linearGradient id="gradient-white" x1="0%" y1="100%" x2="100%" y2="100%">
    	<stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"></stop>
        <stop offset="100%" stop-color="#CC9A81" stop-opacity="0"></stop>
    </linearGradient>
</defs>
Copy the code

The x1, X2, y1, and y2 properties of the tag define where the gradient starts and ends

The gradient color range can consist of two or more colors. Each color is specified by a

tag. The offset property is used to define where the gradient starts and ends. – w3school

Definition of the

element:

The color gradient on a gradient, defined by the stop element. The stop element can be a child of either a element or a

element. – MDN

What is stop-color and stop-opacity above

?

Just as the name implies, it is the color and opacity where the gradient stops.

So it follows:

  • The gradient of 1
    • <linearGradient>The definition of thex1="0%" y1="100%" x2="0%" y2="0%"Represents a linear gradient from bottom left to top left
    • The first one<stop>The definition of theoffset="0%" stop-color="#FFFFFF" stop-opacity="1"The color of the starting position is#FFFFFF, the transparency is 1
    • The second<stop>The definition of theoffset="100%" stop-color="#CC9A81" stop-opacity="0"The color of the end position is#CC9A81, the transparency is 0
  • The gradient of 2
    • <linearGradient>The definition of thex1="0%" y1="100%" x2="100%" y2="100%"Represents a linear gradient from bottom left to bottom right

Take a look at how the long color block on the right works:

<linearGradient id="gradient-hsv" x1="0%" y1="100%" x2="0%" y2="0%">
    <stop offset="0%" stop-color="#FF0000" stop-opacity="1"></stop>
    <stop offset="13%" stop-color="#FF00FF" stop-opacity="1"></stop>
    <stop offset="25%" stop-color="#8000FF" stop-opacity="1"></stop>
    <stop offset="38%" stop-color="#0040FF" stop-opacity="1"></stop>
    <stop offset="50%" stop-color="#00FFFF" stop-opacity="1"></stop>
    <stop offset="63%" stop-color="#00FF40" stop-opacity="1"></stop>
    <stop offset="75%" stop-color="#0BED00" stop-opacity="1"></stop>
    <stop offset="88%" stop-color="#FFFF00" stop-opacity="1"></stop>
    <stop offset="100%" stop-color="#FF0000" stop-opacity="1"></stop>
</linearGradient>
Copy the code

In this case, we can easily understand what it means by using the knowledge we have learned above.

So now that I can draw, how do I get the color at a given position?

By reporting an error, I learned that it was set using the HSV color model.

It is worth mentioning here that know Ali big guy quickly let him fix the bug, the color block can not drag, a drag on the error.

So what is the HSV color model?

HSV(Hue, Saturation, Value) is A color space created by A. R. Smith in 1978 based on the intuitive characteristics of colors, also known as the Hexcone Model.

In this model, the color parameters are: hue (H), saturation (S), lightness (V). – Baidu Encyclopedia

  • Hue H is measured by Angle and ranges from 0° to 360°
  • Saturation S indicates the degree to which the color is close to the spectral color. The value ranges from 0% to 100%
  • Lightness V indicates the brightness of a color. The value ranges from 0% to 100%

From the range above we can calculate the color we want.

So how do you calculate the color?

Let’s get rid of the Iconfont code, which is a little tired after all the confusion.

In the first step, we need to complete the H-tone in HSV, which is the long color block on the right.

Here’s a simple implementation:

// index.html
<div class="H">
  <svg>
    <defs>
      <linearGradient id="gradient-hsv" x1="0%" y1="100%" x2="0%" y2="0%">
        <stop offset="0%" stop-color="#FF0000" stop-opacity="1"></stop>
        <stop offset="13%" stop-color="#FF00FF" stop-opacity="1"></stop>
        <stop offset="25%" stop-color="#8000FF" stop-opacity="1"></stop>
        <stop offset="38%" stop-color="#0040FF" stop-opacity="1"></stop>
        <stop offset="50%" stop-color="#00FFFF" stop-opacity="1"></stop>
        <stop offset="63%" stop-color="#00FF40" stop-opacity="1"></stop>
        <stop offset="75%" stop-color="#0BED00" stop-opacity="1"></stop>
        <stop offset="88%" stop-color="#FFFF00" stop-opacity="1"></stop>
        <stop offset="100%" stop-color="#FF0000" stop-opacity="1"></stop>
      </linearGradient>
    </defs>
    <rect x="0" y="0" width="100%" height="100%" fill="url(#gradient-hsv)"></rect>
  </svg>
  <div class="slide"></div>
</div>

<div class="color">
  <h2>rgb(102, 81, 192)</h2>
  <div class="show" style="background: rgb(102, 81, 192);">

  </div>
</div>
Copy the code
// style.css
svg{
  width: 100%;
  height: 100%;
}

.H{
  width: 20px;
  height: 200px;
  position: relative;
}

.slide{
  position: absolute;
  left: 4px;
  top: -8px;
  border: 8px solid transparent;
  border-right-color: # 888;
  width: 0;
  height: 0;
  pointer-events: none;
}

.show{
  width: 300px;
  height: 100px;
}
Copy the code
// app.js
let H = document.querySelector('.H');
let HRect = H.querySelector('rect');
let HSlide = H.querySelector('.slide')
let HFlag = false;

let Hval = 0;
let Sval = 100;
let Vval = 100;

HRect.addEventListener('mousedown'.() = > {
  HFlag = true;
})
HRect.addEventListener('mousemove'.ev= > {
  if(! HFlag)return;

  // ev.offsetY is the Y coordinate of the mouse relative to the source element
  let offsetY = ev.offsetY / H.offsetHeight;

  HSlide.style.top = ev.offsetY - 8 + 'px'

  // Since H ranges from 0 to 360, multiply by the scale to get a color value
  Hval = 360 * offsetY;

  setHSV();
})
HRect.addEventListener('mouseup'.() = > {
  HFlag = false;
})

let colorEl = document.querySelector('.color');
let colorTitle = colorEl.querySelector('h2');
let colorShow = colorEl.querySelector('.show');

function setHSV(){
  // Here calculate the corresponding RGB value
  let color = `
    rgb(${hsvtorgb(Hval, Sval, Vval).join(', ')})
  `

  colorTitle.innerHTML = color;
  colorShow.style.background = color;
}

setHSV();
Copy the code

There’s also a function called hsvtorgb, which I’ll post separately because I copied it from someone else.

From: www.cnblogs.com/brainworld/…

function hsvtorgb(h, s, v) {
  s = s / 100;
  v = v / 100;
  var h1 = Math.floor(h / 60) % 6;
  var f = h / 60 - h1;
  var p = v * (1 - s);
  var q = v * (1 - f * s);
  var t = v * (1 - (1 - f) * s);
  var r, g, b;
  switch (h1) {
    case 0:
      r = v;
      g = t;
      b = p;
      break;
    case 1:
      r = q;
      g = v;
      b = p;
      break;
    case 2:
      r = p;
      g = v;
      b = t;
      break;
    case 3:
      r = p;
      g = q;
      b = v;
      break;
    case 4:
      r = t;
      g = p;
      b = v;
      break;
    case 5:
      r = v;
      g = p;
      b = q;
      break;
  }
  return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
Copy the code

I actually don’t understand the algorithm. The head 🐶

The above code achieves the following:

Then we implement the color blocks that represent SV in HSV

Because I don’t want to write too long, I will show the modified part of the code:

// index.html
<div class="HSV">
  <div class="SV">
    <svg>
      <defs>
        <linearGradient id="gradient-black" x1="0%" y1="100%" x2="0%" y2="0%">
          <stop offset="0%" stop-color="# 000000" stop-opacity="1"></stop>
          <! -- endColor -->
          <stop class="endColor" offset="100%" stop-color="#CC9A81" stop-opacity="0"></stop>
        </linearGradient>
        <linearGradient id="gradient-white" x1="0%" y1="100%" x2="100%" y2="100%">
          <stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"></stop>
          <! -- endColor -->
          <stop class="endColor" offset="100%" stop-color="#CC9A81" stop-opacity="0"></stop>
        </linearGradient>
      </defs>
      <rect x="0" y="0" width="100%" height="100%" fill="url(#gradient-white)"></rect>
      <rect x="0" y="0" width="100%" height="100%" fill="url(#gradient-black)"></rect>
    </svg>
    <div class="slide"></div>
  </div>

  <div class="H">/ /...</div>
</div>
Copy the code
// style.css
.HSV{
  display: flex;
}

.SV{
  width: 200px;
  margin-right: 10px;
}
Copy the code
// app.js
let SV = document.querySelector('.SV');
// Here we select the second rectangle so that the click event is not obscured by the other rectangle
let SVRect = SV.querySelector('rect:last-child');
let SVSlide = SV.querySelector('.slide');
let SVFlag = false;

SVRect.addEventListener('mousedown'.() = > {
  SVFlag = true;
})
SVRect.addEventListener('mousemove'.ev= > {
  if(! SVFlag)return;

  // The ratio of the slider is multiplied by 100 to obtain the corresponding SV value
  Sval = ev.offsetX / SV.offsetWidth * 100;
  // Transparency is reversed because of the direction
  Vval = (1 - ev.offsetY / SV.offsetHeight) * 100;

  SVSlide.style.left = ev.offsetX + 3 + 'px';
  SVSlide.style.top = ev.offsetY + 3 + 'px';

  setHSV();
})
SVRect.addEventListener('mouseup'.() = > {
  SVFlag = false;
})

let endColors = document.querySelectorAll('.endColor');

function setHSV(){
  // Here calculate the corresponding RGB value
  // This is the final color
  let color = `
    rgb(${hsvtorgb(Hval, Sval, Vval).join(', ')})
  `;
  // This is the end color for the SV
  let StopColor = `
    rgb(${hsvtorgb(Hval, 100.100).join(', ')})
  `;

  // Set the end color for the rect in the page
  [...endColors].map(el= > el.setAttribute('stop-color', StopColor));

  colorTitle.innerHTML = color;
  colorShow.style.background = color;
}
Copy the code

From the above code, I can get the following effects:

In fact, the above code is incomplete, as you can see from the diagram below that it is white on both sides.

I have been looking for questions about SVG and Iconfont, why the result is different…

Then I noticed that the original Iconfont gave the entire color block a solid background color

Let’s add it and see the effect.

// app.js
function setHSV(){
  // ...

  SV.style.background = StopColor;
  
  // ...
}
Copy the code

At this point, the core function of the color selector is complete.

The end of the

There are two other sections, but I don’t want to write too long.

This article is over, but you can continue to improve its features, such as:

  • Complete the functions of the other two sections: color storage board and input box
  • Convert RGB to hexadecimal
  • Write it as a pop-up box
  • Componentize it with Vue

Thank you for reading.