画布说明 (4):从零开始的背景和颜色选择器
我们已经了解canvas的基本特性,也了解了canvas事件的处理方法,现在我们只需要简要的了解canvas如何填充颜色/图案,就可以轻易的实现本章的实战示例“颜色选择器”,话不多说,先看第一种,添加纯色; 首先我们要知道,canvas提供了三个方法,分别用于矩形的:
清除:clearRect(double x,double y,double width,double height);
描边: storkeRect(double x,double y,double width,double height);
填充:fillRect(double x,double y,double width,double height);
以上3个方法,标记矩形的顶点(x,y)以及矩形的宽高(width,height);
由上可知如果覆盖整个canvas只需要设置顶点为(0,0),宽高为绘图表面的宽高(canvas.width,canvas.height)即可,所以填充纯色只需要如下几行代码;
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
context.fillStyle = '#f00';
context.fillRect(0, 0, canvas.width, canvas.height)
这样我就得到了一个纯红的canvas;
第二种,添加渐变色:
我们需要知道第二个知识点,创建渐变:
轴向渐变:createLinearGradient(double x0,double y0,double x1,double, y1);
其中x0,y0是渐变的起点,x1,y1是渐变的终点;
径向渐变:createRadialGradient(double x0,double y0,double(+) r0,double x1,double y1,double(+) r1); 其中x0,y0是起点圆的圆心位置,r0是起始圆半径,必须为正数,x1,y1是终点圆的圆心位置,r1是终点圆的半径,必须为正数;
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
let gradientColor = context.createLinearGradient(0, 0, canvas.width, 0);
gradientColor.addColorStop(1, '#f00');
gradientColor.addColorStop(0, 'rgba(255,255,255,1)');
context.fillStyle = gradientColor;
context.fillRect=(0, 0, canvas.width, canvas.height)
这样我们就得到了一个径向渐变:
第三个,是填充图案 我们可以通过createPattern()方法来创建图案,接受两个参数,第一个是图案的本身,第二个是canvas如何重复图案,取值为repeat,repeat-x,repeat-y,no-repeat;
现在了解了基本的知识: 那么我们实现一个基本的颜色选择器需要哪几部分? 第一:色带:用于满足可以选择任意的颜色的需求,我们可以简单的建立一个canvas设置几段渐变就可以覆盖到所有的颜色(由于rgb取值是0~255)所以我们设置一个条形框,用于选择颜色范围即可:
<canvas id="color-bar" width="20px" height="256px">
Canvas not supported
</canvas>
相比于上面的红白渐变,这里涉及多段的渐变,需要用到一个渐变对象的方法:
gradient.addColorStop(double stop, color) 其中 stop是渐变停止的位置,color可以支持颜色的输入值;
var colorBar = document.getElementById('color-bar');
var colorBarCtx = colorBar.getContext('2d');
function colorBarInit() {
var gradientBar = colorBarCtx.createLinearGradient(0, 0, 0, colorBar.height);
gradientBar.addColorStop(0, '#f00');
gradientBar.addColorStop(1 / 6, '#ff0');
gradientBar.addColorStop(2 / 6, '#0f0');
gradientBar.addColorStop(3 / 6, '#0ff');
gradientBar.addColorStop(4 / 6, '#00f');
gradientBar.addColorStop(5 / 6, '#f0f');
gradientBar.addColorStop(1, '#f00');
colorBarCtx.fillStyle = gradientBar;
colorBarCtx.fillRect(0, 0, colorBar.width, colorBar.height);
}
在初始化方法中执行colorBarInit就可以
彩虹效果请自行实验;
然后我们需要给色带绑定一个事件,用于获取canvas返回的颜色,并赋值给呈色区域:
function getRgba(arr) {
if (arr.length && arr.length >3) {
const alpha = arr[4] || 0;
return `rgba(${arr.slice(0,3).join(',')},${(alpha/255).toFixed(2)})`;
}
return `rgba(${arr.join(',')})`;
}
function getHex(arr) {
return `#${[...arr].map(val => val > 15 ? val.toString(16) : `0${val.toString(16)}`).join('')}`;
}
colorBar.addEventListener('click', function(e) {
const point = {
x: e.offsetX || e.layerX,
y: e.offsetY || e.layerY
};
colorArr = getColorByPoint(colorBarCtx, point);
const alpha = (colorArr[3]/255).toFixed(2);
const rgba = getRgba(colorArr.slice(0,3));
const hex = getHex(colorArr);
showColor(rgba, alpha); // 使用hex,也可以
});
接下来获取点击位置的像素颜色,需要用到context对象上的getImageData方法:
ImageData ctx.getImageData(double x, double y, double w, double h); x,y为获取图像数据的左上角坐标点,w,h分别为获取的图像数据的宽和高;会返回一个ImageData对象,这里我们需要用其data属性,是一个一维数组,包含以 RGBA 顺序的数据
function getColorByPoint(canvas, context, pos) {
const { x, y } = pos;
const imgData = context.getImageData(x-1, y-1, 1, 1);
const { data } = imgData;
return data;
}
接下来简单实现下呈色区域:
<canvas id="color-pan" width="120px" height="50px">
Canvas not supported
</canvas>
const colorPan = document.getElementById('color-pan');
const colorPanCtx = colorPan.getContext('2d');
function showColor(color, alpha = 1) {
colorPanCtx.clearRect(0, 0, colorPan.width, colorPan.height);
colorPanCtx.fillStyle = color;
colorPanCtx.alpha = alpha;
colorPanCtx.fillRect(0, 0, colorPan.width, colorPan.height);
colorPanCtx.fill();
}
这样我们就能得到一个这样的效果,点击色条,即可将选择出的颜色访入右侧的呈色区域
但是我们还有诉求选择透明度,这个时候我们还需要一个新的canvas来承载透明度选择,我们可以建立一个这样的canvas有两层,一层是颜色到白色的渐变,同时垂直方向是黑色到透明的渐变,这样,这个色系中各个层次的颜色都可以提供给我们选择了,我们再创建一个透明度选择区:
<canvas id="color-picker" height="256px" width="256px" style="height: 256px;width: 256px;">
Canvas not supported
</canvas>
const colorPicker = document.getElementById('color-picker');
const colorPickerCtx = colorPicker.getContext('2d');
function colorPickerInit (color = "#f00") {
colorPickerCtx.clearRect(0, 0, colorPan.width, colorPan.height);
// 填充底色
const gradientColor = colorPickerCtx.createLinearGradient(0, 0, colorPicker.width, 0);
gradientColor.addColorStop(1, color);
gradientColor.addColorStop(0, 'rgba(255,255,255,1)');
colorPickerCtx.fillStyle = gradientColor;
colorPickerCtx.fillRect(0, 0, colorPicker.width, colorPicker.height);
colorPickerCtx.fill();
// 填充透明
const gradientTransparent = colorPickerCtx.createLinearGradient(0, 0, 0, colorPicker.height);
gradientTransparent.addColorStop(0, 'rgba(0,0,0,0)');
gradientTransparent.addColorStop(1, 'rgba(0,0,0,1)');
colorPickerCtx.fillStyle = gradientTransparent;
colorPickerCtx.fillRect(0, 0, colorPicker.width, colorPicker.height);
colorPickerCtx.fill();
}
然后修改色带的代码在点击事件上调用colorPickerInit(rgba) 我们就可以得到如下效果:
同理我们可以给渐变区域绑定事件:
colorPicker.addEventListener('click', function(e) {
const point = {
x: e.offsetX || e.layerX,
y: e.offsetY || e.layerY
};
colorArr = getColorByPoint(colorPickerCtx, point);
const alpha = (colorArr[3]/255).toFixed(2);
const rgba = getRgba(colorArr.slice(0,3));
const hex = getHex(colorArr);
showColor(rgba, alpha);
});
然后点击左侧的透明区域,就可调整呈色区域的颜色+透明度:
到了这里,我们再下方显示出选出颜色的rgba和hex值,就基本完成一个颜色设计器的基本功能;
虽然最后实现结果还比较简陋,自认为还是讲清楚如何实现一个颜色选择器提供了明确的思路;完整代码如下:
<!DOCTYPE html>
<head>
<title>color picker</title>
<style>
.content {
width: 456px;
background-color: #626262;
color: #fff;
border-radius: 10px;
}
.content .header {
height: 40px;
line-height: 40px;
padding: 0 20px;
background: linear-gradient(to bottom, #595959 0%,#626262 3%,#575757 7%,#3c3c3c 90%,#3a3a3a 97%,#2e2e2e 100%);
border-bottom: 1px solid #fff;
}
.content .picker {
display: flex;
flex-direction: row;
padding: 20px;
position: relative;
top: 0;
left: 0;
}
#color-picker {
margin-right: 5px;
}
.arrow {
position: fixed;
top: 66px;
width: 0px;
height: 0px;
border: 3px solid #fff;
}
.color-platform {
padding: 0 10px;
}
</style>
</head>
<body>
<div class="content">
<div class="header">
<div class="title">Color Picker</div>
</div>
<div class="picker">
<canvas id="color-picker" height="256px" width="256px" style="height: 256px;width: 256px;">
Canvas not supported
</canvas>
<canvas id="color-bar" width="20px" height="256px">
Canvas not supported
</canvas>
<div class="color-platform">
<canvas id="color-pan" width="120px" height="50px">
Canvas not supported
</canvas>
<div class="rgb-hsb">
<div class="rgb">rgba:<span id="rgba"></span></div>
<div class="hsb">hex:<span id="hex"></span></div>
</div>
<div class="hex"></div>
</div>
</div>
</div>
<script src='color-picker.js'></script>
</body>
</html>
const colorBar = document.getElementById('color-bar');
const colorBarCtx = colorBar.getContext('2d');
function colorBarInit() {
var gradientBar = colorBarCtx.createLinearGradient(0, 0, 0, colorBar.height);
gradientBar.addColorStop(0, '#f00');
gradientBar.addColorStop(1 / 6, '#ff0');
gradientBar.addColorStop(2 / 6, '#0f0');
gradientBar.addColorStop(3 / 6, '#0ff');
gradientBar.addColorStop(4 / 6, '#00f');
gradientBar.addColorStop(5 / 6, '#f0f');
gradientBar.addColorStop(1, '#f00');
colorBarCtx.fillStyle = gradientBar;
colorBarCtx.fillRect(0, 0, colorBar.width, colorBar.height);
}
function getColorByPoint(context, pos) {
const { x, y } = pos;
const imgData = context.getImageData(x-1, y, 1, 1);
return imgData.data;
}
function getRgba(arr) {
if (arr.length && arr.length >3) {
const alpha = arr[4] || 0;
return `rgba(${arr.slice(0,3).join(',')},${(alpha/255).toFixed(2)})`;
}
return `rgba(${arr.join(',')})`;
}
function getHex(arr) {
return `#${[...arr].map(val => val > 15 ? val.toString(16) : `0${val.toString(16)}`).join('')}`;
}
function setColor(ctx, e) {
const point = {
x: e.offsetX || e.layerX,
y: e.offsetY || e.layerY
};
colorArr = getColorByPoint(ctx, point);
const rgba = getRgba(colorArr.slice(0,3));
const hex = getHex(colorArr.slice(0,3));
setValue('rgba', rgba);
setValue('hex', hex);
showColor(rgba); // 使用hex,也可以
return rgba;
}
colorBar.addEventListener('click', function(e) {
const rgba = setColor(colorBarCtx, e);
colorPickerInit(rgba);
});
const colorPan = document.getElementById('color-pan');
const colorPanCtx = colorPan.getContext('2d');
function showColor(color, alpha = 1) {
colorPanCtx.clearRect(0, 0, colorPan.width, colorPan.height);
colorPanCtx.fillStyle = color;
colorPanCtx.alpha = alpha;
colorPanCtx.fillRect(0, 0, colorPan.width, colorPan.height);
colorPanCtx.fill();
}
function setValue(id, value) {
const dom = document.getElementById(id);
dom.innerText = value;
}
const colorPicker = document.getElementById('color-picker');
const colorPickerCtx = colorPicker.getContext('2d');
function colorPickerInit (color = "#f00") {
colorPickerCtx.clearRect(0, 0, colorPan.width, colorPan.height);
// 填充底色
const gradientColor = colorPickerCtx.createLinearGradient(0, 0, colorPicker.width, 0);
gradientColor.addColorStop(1, color);
gradientColor.addColorStop(0, 'rgba(255,255,255,1)');
colorPickerCtx.fillStyle = gradientColor;
colorPickerCtx.fillRect(0, 0, colorPicker.width, colorPicker.height);
// 填充透明
const gradientTransparent = colorPickerCtx.createLinearGradient(0, 0, 0, colorPicker.height);
gradientTransparent.addColorStop(0, 'rgba(0,0,0,0)');
gradientTransparent.addColorStop(1, 'rgba(0,0,0,1)');
colorPickerCtx.fillStyle = gradientTransparent;
colorPickerCtx.fillRect(0, 0, colorPicker.width, colorPicker.height);
}
colorPicker.addEventListener('click', function(e) {
setColor(colorPickerCtx, e);
});
const init = function() {
colorBarInit();
colorPickerInit('#fff');
showColor('#fff');
}();
上一篇: 元素加自定义主题颜色