Vue版本的拾色器

本文主要还是进行DOM操作,以及Vue的基础用法,以及canvas的简单入门。目前实现效果如下:

效果图

准备

  • 如何实现点击某个区域获取对应区域的颜色的功能?
  • 应该传入哪些参数?提供哪些参数?
  • 支持哪几种颜色表现形式,以及使用slide来进行颜色选取的交互

canvas获取颜色

首先要绘制两个canvas,一个用于获取对应颜色,一个用于选取对应颜色:

// 颜色选取的canvas
function colorBar(canvas) {
  var ctx = canvas.getContext("2d");
  canvas.setAttribute("width", "256");
  canvas.setAttribute("height", "10");
  var gradient = ctx.createLinearGradient(0, 0, width, 0);

  gradient.addColorStop(0, "#f00");
  gradient.addColorStop(1 / 6, "#f0f");
  gradient.addColorStop(2 / 6, "#00f");
  gradient.addColorStop(3 / 6, "#0ff");
  gradient.addColorStop(4 / 6, "#0f0");
  gradient.addColorStop(5 / 6, "#ff0");
  gradient.addColorStop(1, "#f00");

  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, width, 10);
}
// 获取颜色的canvas
function colorBox(canvas, color) {
  var ctx = canvas.getContext("2d");
  canvas.setAttribute("width", "256");
  canvas.setAttribute("height", "256");
  // 第一次填充
  var my_gradient1 = ctx.createLinearGradient(0, 0, width, 0);
  my_gradient1.addColorStop(0, "rgba(255,255,255,1)");
  my_gradient1.addColorStop(1, color);
  ctx.fillStyle = my_gradient1;
  ctx.fillRect(0, 0, width, width);
  // 第二次填充
  var my_gradient2 = ctx.createLinearGradient(0, 0, 0, width);
  my_gradient2.addColorStop(0, "rgba(0,0,0,0)");
  my_gradient2.addColorStop(1, "rgba(0,0,0,1)");
  ctx.fillStyle = my_gradient2;
  ctx.fillRect(0, 0, width, width);
}
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

其中ctx.createLinearGradient是用于获取颜色填充区域,my_gradient1.addColorStop则类似于linear-gradient这个CSS属性。

重点:获取颜色

没做之前,我也很好奇如何获取颜色,查询MDN发现,canvas提供了getImageData,我们借助公式即可实现颜色的获取。为了兼容更多的浏览器,我采用了top和left实现标记的移动,而没有采用transform.

function getBoxColor(canvas, pos) {
  var ctx = canvas.getContext("2d");
  var imgData = ctx.getImageData(0, 0, 256, 256);
  var data = imgData.data;
  // ((行数-1)*imageData.width + (列数-1))*4 - 1 + 1/2/3/4
  var dataIndex = (pos.y * imgData.width + pos.x) * 4;
  return [
    data[dataIndex],
    data[dataIndex + 1],
    data[dataIndex + 2],
    (data[dataIndex + 3] / 255).toFixed(2),
  ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13

rbga与hex的互相转换

按照规则进行转换即可,不赘述。

function rgb2hex(rgb) {
  var aRgb = rgb instanceof Array ? rgb : (rgb.split(',') || [0, 0, 0]);
  var temp;
  return [
    (temp = Number(aRgb[0]).toString(16)).length == 1 ? ('0' + temp) : temp,
    (temp = Number(aRgb[1]).toString(16)).length == 1 ? ('0' + temp) : temp,
    (temp = Number(aRgb[2]).toString(16)).length == 1 ? ('0' + temp) : temp,
  ].join('');
}

function hex2rgb(hex) {
  if (hex.length == 3) {
    hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
  }
  return [
    parseInt(hex[0] + hex[1], 16),
    parseInt(hex[2] + hex[3], 16),
    parseInt(hex[4] + hex[5], 16),
  ].join('');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

slide滑动效果

实际上就是三个事件的结合,在标记上添加mousedown事件,注意要加上阻止冒泡,防止外部其他mousedown事件被响应,然后利用事件委托mouseupmouseup事件添加到window上,保证拖拽过程一定能够触发,在使用过程中为了判断是否是点击标记而产生拖拽,我们使用一个变量作为标志位进行标志。

距离判断则是使用clientX - offsetLeft可以计算出对应的挪动距离,然后改变对应的left的值。

setBarAplhaDown(e) {
  // 标志位
  this.aplhaFlag = true;
  this.setBarAplhaMove(e);
},
setBarAplhaMove(e) {
  if (!this.aplhaFlag) return;
  this.setBarAplha(e);
},
clearAplhaUp() {
  this.aplhaFlag = false;
},
setBarAplha(e) {
  let x = e.clientX;
  var lineDiv_left = this.getPosition(this.$refs.aplha.parentNode).left;
  var minDiv_left = x - lineDiv_left;
  this.$refs.aplha.style["left"] = minDiv_left + "px";
  let aplha = "";
  if (minDiv_left >= 0 && minDiv_left < 256) {
    aplha = (minDiv_left / 256).toFixed(2);
  } else {
    return;
  }
  this.$refs.current.style["opacity"] = aplha;
}
//获取元素的绝对位置
getPosition(node) {
    var left = node.offsetLeft; //获取元素相对于其父元素的left值var left
    var top = node.offsetTop;
    var current = node.offsetParent; // 取得元素的offsetParent
    // 一直循环直到根元素
    while (current != null) {
        left += current.offsetLeft;
        top += current.offsetTop;
        current = current.offsetParent;
    }
    return {
        left: left,
        top: top
    };
}
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

总结

  1. 了解canvas的部分api
  2. 熟悉slider实现的DOM原理
  3. Vue自定义指令和动画过渡
  4. DOM位置相关属性详解

问题

参考Element发现并不是利用canvas实现的,应该有别的方式来完成输入框输入响应对应颜色

优化

  • [ ] 添加其他颜色格式
  • [ ] 输入框输入响应对应颜色
  • [ ] 提供预选颜色,以及透明度设置
  • [x] 优化颜色选取时输入框变化