標題很難引人入勝,先放個效果圖好了javascript
若是圖片吸引不了你,那我以爲也就沒啥看的了。html
demo連接: https://win7killer.github.io/demo_set/html_demo/canvas/can_demo/draw_roll_2.html
java
*************************************************git
上次「雷達圖效果」文章很榮幸,被「某天頭條」抓數據抓去了,不開心的是demo連接等全部連接都幹掉了~~~ blabla,連個名字都木有。github
想看的再看下: http://www.cnblogs.com/ufex/p/6655336.htmlcanvas
*************************************************瀏覽器
以前看到的gif效果,爲了這個文章又去找了一下。貌似是ipad的app 「Amaziograph」。看起來真的很爽,很美app
配上我本身畫的圖先:dom
手殘不會畫畫,各位見笑。(手機上瀏覽器畫的哦)函數
a.參考線座標軸 -- 爲了簡單控制參考線顯示隱藏,單獨一個canvas來搞,也不用每次重繪
b.繪畫主體 -- 繪畫效果(canvas畫線);對稱效果(canvas旋轉)
c.配置區 -- 簡單dom
簡單來看,很容易實現嘛
其實就是畫幾條線,可是要均分角度。一種方法是,計算出各個點,而後從中心點發散去畫線;另外一種是,一邊旋轉canvas,一邊畫圓心到統一座標的線。因爲繪畫是需用到canvas旋轉,因此這裏統一使用旋轉來處理。
那麼,就須要先來處理canvas旋轉
1 function drawRotate(deg, fn, _ctx) { 2 _ctx = _ctx || ctx 3 _ctx.save(); 4 _ctx.translate(_ctx.canvas.width / 2, _ctx.canvas.height / 2); 5 _ctx.rotate(deg); 6 fn && fn(_ctx); 7 _ctx.restore(); 8 }
固然,這個是我嘗試屢次以後寫好的方法。
一、存儲ctx狀態到棧,
二、移動旋轉點(canvas座標原點)到canvas中心,
三、旋轉指定角度,
四、執行繪製函數fn,
五、從棧裏邊取回ctx的狀態(包含但不只包含 fillStyle、strokenStyle、translate等等),這裏主要處理的是translate,由於咱們下次用到座標會受影響,因此要讓canva座標原點回到原來的位置。
其實這裏translate仍是比較抽象比較繞的。。。可能我比較遲緩
而後,是繪製參考線座標
1 function baseLine() { 2 ctx_role.clearRect(0, 0, ctx_role.canvas.width, ctx_role.canvas.height); 3 var deg = 360 / pieace; 4 console.log(deg); 5 ctx_role.lineWidth = 1; 6 ctx_role.strokeStyle = 'rgba(0,0,0,.5)'; 7 for (var i = 0, l = pieace; i < l; i++) { 8 drawRotate(i * deg / 180 * Math.PI, function(ctx_role) { 9 draw({ 10 bx: can_role.width / 2, 11 by: can_role.width / 2, 12 ex: can_role.width / 2 + can_role.width, 13 ey: can_role.width / 2 14 }, ctx_role); 15 }, ctx_role); 16 } 17 }
1 function draw(option, _ctx) { 2 _ctx = _ctx || ctx; 3 _ctx.beginPath(); 4 _ctx.moveTo(option.bx - _ctx.canvas.width / 2, option.by - _ctx.canvas.height / 2); 5 _ctx.lineTo(option.ex - _ctx.canvas.width / 2, option.ey - _ctx.canvas.height / 2); 6 _ctx.stroke(); 7 }
這樣,就繪製完成參考線。
首先處理通常的畫線。跟拖拽效果相似,在move過沖中一直畫線連接兩個點。對拖拽不瞭解的能夠去了解下,直接上代碼
1 function bindPc() { 2 can.onmousedown = function(e) { 3 if (e.button != 0) { 4 return false; 5 } 6 7 var op = {}; 8 op.ex = op.bx = e.clientX - can.parentElement.offsetLeft + window.scrollX; 9 op.ey = op.by = e.clientY - can.parentElement.offsetTop + window.scrollY; 10 drawFn(op); 11 document.onmousemove = function(e) { 12 document.body.style.cursor = 'pointer'; 13 op.bx = op.ex; 14 op.by = op.ey; 15 op.ex = e.clientX - can.parentElement.offsetLeft + window.scrollX; 16 op.ey = e.clientY - can.parentElement.offsetTop + window.scrollY; 17 drawFn(op); 18 }; 19 document.onmouseup = function() { 20 document.body.style.cursor = 'default'; 21 document.onmouseup = document.onmousemove = null; 22 }; 23 }; 24 }
1 function drawFn(op) { 2 var deg = Math.floor(360 / pieace); 3 for (var i = 0, l = 360; i < l; i += deg) { 4 drawRotate(i / 180 * Math.PI, function(ctx) { 5 draw(op); 6 }); 7 } 8 }
須要注意,e.button 用來判斷是鼠標哪一個鍵,0是左鍵
這裏又用到了前邊的drawRotate 和 draw。
************************************
至此,應該能夠畫出對稱的線條了。
如下就是錦上添花的事情了
************************************
增長移動端的繪製支持(慚愧,沒怎麼寫過移動端,歡迎多指教)
1 function bindWp() { 2 can.addEventListener('touchstart', function(e) { 3 op = can.op = {}; 4 op.ex = op.bx = e.touches[0].clientX - can.parentElement.offsetLeft + window.scrollX; 5 op.ey = op.by = e.touches[0].clientY - can.parentElement.offsetTop + window.scrollY; 6 drawFn(op); 7 can.addEventListener('touchmove', touchMoveFn); 8 can.addEventListener('touchend', touchEndFn); 9 }); 10 11 function touchEndFn() { 12 document.body.style.cursor = 'default'; 13 can.removeEventListener('touchmove', touchMoveFn); 14 can.removeEventListener('touchend', touchEndFn); 15 } 16 17 function touchMoveFn(e) { 18 op = can.op; 19 document.body.style.cursor = 'pointer'; 20 op.bx = op.ex; 21 op.by = op.ey; 22 op.ex = e.touches[0].clientX - can.parentElement.offsetLeft + window.scrollX; 23 op.ey = e.touches[0].clientY - can.parentElement.offsetTop + window.scrollY; 24 drawFn(op); 25 return false; 26 } 27 }
這裏dom比較簡單,就略過了。只說一項,下載canvas圖片到本地
最簡單的,右鍵保存圖片到本地,可是你確定會罵我傻,誰不知道這操做啊;那麼就來稍微裝X一下吧
線上代碼
1 function download() { 2 var data = can.toDataURL('image/png', 0.8); 3 var $a = document.createElement('a'); 4 $a.download = imgName.value || 'default.png'; 5 $a.target = '_blank'; 6 $a.href = data; 7 $a.click(); 8 }
(寫這個博客的時候,返現本身把這個方法寫麻煩了,繞遠了。/手動尷尬,這裏直接改了)
關鍵點在於 a.download屬性,這個是把文件下載到本地的關鍵哦,而後要把canvas轉成base64(canvas.toDataUrl方法,不清楚的能夠去去了解下,這裏再也不贅述)
******************************************************
最後,附上完整代碼(可能會和上邊的有點出如,還在調整)
<!DOCTYPE html> <html lang="zh"> <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"> <mtea author="win7killer@163.com"></mtea> <title>Document</title> <style> * { margin: 0; padding: 0; } p { line-height: 15px; font-size: 12px; } @media screen and (max-width: 768px) { .wrapper { width: auto; position: relative; overflow: hidden; } } @media screen and (min-width: 769px) { .wrapper { width: 600px; height: 600px; margin: 100px auto 0; position: relative; overflow: hidden; } #panel_box { position: fixed; top: 20px; right: 20px; width: 200px; } } canvas { background: #fafafa; display: block; } #can_role { background: none; position: absolute; top: 0px; left: 0px; pointer-events: none; } #panel_box { padding: 10px; margin-top: 10px; border: 1px solid rgba(10, 10, 10, .7); box-shadow: 10px 7px 10px #999; z-index: 100; } input { width: 80px; margin-left: 20px; } label { text-align: justify; } </style> </head> <body> <div class="wrapper" id="wrapper"> <canvas id="can_role"></canvas> <canvas id="can"></canvas> </div> <div id="panel_box"> <p> <label>畫筆顏色<input id="color_val" type="color" value="#0099ff"/></label> </p> <p> <label>畫筆寬度<input type="number" id="line_width_val" min="1" max="20" value="2"/></label> </p> <p> <label>扇形份數<input type="number" id="pieaceNum" min="1" max="200" value="12"/></label> </p> <p> <label>參考線<input type="checkbox" id="onOff" checked="checked"/></label> </p> <p class="img_name_box"> <label>圖片名稱<input type="text" id="imgName" placeholder="ex:test.png"></label> </p> <p> <a href="javascript:;" id="save_btn" target="">下載到本地</a> </p> </div> <script> var pieace = 6; var ctx = can.getContext('2d'); var ctx_role = can_role.getContext('2d'); can.width = can.height = can_role.width = can_role.height = window.screen.width > 768 ? 600 : window.screen.width; ctx_role.lineJoin = ctx.lineJoin = "round"; ctx_role.lineCap = ctx.lineCap = "round"; function drawFn(op) { var deg = Math.floor(360 / pieace); for (var i = 0, l = 360; i < l; i += deg) { drawRotate(i / 180 * Math.PI, function(ctx) { draw(op); }); } } function draw(option, _ctx) { _ctx = _ctx || ctx; _ctx.beginPath(); _ctx.moveTo(option.bx - _ctx.canvas.width / 2, option.by - _ctx.canvas.height / 2); _ctx.lineTo(option.ex - _ctx.canvas.width / 2, option.ey - _ctx.canvas.height / 2); _ctx.stroke(); } function drawRotate(deg, fn, _ctx) { _ctx = _ctx || ctx _ctx.save(); _ctx.translate(_ctx.canvas.width / 2, _ctx.canvas.height / 2); _ctx.rotate(deg); fn && fn(_ctx); _ctx.restore(); } function baseLine() { ctx_role.clearRect(0, 0, ctx_role.canvas.width, ctx_role.canvas.height); var deg = 360 / pieace; ctx_role.lineWidth = 1; ctx_role.strokeStyle = 'rgba(0,0,0,.5)'; for (var i = 0, l = pieace; i < l; i++) { drawRotate(i * deg / 180 * Math.PI, function(ctx_role) { draw({ bx: can_role.width / 2, by: can_role.width / 2, ex: can_role.width / 2 + can_role.width, ey: can_role.width / 2 }, ctx_role); }, ctx_role); } } function download() { var data = can.toDataURL('image/png', 0.8); var $a = document.createElement('a'); $a.download = imgName.value || 'default.png'; $a.target = '_blank'; $a.href = data; $a.click(); // if (typeof MouseEvent === 'function') { // var evt = new MouseEvent('click', { // view: window, // bubbles: true, // cancelable: false // }); // $a.dispatchEvent(evt); // } } function bindPc() { can.onmousedown = function(e) { if (e.button != 0) { return false; } var op = {}; op.ex = op.bx = e.clientX - can.parentElement.offsetLeft + window.scrollX; op.ey = op.by = e.clientY - can.parentElement.offsetTop + window.scrollY; drawFn(op); document.onmousemove = function(e) { document.body.style.cursor = 'pointer'; op.bx = op.ex; op.by = op.ey; op.ex = e.clientX - can.parentElement.offsetLeft + window.scrollX; op.ey = e.clientY - can.parentElement.offsetTop + window.scrollY; drawFn(op); }; document.onmouseup = function() { document.body.style.cursor = 'default'; document.onmouseup = document.onmousemove = null; }; }; } function bindWp() { can.addEventListener('touchstart', function(e) { op = can.op = {}; op.ex = op.bx = e.touches[0].clientX - can.parentElement.offsetLeft + window.scrollX; op.ey = op.by = e.touches[0].clientY - can.parentElement.offsetTop + window.scrollY; drawFn(op); can.addEventListener('touchmove', touchMoveFn); can.addEventListener('touchend', touchEndFn); }); function touchEndFn() { document.body.style.cursor = 'default'; can.removeEventListener('touchmove', touchMoveFn); can.removeEventListener('touchend', touchEndFn); } function touchMoveFn(e) { op = can.op; document.body.style.cursor = 'pointer'; op.bx = op.ex; op.by = op.ey; op.ex = e.touches[0].clientX - can.parentElement.offsetLeft + window.scrollX; op.ey = e.touches[0].clientY - can.parentElement.offsetTop + window.scrollY; drawFn(op); return false; } } function bindSets() { color_val.onchange = function() { ctx.strokeStyle = color_val.value; } line_width_val.onchange = function() { ctx.lineWidth = line_width_val.value; } pieaceNum.onchange = function() { ctx.clearRect(0, 0, can.width, can.height); reset(); } onOff.onchange = function() { if (this.checked == true) { can_role.style.display = 'block'; } else { can_role.style.display = 'none'; } } } function bind() { bindPc(); bindWp(); bindSets(); save_btn.onclick = download; } function reset() { pieace = pieaceNum.value; ctx.strokeStyle = 'rgba(100,100,100,.7)'; baseLine(); ctx.lineWidth = line_width_val.value; ctx.strokeStyle = color_val.value; } function init() { reset(); bind(); } init(); </script> </body> </html>
**************偷偷留個名字,防抓 博客園-fe-bean***************
1.canvas_translate
2.canvas_rotate
3.canvas_toDataUrl
4.a.download && base64
其他的想起來再添加吧
最後,歡迎你們多提意見、交流,點贊轉載那就更棒了。
再丟一張圖
下期再見咯~~~
**************** 少俠留步,能看到這裏的,我要給大家一個獎勵 ***************
這個demo是能夠在移動端玩的,意味着有電容筆的親,能夠爽啊~(個別瀏覽器腦殘會左右來回跑~~)
沒有電容筆的親,確定是大多數,咱們同樣能玩啊!!!
叫大家快速作一款電容筆(固然沒那麼好用)
1.找一隻木質鉛筆
2.削出鉛筆頭
3.把鉛筆頭斜着磨平,如圖
4.用磨平這一側去電容屏上畫(開始吧)
我上邊那張圖就是拿鉛筆畫的~~~
************************************