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
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
<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.