阿花涂鸦日程规划

331 阅读3分钟

功能需求

  • 功能一:切换画笔颜色
  • 功能二:调整笔刷粗细
  • 功能三:清空画布
  • 功能四:橡皮擦擦除
  • 功能五:撤销操作
  • 功能六:保存成图片
  • 功能七:兼容移动端(支持触摸)

项目流程

准备画板

<canvas id="drawing-board"></canvas>
//初始化JS
let canvas = document.getElementById("drawing-board");
let ctx = canvas.getContext("2d");
//将画板设置为全屏
let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;
canvas.width = pageWidth;
canvas.height = pageHeight;

画笔的实现

实现思路:监听鼠标事件,用drawCircle()方法把记录的数据画出来。

1:设置初始化当前画布功能为画笔状态,painting = false
2:当鼠标按下时(mousedown),把painting设为true,表示正在画,鼠标没松开。把鼠标点记录下来。
3:当按下鼠标的时候,鼠标移动(mousemove)就把点记录下来并画出来。
4:如果鼠标移动过快,浏览器跟不上绘画速度,点与点之间会产品间隙,所以我们需要将画出的点用线连起来 lineTo()。
5:鼠标松开的时候(mouseup),把painting设为false

let painting = false;
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,clear);
        lastPoint = newPoint;
    }
};
 
//鼠标松开事件
canvas.onmouseup = function () {
    painting = false;
}
 
// 画点函数
function drawCircle(x, y, radius) {
    ctx.save();
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2);
    ctx.fill();
}
 
// 划线函数
function drawLine(x1, y1, x2, y2) {
    ctx.lineWidth = 3;
    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
    ctx.closePath();
}

橡皮擦功能

实现思路:
1:获取橡皮擦元素
2:设置橡皮擦初始状态,clear = false。
3:监听橡皮擦click事件,点击橡皮擦,改变橡皮擦状态,clear = true。
4:clear为true时,移动鼠标使用canvas剪裁(clip())擦除画布。

let eraser = document.getElementById("eraser");
let clear = false;
 
eraser.onclick = function () {
    clear = true;
};
 
...
if (clear) {
    ctx.save();
    ctx.globalCompositeOperation = "destination-out";
    ctx.stroke();
    ctx.closePath();
    ctx.clip();
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.restore();

清空画布

实现思路:
1:获取元素节点
2:点击清空按钮,清空canvas画布

let reSetCanvas = document.getElementById("clear");
 
reSetCanvas.onclick = function () {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
};

调整画笔粗细

实现思路:
1:创建input[type=range]; 2:滑动range获取value值,赋值给ctx.linewidth


let range = document.getElementById("range");
 
range.onchange = function(){
    lWidth = this.value;
};

保存为图片

实现思路:
1:获取canvas.toDateURL
2:在页面里创建并插入一个a标签
3:a标签href等于canvas.toDateURL,并添加download属性
4:点击保存按钮,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 = "zspic" + (new Date).getTime();
    saveA.target = "_blank";
    saveA.click();
};

撤销操作

实现思路:
1:准备一个数组记录历史操作
2:每次使用画笔前将当前绘图表面储存进数组
3:点击撤销时,恢复到上一步的绘图表面

canvas.ontouchstart = function (e) {
     // 在这里储存绘图表面
    this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height);
    saveData(this.firstDot);
    ...
}
 
let undo = document.getElementById("undo");
let historyDeta = [];
 
function saveData (data) {
    (historyDeta.length === 10) && (historyDeta.shift()); // 上限为储存10步,太多了怕挂掉
    historyDeta.push(data);
}
undo.onclick = function(){
    if(historyDeta.length < 1) return false;
    ctx.putImageData(historyDeta[historyDeta.length - 1], 0, 0);
    historyDeta.pop()
};

为了避免内存对性能的影响,设置存储上线

兼容移动端

实现思路: 1:判断设备是否支持触摸 2:若支持触摸,设置为true,使用touch时间;不支持则为false,使用mouse时间

if (document.body.ontouchstart !== undefined) {
    // 使用touch事件
    anvas.ontouchstart = function (e) {
        // 开始触摸
    }
    canvas.ontouchmove = function (e) {
        // 开始滑动
    }
    canvas.ontouchend = function () {
        // 滑动结束
    }
}else{
    // 使用mouse事件
    ...
}

在touch事件里,通过.touches[0].clientX和.touches[0].clientY来获取坐标的,要和mouse事件区别开。

总结

涉及到的知识点有:监听mouse、touch事件,以及canvas的moveTo()、lineTo()、stroke()、clip()、clearRect()等方法。

昨晚之后自己对比Win10自带的画板,发现其实很多的功能还可以实现。例如笔刷的水墨风格,喷雾风格都是可以实现。在未来也会继续进行优化的