本文主要介绍:css
名称: 智绘画板html
技术栈: HTML5,CSS3,JavaScript,移动端git
功能描述:github
PC端的预览图:canvas
移动端的预览图:数组
看完上面的预览图和体验过智绘画板以为还能够的,记得点个赞哦,无论你是否十分激动,反正我是挺激动的,毕竟本身实现出现的项目效果,挺自豪的,说了一堆废话,下面就能够动起手来敲代码,实现本身想要的效果!!!浏览器
注:下面实现项目效果主要是关于JavaScript方面的,下面仅仅是提供实现思路的代码,并不是所有代码。app
经过用例图,咱们知道用户进入咱们这个网站有哪些功能?函数
用户能够进行的操做:
我书写html的同时,引入了css文件和js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>
复制代码
css代码能够根据我的习惯进行美化界面,因此这里就不写css的代码了,你们能够直接看项目代码或者从开发者工具中审查元素观看。若是有问题能够私聊我,我以为问题不大。
首先,准备个容器,也就是画板了,前面的html已经书写好这个容器,这里纯属是废话。
<canvas id="canvas"></canvas>
复制代码
而后初始化js
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
复制代码
我打算把画板作成全屏的,因此接下来设置一下canvas的宽高
let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;
canvas.width = pageWidth;
canvas.height = pageHeight;
复制代码
因为部分IE不支持canvas,若是要兼容IE,咱们能够建立一个canvas,而后使用excanvas
初始化,针对IE加上exCanvas.js,这里咱们明确不考虑IE。
可是我在电脑上对浏览器的窗口进行改变,画板不会自适应的放缩。解决办法:
// 记得要执行autoSetSize这个函数哦
function autoSetSize(){
canvasSetSize();
// 当执行这个函数的时候,会先设置canvas的宽高
function canvasSetSize(){
// 把变化以前的画布内容copy一份,而后从新画到画布上
let imgData = context.getImageData(0,0,canvas.width,canvas.height);
let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;
canvas.width = pageWidth;
canvas.height = pageHeight;
context.putImageData(imgData,0,0);
}
// 在窗口大小改变以后,就会触发resize事件,从新设置canvas的宽高
window.onresize = function(){
canvasSetSize();
}
}
复制代码
实现思路:监听鼠标事件, 用drawLine()
方法把记录的数据画出来。
painting = false
。mousedown
),把painting
设为true
,表示正在画,鼠标没松开。把鼠标点记录下来。mousemove
)就把点记录下来并画出来。lineTo()
)。mouseup
),把painting
设为false
。注:drawCircle
这个方法其实能够不用书写,这个只是为了让你们可以理解开始点击的位置在哪里?
function listenToUser() {
// 定义一个变量初始化画笔状态
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);
lastPoint = newPoint;
}
}
// 鼠标松开事件
canvas.onmouseup = function(){
painting = false;
}
}
// 画点函数
function drawCircle(x,y,radius){
// 新建一条路径,生成以后,图形绘制命令被指向到路径上生成路径。
context.beginPath();
// 画一个以(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) 绘制一条从当前位置到指定x以及y位置的直线
context.lineTo(x2,y2);
// 经过线条来绘制图形轮廓
context.stroke();
context.closePath();
}
复制代码
实现思路:
eraserEnabled = false
。click
事件,点击橡皮擦,改变橡皮擦状态,eraserEnabled = true
,而且切换class,实现被激活的效果。eraserEnabled
为true
,移动鼠标用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-5,y-5,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-5,y-5,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');
}
复制代码
实现思路:
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);
}
复制代码
实现思路:
canvas.toDateURL
canvas.toDateURL
,并添加download属性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();
}
复制代码
实现思路:
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();
}
复制代码
实现思路:
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('弹一弹')
penDetail.classList.add('active');
}else{
penDetail.classList.remove('active');
}
ifPop = !ifPop;
}
复制代码
实现思路跟改变画板背景颜色的思路相似。
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;
}
}
}
}
复制代码
实现思路:
canvasHistory
数组(生成快照使用 canvas 的 toDataURL()
方法,生成的是 base64 的图片);canvasHistory
数组中对应索引的快照使用 canvas 的 drawImage()
方法重绘一遍;let undo = document.getElementById("undo");
let redo = document.getElementById("redo");
// ...
canvas.ontouchend = function () {
painting = false;
canvasDraw();
}
// ...
canvas.onmouseup = function(){
painting = false;
canvasDraw();
}
let canvasHistory = [];
let step = -1;
// 绘制方法
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();
}
复制代码
实现思路:
true
,则使用touch
事件;false
,则使用mouse
事件// ...
if (document.body.ontouchstart !== undefined) {
// 使用touch事件
anvas.ontouchstart = function (e) {
// 开始触摸
}
canvas.ontouchmove = function (e) {
// 开始滑动
}
canvas.ontouchend = function () {
// 滑动结束
}
}else{
// 使用mouse事件
// ...
}
// ...
复制代码
解决办法:
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();
}
}
复制代码
解决办法:看一下文档,得出方法,只须要简单修改一下绘制线条的代码就行
// 画线函数
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();
}
复制代码
解决办法:
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();
复制代码
上面那段代码就实现了圆形区域的擦除,也就是先实现一个圆形路径,而后把这个路径做为剪辑区域,再清除像素就好了。有个注意点就是须要先保存绘图环境,清除完像素后要重置绘图环境,若是不重置的话之后的绘图都是会被限制在那个剪辑区域中。
由于浏览器初始会将页面如今手机端显示时进行缩放,所以咱们能够在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
*/
复制代码
因为移动端是触摸事件,因此要用到H5的属性touchstart/touchmove/touchend,可是PC端只支持鼠标事件,因此要进行特性检测。
在touch
事件里,是经过.touches[0].clientX
和.touches[0].clientY
来获取坐标的,这点要和mouse
事件区别开。
解决办法1:http://js.jirengu.com/dafic/2/edit
解决办法2:http://js.jirengu.com/worus/2/edit
参考连接:canvas长宽变化时,画布内容消失
参考连接: HTML5 实现橡皮擦的擦除效果
嗯嗯,这个问题还没有解决,因此我就先把自定义背景颜色的功能取消掉,可是并无用,仍是存在橡皮檫会把背景层给擦掉,但愿有大神看到这篇文章,给一点建议和方法,谢谢!
这个嘛,问题不大,只不过是我漏写context.beginPath(); ,也花了一点时间在上面解决bug,让我想起“代码千万行,注释第一行;编程不规范,同事两行泪 ”,仍是按照文档操做规范操做好,真香!!!
本文做者 xyyojl
本文若有错误之处,请留言,我会及时更正
或者提bug、提需求也是能够的
以为对您有帮助的话就点个赞或收藏吧!
欢迎转载或分享,转载时请注明出处