关于canvas 写字板,画板的实例2.0
这是继一篇《关于canvas 写字板,画板的实例1.0》的改进版,除了1.0的画线,保存图片,清除功能外,另外添加了一些其他功能。
先来张截图:

这里没有考虑太多布局上的问题,因为我的h5基本上都是在移动端上运行的,所以这里只做了移动端的,如有需要,把几个事件改下,就能在pc端运行了。
从左到右依次是:画笔颜色选择,线条粗细选择,画笔切换,橡皮切换,撤销操作,反撤销操作,保存图片,清除所有。可根据个人需要添加其他功能。
之前写的一些小实例,基本都没贴链接,害的我跑回去给它们又贴了一遍链接,以后要是还写些什么的话,先把链接贴着吧。
这是链接:点我看效果 //建议在chrome浏览器打开,传到github上,有的浏览器出问题了,本地测试都还好没问题。
接下来就步入正题了,首先是html:
<body> <div class="write-content ab"> <div class="ab header"> <img class="ab " src="img/yes.png" alt=""/> <img class="ab hide" src="img/delete.png" alt=""/> </div> <div class="write-title ab "> <ul class="write-ul"> <li class="color li_1 bg1 re"> <img class="ab" src="img/color.png" alt=""/> <img class="ab" src="img/color.png" alt=""/> <ul class="change-color ab hide"> <li data-canvas="white" class="white color-size fl"></li> <li data-canvas="black" class="black color-size fl"></li> <li data-canvas="red" class="red color-size fl"></li> <li data-canvas="yellow" class="yellow color-size fl"></li> <li data-canvas="blue" class="blue color-size fl"></li> </ul> </li> <li class="width li_1 bg1 re"> <img class="ab" src="img/width1.png" alt=""/> <img class="ab" src="img/width.png" alt=""/> <ul class="change-width ab hide" > <li data-canvas="1" class="width_1 width-size"></li> <li data-canvas="3" class="width_2 width-size"></li> <li data-canvas="5" class="width_3 width-size"></li> <li data-canvas="7" class="width_4 width-size"></li> <li data-canvas="9" class="width_5 width-size"></li> </ul> </li> <li class="write li_1 bg1 re"> <img class="ab" src="img/write1.png" alt=""/> <img class="ab" src="img/write.png" alt=""/> </li> <li class="rubber li_1 bg1 re"> <img class="ab" src="img/rubber1.png" alt=""/> <img class="ab" src="img/rubber.png" alt=""/> </li> <li class="_left li_1 bg1 re"> <img class="ab" src="img/left1.png" alt=""/> <img class="ab" src="img/left.png" alt=""/> </li> <li class="_right li_1 bg1 re"> <img class="ab" src="img/right1.png" alt=""/> <img class="ab" src="img/right.png" alt=""/> </li> <li class="save li_1 bg1 re"> <img class="ab" src="img/save1.png" alt=""/> <img class="ab" src="img/save.png" alt=""/> </li> <li class="clear li_1 bg1 re "> <img class="ab" src="img/clear1.png" alt=""/> <img class="ab" src="img/clear.png" alt=""/> </li> </ul> </div> <div class="write-body "> <canvas id="canvas" class="ab"> </canvas> </div> <div class="saveImg ab" id="showImg"> <img id="img" src="" alt=""> </div> </div> </body>
然后是css:
*{ margin: 0; padding:0; } ul li{ list-style: none; } .re{ position: relative; } .ab{ position: absolute; } .fl{ float: left; } .fr{ float: right; } .hide{ display: none; } .show{ display: block; } body{ overflow: hidden; } #canvas{ z-index: 0; } #canvas_bak{ z-index: 1; } .write{ width:100%; height:100%; } .write-title{ height:7%; background-color: #999; top:53%; } .write-content{ /*top:49%;*/ } .header{ height:5%; width:9.6666%; left:45%; top:48.5%; z-index: 100; } .header img{ height: 100%; width: 100%; } .write-ul .li_1{ background-size: 70% 70%; float: left; height:7%; width:12.5%; } .bg1{ background:url("../img/bg1.png") no-repeat center; } .bg{ background:url("../img/bg.png") no-repeat center; } /*.write-ul .li_1:hover{ background:url("../img/bg.png") no-repeat center; background-size: 70% 70%; }*/ .write-ul .li_1 img{ height:50%; width:50%; left:25%; top:25%; } .change-color{ border:1px solid #333; left:0; top:100%; background: #eee; z-index: 1000; } .color-size{ height:10px; width:10px; margin: 2px; border:1px solid #fff; } /*.color-size:hover{ border:1px solid #333; }*/ .white{ background: white; } .black{ background: black; } .red{ background: red; } .yellow{ background:yellow; } .blue{ background:blue; } .change-width{ border:1px solid #333; width:100% left:0; top:100%; background: #eee; z-index: 1000; } .width-size{ width:40px; margin:5px; ; background: #000; border:1px solid #000; } /*.width-size:hover{ border:1px solid #fff; }*/ .width_1{ height:1px; } .width_2{ height:3px; } .width_3{ height:5px; } .width_4{ height:7px; } .width_5{ height:9px; } .border3{ border:1px solid #333; } .borderf{ border:1px solid #fff; } canvas{ top:60%; } .saveImg{ display: none; left:0; top:0; z-index: 1111111; } @-webkit-keyframes down{ 0%{ -webkit-transform: translate3d(0, 148%, 0); } 100%{ -webkit-transform: translate3d(0, 0, 0); } } .down{ -webkit-animation: down 3s ease ; } @-webkit-keyframes up{ 0%{ -webkit-transform: translate3d(0, 148%, 0); } 100%{ -webkit-transform: translate3d(0, 0, 0); } } .up{ -webkit-animation: up 3s ease ; }
最后是js:

这个js 也就200多行,不算多。
这几挑几个重点的模块:
首先是几个变量:
//用于撤销的数组 var cancelList = new Array(); //撤销的次数 var cancelIndex = 0; //颜色,宽度框影藏显示标识 var temp1 = 1; var temp2 = 1;
cancelList是用来存放每次画笔动作结束后的图像,用于撤销,
cancelIndex用来记录撤销的次数,撤销:cancelIndex++;反撤销cancelIndex--,保证每次动作能得到对应的图像。
temp1,temp2这两个是记录选择框的显示隐藏的,注释也有。
这是核心代码:
write_touch:function(){
var _temp=0;
el.on("touchstart",function(e){
$(".change-width").hide();
$(".change-color").hide();
temp1 = 1;
temp2 = 1;
$("#showImg").removeClass("up");
_temp = 1;
var touch = e.touches[0];
ctx.beginPath();
ctx.moveTo(touch.pageX - canvas.offsetLeft, touch.pageY - canvas.offsetTop);
});
el.on("touchmove",function(e){
console.log(123);
// 重新获取位置
var touch = e.touches[0];
var x=touch.pageX - canvas.offsetLeft;
var y=touch.pageY - canvas.offsetTop;
//橡皮功能;
if($(".rubber").hasClass('bg')){
_temp=0;
ctx.clearRect(x-5, y-5 ,10,10);
}
if (_temp==1) {
//获取颜色和大小
var _color=$(".change-color li.border3").attr("data-canvas");
var _size=$(".change-width li.borderf").attr("data-canvas");
ctx.strokeStyle=_color;
ctx.lineWidth=_size;
//画
ctx.lineTo(x, y);
ctx.stroke();
};
});
el.on("touchend",function(){
page.saveImageToAry();
});
},
这里将划线与橡皮擦除功能写在一起了,刚开始我写的时候,擦除是让线条颜色变白色,就用来模拟擦除功能,后来发现这么做是不对的,这是改进后的,这里有一点没写,就是橡皮的大小是固定的10*10;
每次 touchend事件后,都要保存现在的图像到数组里,以便用于撤销,
撤销动作:
cancel : function(){ cancelIndex++; ctx.clearRect(0,0,pageW-2,pageH/2.5); var image = new Image(); var index = cancelList.length-1 - cancelIndex ; var url = cancelList[index]; image.src = url; image.onload = function(){ ctx.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , pageW-2 , pageH/2.5); } }
看这代码就明白了,是将之前存储在数组的图像,重新渲染到画布上,
同上反撤销也是一样的道理,这里我把它两个分开写了,其实两个用的方法是一样的,应该合起来才好点。
撤销的逻辑,需根据自身需求来写。
还有,获取颜色,线条粗细比较简单,就不多写了,以及保存图片,清除画布跟上一篇一样,也就不写了。
这里还要说的就是,我在写这篇的时候,发现之前写的代码有些不合理的地方,顺便给改进了,之前用的是两个画布,一个用来画线条,一个用来存储图像,当时做得到时候是因为在一个画布里面同时画线条存储图像,妹画依次,图像都会往下有个1px的位移,当时也没搞懂什么情况,现在解决了,不需要两个画布,只要一个就可以了。算是给自己长知识了。上面给的链接,还是以前两个画布的写法就不更新了。
补充:完整js:
/** * Created by chuyunshi on 17-05-31. */ $(document).ready(function(){ var pageH,pageW; var canvas = document.getElementById("canvas"); var showImg=document.getElementById("showImg"); var ctx = canvas.getContext("2d"); var el = $("#canvas"); //用于撤销的数组 var cancelList = new Array(); //撤销的次数 var cancelIndex = 0; //颜色,宽度框影藏显示标识 var temp1 = 1; var temp2 = 1; page={ init:function(){ page.resize(); page.show_hide(); page.change_bg(); page.color_change(); page.width_change(); page.click_border(); page.write_touch(); page.clear(); page.save(); //阻止页面滑动,主要是微信打开的页面往下滑动,整个页面跟着滑动 $('body').on("touchmove",function (e) { e.preventDefault(); }); //撤销事件 $('._left').on("click",function(){ console.log(cancelList.length) console.log(cancelIndex) if(cancelIndex < cancelList.length){ page.cancel(); } }) //反撤销事件 $('._right').on("click",function(){ if(cancelIndex>0){ page.next(); } }) }, resize:function(){ console.log(123); pageH = $(window).height(); pageW = $(window).width(); canvas.height=pageH/2.5; canvas.width=pageW-2; $("body,.write-content, .write-ul").height(pageH).width(pageW); $("#showImg").css({ "height":pageH/2.5, "width":pageW-2 }) }, //整个画布的影藏显示,该功能已被注销 show_hide:function(){ var temp=0; $(".header").click(function(){ if(temp==0){ $(this).find('img').eq(0).hide(); $(this).find('img').eq(1).show(); temp=1; // $(".write-content").css('top','0%') }else{ $(this).find('img').eq(1).hide(); $(this).find('img').eq(0).show(); $("#showImg").hide(); temp=0; // $(".write-content").css('top','49%') } }) }, change_bg:function(){ $('.write-ul .li_1').click(function(){ $(this).addClass("bg").removeClass("bg1"); $(this).siblings().addClass("bg1").removeClass("bg"); $(this).find('img').eq(0).show(); $(this).find('img').eq(1).hide(); for(var i=0;i<12;i++){ $(this).siblings().find('img').eq(2*i+1).show(); } }) }, //颜色选择框隐藏显示 color_change:function(){ $(".color").click(function(){ if(temp1==1){ $(".change-color").show(); temp1=0; }else{ $(".change-color").hide(); temp1=1; } }) }, //宽度选择框显示影藏 width_change:function(){ $(".width").click(function(){ if(temp2 == 1){ $(".change-width").show(); temp2 = 0; }else{ $(".change-width").hide(); temp2 = 1; } }) }, //给被选择的颜色,宽度加标识框 click_border:function(){ $(".change-color li").click(function(){ $(this).addClass('border3').siblings().removeClass('border3') }) $(".change-width li").click(function(){ $(this).addClass('borderf').siblings().removeClass('borderf') }) }, //书写及橡皮功能 write_touch:function(){ var _temp=0; el.on("touchstart",function(e){ $(".change-width").hide(); $(".change-color").hide(); temp1 = 1; temp2 = 1; $("#showImg").removeClass("up"); _temp = 1; var touch = e.touches[0]; ctx.beginPath(); ctx.moveTo(touch.pageX - canvas.offsetLeft, touch.pageY - canvas.offsetTop); }); el.on("touchmove",function(e){ console.log(123); // 重新获取鼠标位置 var touch = e.touches[0]; var x=touch.pageX - canvas.offsetLeft; var y=touch.pageY - canvas.offsetTop; //橡皮功能; if($(".rubber").hasClass('bg')){ _temp=0; ctx.clearRect(x-5, y-5 ,10,10); } if (_temp==1) { //获取颜色和大小 var _color=$(".change-color li.border3").attr("data-canvas"); var _size=$(".change-width li.borderf").attr("data-canvas"); ctx.strokeStyle=_color; ctx.lineWidth=_size; //画 ctx.lineTo(x, y); ctx.stroke(); }; }); el.on("touchend",function(){ page.saveImageToAry(); }); }, //撤销上一个操作 cancel : function(){ cancelIndex++; ctx.clearRect(0,0,pageW-2,pageH/2.5); var image = new Image(); var index = cancelList.length-1 - cancelIndex ; var url = cancelList[index]; image.src = url; image.onload = function(){ ctx.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , pageW-2 , pageH/2.5); } }, //反撤销上一个操作 next : function(){ cancelIndex--; console.log( cancelIndex); ctx.clearRect(0,0,pageW-2,pageH/2.5); var image = new Image(); var index = cancelList.length-1 - cancelIndex ; var url = cancelList[index]; image.src = url; image.onload = function(){ ctx.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , pageW-2 , pageH/2.5); } }, //保存历史 用于撤销 saveImageToAry:function (){ cancelIndex = 0; var dataUrl = canvas.toDataURL(); cancelList.push(dataUrl); }, //清除画布 clear:function(){ $(".clear").click(function(){ ctx.clearRect(0, 0, pageW-2, pageH/2.5); showImg.style.display="none"; $("#showImg").removeClass("up"); }) }, //画布保存成图片 save:function(){ $(".save").click(function() { var img = document.getElementById("img"); img.src = canvas.toDataURL(); ctx.clearRect(0, 0, pageW-2, pageH/2.5); showImg.style.display = "block"; $("#showImg").addClass("up"); }); }, } page.init(); })
浙公网安备 33010602011771号