欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

画布说明 (4):从零开始的背景和颜色选择器

最编程 2024-03-30 14:08:43
...

我们已经了解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');
}();