使用 Chrome DevTools 做开发的同学应该都知道,Chrome 开发工具中的取色板简直是网页设计时神一般的存在,不仅可以实时预览页面样式,配合插件还可以直接保存到本地,可谓 debug 一把手。今天就来试试用原生 JS 还原一个取色板吧!DevTools 取色板预览:

preview

图中不仅包含了取色、调节色相、调节透明度、颜色预览等基本功能外,还可以使用三种方式:HEX, RGBA, HSLA输出颜色代码。此外,取色板还包括了一个屏幕取色器(JS 无法实现),以及一个下方的自定义色板的功能(暂未实现)。本文将会实现的效果如图:

preview

静态页面搭建

原型实现

这次我们采用基本的浮动布局,首先搭建基本的HTML骨架,这部分较为简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="wrapper">
<div class="color-picker" name="color">
<div class="color-indicator" name="color"></div>
</div>
<div class="preview"></div>
<div class="hue palette" name="hue">
<div class="slider" name="hue"></div>
</div>
<div class="alpha palette" name="alpha">
<div class="slider" name="alpha"></div>
</div>
<h5 class="hex result">#fff</h5>
<h5 class="rgb result">rgba(255, 255, 255, 1)</h5>
<h5 class="hsl result">hsla(0, 0%, 100%, 1)</h5>
</div>
<div class="target">Change my color pls!</div>

其中,div.color-indicator为色盘上的游标,两个div.slider为色相、透明度控件上的滑块。为了布局方便,这里不考虑响应性全部采用px为单位布局,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@import url(https://fonts.googleapis.com/css?family=Quicksand);

html,
body {
margin: 0;
box-sizing: border-box;
}
body {
background-color: #333;
font-family: "Quicksand", sans-serif;
padding-top: 100px;
}

.wrapper {
background: white;
margin: 0 auto;
width: 200px;
height: 265px;
border-radius: 3px;
border: 1px solid #666;
cursor: default;
}

.color-picker {
width: 200px;
height: 120px;
margin-bottom: 15px;
border-radius: inherit;
background: darkred;
overflow: hidden;
}

.preview {
background: darkgreen;
float: left;
width: 32px;
height: 32px;
border-radius: 50%;
margin-left: 12px;
border: 1px solid #eee;
}

.palette {
background: darkblue;
width: 120px;
height: 12px;
margin-left: 60px;
position: relative;
border-radius: 1px;
margin-bottom: 10px;
}

.result {
color: #aaa;
margin: 0;
line-height: 2;
text-align: center;
cursor: text;
}

.target {
width: max-content;
margin: 0 auto;
padding-top: 10px;
color: white;
font-size: 24px;
cursor: pointer;
}

由于等会要给div.target绑定点击事件触发取色板,这里将该元素的宽度设置为max-content(← 兼容性不好,使用时一定要加fallback),可以将宽度由100%缩减为文字部分的实际宽度。到这一步之后效果如下:

样式加工

接下来便是将取色板上的 CSS 样式由易到难逐个击破的时间。

  1. 色板底部小三角

    使用伪元素完成。为了使小三角能与父元素有相同的背景颜色与边框,这里并没有采用border-radius模拟三角形,而是使用background: linear-gradient()。另外,使用绝对定位时应该为父元素设置position: relative属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .wrapper {
    position: relative;
    }
    .wrapper::after {
    content: "";
    position: absolute;
    width: 10px;
    height: 10px;
    top: 260px;
    left: 94px;
    background: linear-gradient(135deg, transparent 50%, white 50%);
    border-width: 1px 1px 0 0 inherit solid;
    transform: rotate(45deg);
    }
  2. 色相滑块

    也是使用linear-gradient,为了颜色的连贯性必须多加几个色标。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    .hue {
    background: linear-gradient(
    to left,
    hsl(0, 100%, 50%) 0%,
    hsl(30, 100%, 50%) 8.33%,
    hsl(60, 100%, 50%) 16.67%,
    hsl(90, 100%, 50%) 25%,
    hsl(120, 100%, 50%) 33.33%,
    hsl(150, 100%, 50%) 41.67%,
    hsl(180, 100%, 50%) 50%,
    hsl(210, 100%, 50%) 58.33%,
    hsl(240, 100%, 50%) 66.67%,
    hsl(270, 100%, 50%) 75%,
    hsl(300, 100%, 50%) 83.33%,
    hsl(330, 100%, 50%) 91.67%,
    hsl(0, 100%, 50%) 100%
    );
    }
  3. 取色板渐变

    取色板渐变由三个部分组成:

    • 明度渐变: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1))
    • 饱和度渐变:linear-gradient(to left, rgba(0, 0, 0, 0), rgba(255, 255, 255, 1))
    • 底色:background-color

      将这三个背景叠加在一起,便形成了我们的取色盘。当我们需要改变色盘的颜色时,只需改变background-color的色相即可。

      1
      2
      3
      4
      5
      .color-picker {
      background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)), linear-gradient(to
      left, rgba(0, 0, 0, 0), rgba(255, 255, 255, 1));
      background-color: red;
      }
  4. 透明度滑块及预览颜色

    棋盘图现在已经成为了公认的表示透明区域的方法了。它也可以通过渐变来实现,只不过需要利用斜向 45° 的(0, 25, 75, 100)四个色标的渐变将矩形切割成两个三角形加上中间的六边形。棋盘图的画法在《CSS 揭秘》一书中有较为详细的解读,这里不做赘述:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    .alpha {
    background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 255, 1)) 0 0 / cover, linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.25) 25%,
    transparent 0,
    transparent 75%,
    rgba(0, 0, 0, 0.25) 0
    ) 0 0 / 12px 12px,
    linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.25) 25%,
    transparent 0,
    transparent 75%,
    rgba(0, 0, 0, 0.25) 0
    ) 6px 6px / 12px 12px;
    }

    .preview {
    background: linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.25) 25%,
    transparent 0,
    transparent 75%,
    rgba(0, 0, 0, 0.25) 0
    ) 0 0 / 12px 12px, linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.25) 25%,
    transparent 0,
    transparent 75%,
    rgba(0, 0, 0, 0.25) 0
    ) 6px 6px / 12px 12px;
    background-color: rgba(255, 0, 0, 0.5);
    }

    透明度滑块与预览颜色不同的地方在于,透明度滑块的棋盘图上面还需要加一层当前颜色的渐变,而预览颜色区域只需要加上一个背景即可。预览:

交互设计

由于省略了HEX, RGB, HSL的切换按钮,这里的交互一共只有 3 个:色盘、色相滑块、透明度滑块。

首先我们为色盘游标增加一点基本样式:

1
2
3
4
5
6
7
8
9
10
.color-indicator {
position: relative;
left: -6px;
top: -6px;
width: 12px;
height: 12px;
transform: translate(-6px, -6px);
border-radius: 50%;
border: 1px solid white;
}

这里为了让色盘游标移动的中心位于圆心,加上了relative以及lefttop属性,接着再用translate将它移至屏幕外;接着,定义滑块的公共样式:

1
2
3
4
5
6
7
8
9
10
11
.slider {
position: relative;
left: -8px;
top: -2px;
width: 16px;
height: 16px;
border-radius: 50%;
transform: translateX(120px);
background: rgb(248, 248, 248);
filter: drop-shadow(2px 2px 3px rgba(0, 0, 0, 0.2));
}

由于初始滑块位置在右侧,这里直接使用translateX(120px)将两个滑块均移动120px靠近右侧,并且也用lefttop对槽内相对位置进行定位。这里利用border-radius将滑块变换为圆形之后,就不能使用box-shadow对其进行描边了(box-shadow产生的阴影只能为矩形)。这里使用了filter中的drop-shadow滤镜属性,目前仅IE不支持。

到这里界面部分就结束啦,交互逻辑请看下期!