手门把教你完成1个canvas智美术绘画板的方式

日期:2021-01-19 类型:科技新闻 

关键词:如何制作微信小程序,微信小程序源码,小程序码生成,凡科网微信小程序,微信公众号小程序

本文关键详细介绍:

  • 新项目详细介绍
  • 新项目实际效果展现
  • 1步步完成新项目实际效果
  • 踩坑

 1、新项目详细介绍

名字:智美术绘画板

技术性栈:HTML5,CSS3,JavaScript,挪动端

作用叙述:

  • 适用PC端和挪动端线上美术绘画作用
  • 完成随意挑选画笔色调、调剂画笔粗细和橡皮檫擦除等美术绘画作用
  • 完成线上画板的当地储存作用
  • 适用撤消和回到实际操作
  • 自定情况色调

 2、新项目实际效果展现

新项目详细地址 预览详细地址

预览图

PC端预览图:

挪动端预览图:

看完上面的预览图和体验过 智美术绘画板 感觉还能够的,记得点个赞哦,无论你是不是10分兴奋,总之我是挺兴奋的,终究自身完成出現的新项目实际效果,挺引以为豪的,说了1堆空话,下面便可以动起手来敲编码,完成自身要想的实际效果!!!

注:下面完成新项目实际效果关键是有关JavaScript层面的,下面仅仅是出示 完成思路的编码并不是所有编码

3、1步步完成新项目实际效果

(1)剖析网页页面

根据 测试用例图 ,大家了解客户进到大家这个网站有哪些作用?

客户能够开展的实际操作:

  • 画画
  • 更改画笔的粗细
  • 切换画笔的色调
  • 应用橡皮檫擦除不要想的一部分
  • 清空画板
  • 将自身画的物品储存成照片
  • 开展撤消和重做实际操作
  • 切换画板情况色调
  • 适配挪动端(适用触碰)

(2)开展HTML合理布局

我撰写html的另外,引进了css文档和js文档

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF⑻">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>智美术绘画板</title>
    <link rel="shortcut icon" href="./image/favicon.png" type="image/x-icon">
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
    <canvas id="canvas"></canvas>
    <div class="bg-btn"></div>
    <div class="color-group" id="bgGroup">
        <h3>挑选情况色调:</h3>
        <ul class="clearfix">
            <li class="bgcolor-item" style="background-color: blue;"></li>
            <li class="bgcolor-item" style="background-color: black;"></li>
            <li class="bgcolor-item" style="background-color: #FF3333;"></li>
            <li class="bgcolor-item" style="background-color: #0066FF;"></li>
            <li class="bgcolor-item" style="background-color: #FFFF33;"></li>
            <li class="bgcolor-item" style="background-color: #33CC66;"></li>
            <li class="bgcolor-item" style="background-color: gray;"></li>
            <li class="bgcolor-item" style="background-color: #F34334;"></li>
            <li class="bgcolor-item" style="background-color: #fff;box-shadow: 0 1px 2px 0 rgba(32,33,36,0.28);"></li>
            <li class="bgcolor-item" style="background-color: #9B27AC;"></li>
            <li class="bgcolor-item" style="background-color: #4CB050;"></li>
            <li class="bgcolor-item" style="background-color: #029688;"></li>
        </ul>
        <i class="closeBtn"></i>
    </div>
    <div class="tools">
        <div class="container">
            <button class="save"  id="save" title="储存"></button>
            <button class="brush active" id="brush" title="画笔"></button>
            <button class="eraser" id="eraser" title="橡皮擦"></button>
            <button class="clear" id="clear" title="清屏"></button>
            <button class="undo"  id="undo" title="撤消"></button>
            <button class="redo"  id="redo" title="再做"></button>
        </div>
    </div>
    <div class="pen-detail" id="penDetail">
        <i class="closeBtn"></i>
        <p>笔尺寸</p>
        <span class="circle-box"><i id="thickness"></i></span> <input type="range" id="range1" min="1" max="10" value="1">
        <p>笔色调</p>
        <ul class="pen-color clearfix">
            <li class="color-item active" style="background-color: black;"></li>
            <li class="color-item" style="background-color: #FF3333;"></li>
            <li class="color-item" style="background-color: #99CC00;"></li>
            <li class="color-item" style="background-color: #0066FF;"></li>
            <li class="color-item" style="background-color: #FFFF33;"></li>
            <li class="color-item" style="background-color: #33CC66;"></li>
        </ul>
        <p>不全透明度</p>
        <i class="showOpacity"></i> <input type="range" id="range2" min="1" max="10" value="1">
    </div>
    <script src="./js/main.js"></script>
</body>
</html>

(3)用CSS清理页面

css编码能够依据本人习惯性开展清理页面,因此这里就不写css的编码了,大伙儿能够立即看 新项目编码 或从开发设计者专用工具中核查元素收看。假如有难题能够私聊我,我感觉难题不大。

(4)应用JS完成新项目的实际作用

1.提前准备工作中

最先,提前准备个器皿,也便是画板了,前面的html早已撰写好这个器皿,这里纯属是空话。

<canvas id="canvas"></canvas>

随后原始化js

let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');

我准备把画板做满足屏的,因此接下来设定1下 canvas 的宽高

let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;

canvas.width = pageWidth;
canvas.height = pageHeight;

因为一部分IE不适用 canvas ,假如要适配IE,大家能够建立1个 canvas ,随后应用 excanvas 原始化,对于IE再加exCanvas.js,这里大家确立不考虑到IE。

可是我在电脑上上对访问器的对话框开展更改,画板不容易自融入的放缩。处理方法:

// 记得要实行autoSetSize这个涵数哦
function autoSetSize(){
    canvasSetSize();
    // 当实行这个涵数的情况下,会先设定canvas的宽高
    function canvasSetSize(){
        let pageWidth = document.documentElement.clientWidth;
        let pageHeight = document.documentElement.clientHeight;
        
        canvas.width = pageWidth;
        canvas.height = pageHeight;
    }
    // 在对话框尺寸更改以后,就会开启resize恶性事件,再次设定canvas的宽高
    window.onresize = function(){
        canvasSetSize();
    }
}

2.完成画画的作用

完成思路:监视电脑鼠标恶性事件, 用 drawLine() 方式把纪录的数据信息画出来。

  • 原始化当今画板的画笔情况, painting = false
  • 当电脑鼠标按下时( mousedown ),把 painting 设为 true ,表明正在画,电脑鼠标没松开。把电脑鼠标点纪录下来。
  • 当按下电脑鼠标的情况下,电脑鼠标挪动( mousemove )就 把点纪录 下来并画出来。 假如电脑鼠标挪动过快,访问器跟不上美术绘画速率,点与点之间会出現空隙,因此大家必须将画出的点用线连起来( lineTo() )。
  • 电脑鼠标松开的情况下( mouseup ),把 painting 设为 false

注: drawCircle 这个方式实际上能够无需撰写,这个只是以便让大伙儿可以了解刚开始点一下的部位在哪儿里?

function listenToUser() {
    // 界定1个自变量原始化画笔情况
    let painting = false;
    // 纪录画笔最终1次的部位
    let lastPoint = {x: undefined, y: undefined};

    // 电脑鼠标按下恶性事件
    canvas.onmousedown = function(e){
        painting = true;
        let x = e.clientX;
        let y = e.clientY;
        lastPoint = {'x':x,'y':y};
        drawCircle(x,y,5);
    }

    // 电脑鼠标挪动恶性事件
    canvas.onmousemove = function(e){
        if(painting){
            let x = e.clientX;
            let y = e.clientY;
            let newPoint = {'x':x,'y':y};
            drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y);
            lastPoint = newPoint;
        }
    }

    // 电脑鼠标松开恶性事件
    canvas.onmouseup = function(){
        painting = false;
    }
}

// 画点涵数
function drawCircle(x,y,radius){
    // 新建1条相对路径,转化成以后,图型绘图指令被指向到相对路径上转化成相对路径。
    context.beginPath();
    // 画1个以(x,y)为圆心的以radius为半径的圆弧(圆),
    // 从startAngle刚开始到endAngle完毕,依照anticlockwise给定的方位(默认设置为顺时针)来转化成。
    context.arc(x,y,radius,0,Math.PI*2);
    // 根据填充相对路径的內容地区转化成实心的图型
    context.fill();
    // 闭合相对路径以后图型绘图指令又再次指向到左右文中。
    context.closePath();
}

function drawLine(x1,y1,x2,y2){
    // 设定线条宽度
    context.lineWidth = 10;
    // 设定线条尾端款式。
    context.lineCap = "round";
    // 设置线条与线条间接性合处的款式
    context.lineJoin = "round";
    // moveTo(x,y)将笔触挪动到特定的座标x和y上
    context.moveTo(x1,y1);
    // lineTo(x, y) 绘图1条从当今部位到特定x和y部位的平行线
    context.lineTo(x2,y2);
    // 根据线条来绘图图型轮廊
    context.stroke();
    context.closePath();
}

3.完成橡皮擦作用

完成思路:

  1. 获得橡皮擦元素
  2. 设定橡皮擦原始情况, eraserEnabled = false
  3. 监视橡皮擦 click 恶性事件,点一下橡皮擦,更改橡皮擦情况, eraserEnabled = true ,而且切换class,完成 被激活 的实际效果。
  4. eraserEnabledtrue 时,挪动电脑鼠标用 context.clearRect() 完成了 橡皮檫。

可是我发现canvas的API中,能够消除像素的便是clearRect方式,可是clearRect方式的消除地区矩形框,终究绝大多数人的习惯性中的橡皮擦全是圆形的,因此就引进了剪辑地区这个强劲的作用,也便是clip方式。下面的编码是应用 context.clearRect() 完成了 橡皮檫。请看踩坑一部分,掌握怎样更好的完成橡皮檫。

let eraser = document.getElementById("eraser");
let eraserEnabled = false;

// 记得要实行listenToUser这个涵数哦
function listenToUser() {
   	// ... 意味着省略了以前写的编码
    // ...

    // 电脑鼠标按下恶性事件
    canvas.onmousedown = function(e){
        // ...
        if(eraserEnabled){//要应用eraser
            context.clearRect(x⑸,y⑸,10,10)
        }else{
            lastPoint = {'x':x,'y':y}
        }
    }

    // 电脑鼠标挪动恶性事件
    canvas.onmousemove = function(e){
        let x = e.clientX;
        let y = e.clientY;
        if(!painting){return}
        if(eraserEnabled){
            context.clearRect(x⑸,y⑸,10,10);
        }else{
            var newPoint = {'x':x,'y':y};
            drawLine(lastPoint.x, lastPoint.y,newPoint.x, newPoint.y);
            lastPoint = newPoint;
        }  
    }

    // ...
}


// 点一下橡皮檫
eraser.onclick = function(){
    eraserEnabled = true;
    eraser.classList.add('active');
    brush.classList.remove('active');
}

4.完成清屏作用

完成思路:

获得元素连接点。

点一下清空按钮清空canvas画布。

let reSetCanvas = document.getElementById("clear");

// 完成清屏
reSetCanvas.onclick = function(){
    ctx.clearRect(0,0,canvas.width,canvas.height);
    setCanvasBg('white');
}

// 再次设定canvas情况色调
function setCanvasBg(color) {
    ctx.fillStyle = color;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
}

5.完成储存成照片作用

完成思路:

  • 获得canvas.toDateURL
  • 在网页页面里建立并插进1个a标识
  • a标识href等于canvas.toDateURL,并加上download特性
  • 点一下储存按钮,a标识开启click恶性事件
let save = document.getElementById("save");

// 免费下载照片
save.onclick = function(){
    let imgUrl = canvas.toDataURL('image/png');
    let saveA = document.createElement('a');
    document.body.appendChild(saveA);
    saveA.href = imgUrl;
    saveA.download = 'mypic'+(new Date).getTime();
    saveA.target = '_blank';
    saveA.click();
}

6.完成更改情况色调的作用

完成思路:

  1. 获得相应的元素连接点。
  2. 给每个class为bgcolor-item的标识加上点一下恶性事件,当点一下恶性事件开启时,更改情况色调。
  3. 点一下设定情况色调的div以外的地区,完成掩藏那个div。
let selectBg = document.querySelector('.bg-btn');
let bgGroup = document.querySelector('.color-group');
let bgcolorBtn = document.querySelectorAll('.bgcolor-item');
let penDetail = document.getElementById("penDetail");
let activeBgColor = '#fff';


// 完成了切换情况色调
for (let i = 0; i < bgcolorBtn.length; i++) {
    bgcolorBtn[i].onclick = function (e) {
        // 阻拦冒泡
        e.stopPropagation();
        for (let i = 0; i < bgcolorBtn.length; i++) {
            bgcolorBtn[i].classList.remove("active");
            this.classList.add("active");
            activeBgColor = this.style.backgroundColor;
            setCanvasBg(activeBgColor);
        }
    }
}

document.onclick = function(){
    bgGroup.classList.remove('active');
}

selectBg.onclick = function(e){
    bgGroup.classList.add('active');
    e.stopPropagation();
}

7.完成更改画笔粗细的作用

完成思路:

  1. 完成让设定画笔的特性的会话框出現。
  2. 获得相应的元素连接点。
  3. 当input=range的元素产生更改的情况下,获得到的值取值给lWidth。
  4. 随后设定context.lineWidth = lWidth。
let range1 = document.getElementById('range1');
let lWidth = 2;
let ifPop = false;

range1.onchange = function(){
    console.log(range1.value);
    console.log(typeof range1.value)
    thickness.style.transform = 'scale('+ (parseInt(range1.value)) +')';
    console.log(thickness.style.transform )
    lWidth = parseInt(range1.value*2);
}


// 画线涵数
function drawLine(x1,y1,x2,y2){
    // ...
    context.lineWidth = lWidth;
    // ...
}

// 点一下画笔
brush.onclick = function(){
    eraserEnabled = false;
    brush.classList.add('active');
    eraser.classList.remove('active');
    if(!ifPop){
        // 弹出框
        console.log('弹1弹')
        penDetail.classList.add('active');
    }else{
        penDetail.classList.remove('active');
    }
    ifPop = !ifPop;
}

8.完成更改画笔色调的作用

完成思路跟 更改画板情况色调 的思路相近。

let aColorBtn = document.getElementsByClassName("color-item");

getColor();

function getColor(){
    for (let i = 0; i < aColorBtn.length; i++) {
        aColorBtn[i].onclick = function () {
            for (let i = 0; i < aColorBtn.length; i++) {
                aColorBtn[i].classList.remove("active");
                this.classList.add("active");
                activeColor = this.style.backgroundColor;
                ctx.fillStyle = activeColor;
                ctx.strokeStyle = activeColor;
            }
        }
    }
}

9.完成更改撤消和重做的作用

完成思路:

  1. 储存快照:每进行1次绘图实际操作则储存1份 canvas 快照到 canvasHistory 数字能量数组(转化成快照应用 canvas 的 toDataURL() 方式,转化成的是 base64 的照片);
  2. 撤消和反撤消:把 canvasHistory 数字能量数组中对应数据库索引的快照应用 canvas 的 drawImage() 方式重绘1遍;
  3. 绘图新图象:实行新的绘图实际操作时,删掉当今部位以后的数字能量数组纪录,随后加上新的快照。
let undo = document.getElementById("undo");
let redo = document.getElementById("redo");

// ...
canvas.onmouseup = function(){
        painting = false;
        canvasDraw();
}

let canvasHistory = [];
let step = ⑴;

// 绘图方式
function canvasDraw(){
    step++;
    if(step < canvasHistory.length){
        canvasHistory.length = step;  // 断开数字能量数组
    }
    // 加上新的绘图到历史时间纪录
    canvasHistory.push(canvas.toDataURL());
}

// 撤消方式
function canvasUndo(){
    if(step > 0){
        step--;
        // ctx.clearRect(0,0,canvas.width,canvas.height);
        let canvasPic = new Image();
        canvasPic.src = canvasHistory[step];
        canvasPic.onload = function () { ctx.drawImage(canvasPic, 0, 0); }
        undo.classList.add('active');
    }else{
        undo.classList.remove('active');
        alert('不可以再再次撤消了');
    }
}
// 重做方式
function canvasRedo(){
    if(step < canvasHistory.length - 1){
        step++;
        let canvasPic = new Image();
        canvasPic.src = canvasHistory[step];
        canvasPic.onload = function () { 
            // ctx.clearRect(0,0,canvas.width,canvas.height);
            ctx.drawImage(canvasPic, 0, 0);
        }
        redo.classList.add('active');
    }else {
        redo.classList.remove('active')
        alert('早已是全新的纪录了');
    }
}
undo.onclick = function(){
    canvasUndo();
}
redo.onclick = function(){
    canvasRedo();
}

10.适配挪动端

完成思路:

  • 分辨机器设备是不是适用触碰
  • true ,则应用 touch 恶性事件; false ,则应用 mouse 恶性事件
// ...
if (document.body.ontouchstart !== undefined) {
    // 应用touch恶性事件
    anvas.ontouchstart = function (e) {
        // 刚开始触碰
    }
    canvas.ontouchmove = function (e) {
        // 刚开始拖动
    }
    canvas.ontouchend = function () {
        // 拖动完毕
    }
}else{
    // 应用mouse恶性事件
    // ...
}
// ...

4、踩坑

 难题1:在电脑上上对访问器的对话框开展更改,画板不容易自融入

处理方法:

onresize回应恶性事件解决中,获得到的网页页面规格主要参数是变动后的主要参数 。

当对话框尺寸产生更改以后,再次设定canvas的宽高,简易来讲,便是对话框更改以后,给canvas.width和canvas.height再次取值。

// 记得要实行autoSetSize这个涵数哦
function autoSetSize(){
    canvasSetSize();
    // 当实行这个涵数的情况下,会先设定canvas的宽高
    function canvasSetSize(){
        let pageWidth = document.documentElement.clientWidth;
        let pageHeight = document.documentElement.clientHeight;
        
        canvas.width = pageWidth;
        canvas.height = pageHeight;
    }
    // 在对话框尺寸更改以后,就会开启resize恶性事件,再次设定canvas的宽高
    window.onresize = function(){
        canvasSetSize();
    }
}

难题2:当绘图线条宽度较为小的情况下还好,1旦较为粗就会出現难题

处理方法:看1下文本文档,得出方式,只必须简易改动1下 绘图线条的编码 就行

// 画线涵数
function drawLine(x1,y1,x2,y2){
    context.beginPath();
    context.lineWidth = lWidth;
    //-----添加-----
    // 设定线条尾端款式。
    context.lineCap = "round";
    // 设置线条与线条间接性合处的款式
    context.lineJoin = "round";
    //-----添加-----
    context.moveTo(x1,y1);
    context.lineTo(x2,y2);
    context.stroke();
    context.closePath();
}

难题3:怎样完成圆形的橡皮檫?

处理方法:

canvas的API中,能够消除像素的便是clearRect方式,可是clearRect方式的消除地区矩形框,终究绝大多数人的习惯性中的橡皮擦全是圆形的,因此就引进了剪辑地区这个强劲的作用,也便是clip方式。用法很简易:

ctx.save()
ctx.beginPath()
ctx.arc(x2,y2,a,0,2*Math.PI);
ctx.clip()
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.restore();

上面那段编码就完成了圆形地区的擦除,也便是先完成1个圆形相对路径,随后把这个相对路径做为剪辑地区,再消除像素就可以了。有个留意点便是必须先储存制图自然环境,消除完像素后要重设制图自然环境,假如不重设的话之后的制图全是会被限定在那个剪辑地区中。

难题4:怎样适配挪动端?

1.加上meta标识

由于访问器原始会将网页页面如今手机上端显示信息时开展放缩,因而大家能够在meta标识中设定meta viewport特性,告知访问器不将网页页面开展放缩,网页页面宽度=客户机器设备显示屏宽度

<meta name="viewport" content="width=device-width,
initial-scale=1,user-scalable=no,
maximum-scale=1.0,minimum-scale=1.0"/>

/*
网页页面宽度=挪动宽度 :width=device-width
客户不能以放缩:user-scalable=no
放缩占比:initial-scale=1
最大放缩占比:maximum-scale=1.0
最少放缩占比:minimum-scale=1.0
*/

2.在挪动端基本上应用的全是touch恶性事件,与PC端不一样

因为挪动端是触碰恶性事件,因此要用到H5的特性touchstart/touchmove/touchend,可是PC端只适用电脑鼠标恶性事件,因此要开展特点检验。

touch 恶性事件里,是根据 .touches[0].clientX.touches[0].clientY 来获得座标的,这点要和 mouse 恶性事件差别开。

难题5:出現1个难题便是清空以后,再次画,随后出現原先的画的物品

这个嘛,难题不大,只但是是我漏写context.beginPath(); ,也花了1点時间在上面处理bug,让我想起“编码干万行,注解第1行;程序编写不标准,朋友两行泪 ”,還是依照文本文档实际操作标准实际操作好,真香!!!

以上便是本文的所有內容,期待对大伙儿的学习培训有一定的协助,也期待大伙儿多多适用脚本制作之家。