今天看了下h5的canves,想了下,准備用它制作一個簡易畫板:
畫板主要有兩個組件,一個畫板,一個調色板,開始調色板想使用其他插件,但引入太麻煩了,就直接用canves生成了一個;
1.畫板:
電腦用鼠標事件(down,move,up來控制),平板/手機用相應其他的事件,這里需要做一個事件傳遞的優化,目前還沒搞;
先給幾個模式:素描,畫圓,畫矩形,畫線段....
切換結構如下:
eventon=mode;
if(mode changed){enevton=null;eventon=mode}

<div class="btn-group" data-toggle="buttons"> <label class="btn btn-primary glyphicon glyphicon-pencil" data-toggle="popover" title="素描"> <input type="radio" name="options" value='0'> </label> <label class="btn btn-primary glyphicon" data-toggle="popover" title="圓"> 圓<input type="radio" name="options" value='1'> </label> <label class="btn btn-primary glyphicon" data-toggle="popover" title="矩形"> 矩形<input type="radio" name="options" value='2'> </label> <label class="btn btn-primary glyphicon" data-toggle="popover" title="線段"> 線段<input type="radio" name="options" value='3'> </label> </div>

$("input[name='options']").off().change((e)=>{ let mode =$(e.target).attr('value'); let a =context.getMode(mode); startmode(box,context.getMode(mode)); }); var context={ map:function(){ let map= new Map(); map.set('0',pencil); map.set('1',drawcCircle); map.set('2',drawRectangle); map.set('3',drawcLine); return map; }(), getMode:function(mode){ return this.map.get(mode); } }
mode結構如下:
down觸發 move監聽 up結束
例如素描的mode:

function pencil(box){//素描 let cxt = box.getContext("2d"); box.onmousedown = function(e){ let x = e.offsetX; let y = e.offsetY; box.onmousemove = function(e1){ cxt.beginPath(); cxt.moveTo(x,y); x = e1.offsetX; y = e1.offsetY; cxt.lineTo(x,y); cxt.closePath(); cxt.stroke(); } } box.onmouseup = function(e){ box.onmousemove = null; context.backStore(box); } }
2.調色板:
生成一個漸變顏色條帶

function initcolor(){//調色板初始化 //顏色初始化 let can = document.getElementById("colorPicker"); let ctx=can.getContext("2d"); let colors=[ '#000000', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF', '#C0C0C0', '#FFFFFF' ]; var gradientBar = ctx.createLinearGradient(0, 0, can.width, 0); for(let i=0;i<colors.length;i++){ gradientBar.addColorStop(i/(colors.length-1), colors[i]); } ctx.fillStyle = gradientBar; ctx.beginPath(); ctx.rect(0, 0, can.width, can.height); ctx.fill(); ctx.closePath(); ctx.restore(); return can; }
監聽選擇的顏色值.賦值給畫板的strokeStyle;

colorBox.onmousedown = function(e){ let x = e.offsetX; let y = e.offsetY; let imgData=colorBox.getContext("2d").getImageData(x,0,x+1,1); box.getContext("2d").strokeStyle ='rgb('+imgData['data'][0]+','+imgData['data'][1]+','+imgData['data'][2]+')' }
3.效果圖:
4.源碼:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>畫板功能</title> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.min.js"></script> <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> <style type="text/css"> html,body{height:100%;} .container{width: 80%;height: 80%;} .header-p{width: 100%; height: 20%;} .header-body{width: 100%;height: 80%;} #colorPicker{height: 17px;width: max-content;} </style> <script> window.onload=function(){ var box = init(); var colorBox = initcolor(); colorBox.onmousedown = function(e){ let x = e.offsetX; let y = e.offsetY; let imgData=colorBox.getContext("2d").getImageData(x,0,x+1,1); box.getContext("2d").strokeStyle ='rgb('+imgData['data'][0]+','+imgData['data'][1]+','+imgData['data'][2]+')' } $("input[name='options']").off().change((e)=>{ let mode =$(e.target).attr('value'); let a =context.getMode(mode); startmode(box,context.getMode(mode)); }); $("#reset").click(()=>{ context.back_back(box); }); $("#advance").click(()=>{ context.back_advance(box); }); $("#save").click(()=>{ var a = document.createElement('a'); a.href = box.toDataURL('image/png'); //下載圖片 a.download = 'canvas.png'; a.click(); }) function init(){//初始化 //畫板 var box = document.getElementById("myCanvas"); box.width = $(".text-center.header-body").width(); box.height = $(".text-center.header-body").height(); return box; } function initcolor(){//調色板初始化 //顏色初始化 let can = document.getElementById("colorPicker"); let ctx=can.getContext("2d"); let colors=[ '#000000', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF', '#C0C0C0', '#FFFFFF' ]; var gradientBar = ctx.createLinearGradient(0, 0, can.width, 0); for(let i=0;i<colors.length;i++){ gradientBar.addColorStop(i/(colors.length-1), colors[i]); } ctx.fillStyle = gradientBar; ctx.beginPath(); ctx.rect(0, 0, can.width, can.height); ctx.fill(); ctx.closePath(); ctx.restore(); return can; } function startmode(box,f){//模式選擇 box.onmousedown=null; box.onmousemove=null; box.onmouseup=null; f(box); } var context={ rollbackSize:20,//回滾步數20步 bakStores:function(){ let stores = []; let backCanvas = document.createElement('canvas'); let backCtx = backCanvas.getContext('2d'); backCanvas.width = box.width; backCanvas.height = box.height; backCtx.drawImage(box, 0, 0, box.width, box.height); stores.push(backCanvas); return stores; }(),//提供回滾,撤銷操作 backflag:0,//回滾標記 map:function(){ let map= new Map(); map.set('0',pencil); map.set('1',drawcCircle); map.set('2',drawRectangle); map.set('3',drawcLine); return map; }(), getMode:function(mode){ return this.map.get(mode); }, backStore:function(box){//存儲 let backCanvas = document.createElement('canvas'); let backCtx = backCanvas.getContext('2d'); backCanvas.width = box.width; backCanvas.height = box.height; backCtx.drawImage(box, 0, 0, box.width, box.height); this.bakStores.push(backCanvas); if(this.bakStores.length>=this.rollbackSize){ bakStores.shift(); } this.backflag = 0; }, back_back:function(box){//后退 if(this.bakStores.length-this.backflag>1){ this.backflag++; let cxt = box.getContext("2d"); cxt.clearRect(0, 0, box.width, box.height); cxt.drawImage(this.bakStores[this.bakStores.length-this.backflag-1], 0, 0, box.width, box.height); cxt.stroke(); } }, back_advance:function(box){//前進 if(this.bakStores.length-this.backflag>0&&this.backflag>0){ this.backflag --; let cxt = box.getContext("2d"); cxt.clearRect(0, 0, box.width, box.height); cxt.drawImage(this.bakStores[this.bakStores.length-this.backflag-1], 0, 0, box.width, box.height); cxt.stroke(); } } } function pencil(box){//素描 let cxt = box.getContext("2d"); box.onmousedown = function(e){ let x = e.offsetX; let y = e.offsetY; box.onmousemove = function(e1){ cxt.beginPath(); cxt.moveTo(x,y); x = e1.offsetX; y = e1.offsetY; cxt.lineTo(x,y); cxt.closePath(); cxt.stroke(); } } box.onmouseup = function(e){ box.onmousemove = null; context.backStore(box); } } function drawcLine(box){//線段 let cxt = box.getContext("2d"); box.onmousedown = function(e){ //緩存起始點圖像,移動時不斷加載刷新 let backCanvas = document.createElement('canvas'); let backCtx = backCanvas.getContext('2d'); backCanvas.width = box.width; backCanvas.height = box.height; backCtx.drawImage(box, 0, 0, box.width, box.height); let x = e.offsetX; let y = e.offsetY; box.onmousemove = function(e1){ let x1 = e1.offsetX; let y1 = e1.offsetY; cxt.clearRect(0, 0, box.width, box.height); cxt.drawImage(backCanvas, 0, 0, box.width, box.height); cxt.beginPath(); cxt.moveTo(x,y); cxt.lineTo(x1,y1); cxt.closePath(); cxt.stroke(); } } box.onmouseup = function(e){ box.onmousemove = null; context.backStore(box); } } function drawcCircle(box){//畫圓 let cxt = box.getContext("2d"); box.onmousedown = function(e){ //緩存起始點圖像,移動時不斷加載刷新 let backCanvas = document.createElement('canvas'); let backCtx = backCanvas.getContext('2d'); backCanvas.width = box.width; backCanvas.height = box.height; backCtx.drawImage(box, 0, 0, box.width, box.height); let x = e.offsetX; let y = e.offsetY; box.onmousemove = function(e1){ let x1 = e1.offsetX; let y1 = e1.offsetY; let distance=Math.sqrt(Math.pow(Math.abs(x - x1),2)+Math.pow(Math.abs(y - y1),2)); cxt.clearRect(0, 0, box.width, box.height); cxt.drawImage(backCanvas, 0, 0, box.width, box.height); cxt.beginPath(); cxt.arc(x+(x1-x)/2,y+(y1-y)/2,distance/2,0,2*Math.PI); cxt.stroke(); } } box.onmouseup = function(e){ box.onmousemove = null; context.backStore(box); } } function drawRectangle(box){//畫矩形 let cxt = box.getContext("2d"); //與畫圓原理相同,同上 box.onmousedown = function(e){ let backCanvas = document.createElement('canvas'); let backCtx = backCanvas.getContext('2d'); backCanvas.width = box.width; backCanvas.height = box.height; backCtx.drawImage(box, 0, 0, box.width, box.height); let x = e.offsetX; let y = e.offsetY; box.onmousemove = function(e1){ cxt.clearRect(0, 0, box.width, box.height); cxt.drawImage(backCanvas, 0, 0, box.width, box.height); cxt.beginPath(); cxt.rect(x,y,e1.offsetX - x,e1.offsetY - y); cxt.stroke(); } } box.onmouseup = function(e){ box.onmousemove = null; context.backStore(box); } } } </script> </head> <body> <div class="container"> <p class="text-center header-p"> <table class="table"> <thead> <tr> <th width="20%"> <div class="btn-group" data-toggle="buttons"> <label class="btn btn-primary glyphicon glyphicon-pencil" data-toggle="popover" title="素描"> <input type="radio" name="options" value='0'> </label> <label class="btn btn-primary glyphicon" data-toggle="popover" title="圓"> 圓<input type="radio" name="options" value='1'> </label> <label class="btn btn-primary glyphicon" data-toggle="popover" title="矩形"> 矩形<input type="radio" name="options" value='2'> </label> <label class="btn btn-primary glyphicon" data-toggle="popover" title="線段"> 線段<input type="radio" name="options" value='3'> </label> </div> </th> <th width="40%"> <div class="btn-group"> <label class="btn btn-primary glyphicon">調色板:</label> <label class="btn btn-primary glyphicon"> <canvas id="colorPicker"></canvas> </label> </div> </th> <th width="20%" style="text-align: right"> <div class="btn-group"> <button class="btn btn-primary glyphicon" type="button" id ="reset"> 撤銷<span class='glyphicon-chevron-left'></span> </button> <button class="btn btn-primary glyphicon" type="button" id ="advance"> 恢復<span class='glyphicon-chevron-right'></span> </button> <button class="btn btn-primary glyphicon" type="button" id ="save"> 保存 </button> </div> </th> </tr> </thead> </table> </p> <p class="text-center header-body"> <canvas id="myCanvas" style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> </p> </div> </body> </html>