Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 91精品91,中文字幕日本久久2019,色噜噜综合网

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          Canvas怎樣修改線條的寬度、顏色和形狀?

          Canvas怎樣修改線條的寬度、顏色和形狀?

          Canvas中,線的默認顏色為黑色,寬度為lpx,但我們可以使用相關(guān)屬性為線添加不同的樣式。下面我們將從寬度、描邊顏色、端點形狀3方面詳細講解線條樣式的設(shè)置方法。

          1.設(shè)置線條寬度

          使用lineWidth屬性可以定義線的寬度,該屬性的取值為數(shù)值(不帶單位),以像素為計量。設(shè)置線的寬度的示例代碼如下:

          context.lineNidth='10';

          上述代碼中設(shè)置了線的寬度為10。

          2.設(shè)置描邊顏色

          使用strokeStyle屬性可以定義線的描邊顏色,該屬性的取值為十六進制顏色值或顏色的英文名。設(shè)置描邊顏色的示例代碼如下:

          context.strokeStyle='4f00';
          context.strokeStyle='red';

          在上述代碼中,兩種方式都可以用于設(shè)置線的描邊顏色為紅色。

          3.設(shè)置端點形狀

          默認情況下,線的端點是方形的,通過lineCap屬性可以改變端點的形狀,示例代碼如下:

          context.1ineCap=,屬性值';

          lineCap屬性的取值有3個,具體如表所示。

          lineCap屬性的取值

          了解了設(shè)置線的樣式的基本方法后,下面演示如何為線設(shè)置樣式。

          (1)創(chuàng)建C:icodekchapter02\demol3.html,創(chuàng)建畫布并為線設(shè)置寬度、顏色和端點形狀,具體代碼如下:

          <!DOCTYPE html>
          <html>
          <head>
            <meta charset="UTF-8">
            <title>Document</title>
          </head>
          <body>
            <canvas id="cas" width="300" height="300">
               您的測覽器不支持Canvas標(biāo)簽
            </canvas>
            <script>
             var context=document.getElementById('cas').getContext('2d');
             context.moveTo(10,10);          // 定義初始位置
             context.1ineTo(300,10);         // 定義連線端點
             context.lineWidth='10';         // 設(shè)置線的寬度
             context.strokeStyle='red';      // 設(shè)置線的顏色
             context.lineCap='round';        // 設(shè)置線的端點形狀
             context.stroke();               // 定叉描邊
            </script>
          </body>
          </html>

          上述代碼中,第15行代碼設(shè)置了線的寬度為10像素:第16行代碼設(shè)置了線的顏色為紅色;第17行代碼設(shè)置了線的端點為圓形。

          (2)保存代碼,在瀏覽器中測試,頁面效果如圖所示。

          設(shè)置線的寬度、顏色和端點形狀

          頁面顯示一條紅色的線,說明我們已經(jīng)成功為線設(shè)置了樣式。


          者:前端日志

          轉(zhuǎn)發(fā)鏈接:https://mp.weixin.qq.com/s/zoEfqIwfsnAWVO8L7xuUuQ

          例簡介

          用canvas實現(xiàn)涂鴉效果,包括更換筆觸大小顏色、換背景圖、橡皮檫、歷史記錄、清屏等功能,并能保存涂鴉圖片到本地。

          編寫靜態(tài)頁面

          html代碼和css樣式如下圖,這一塊比較簡單,也不是本文重點,可自行查看。

          <!DOCTYPE html>
          <html lang="zh">
          
          
          <head>
              <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
              <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
              <meta name="renderer" content="webkit">
              <meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1" />
              <title>涂鴉</title>
              <link rel="shortcut icon" href="#" />
              <link rel="stylesheet" type="text/css" href="css/base.css">
              <link rel="stylesheet" type="text/css" href="css/handWriting.css">
          </head>
          
          
          <body>
              <div class="wrapper">
                  <canvas class="offCanvas"></canvas>
                  <canvas class="canvas"></canvas>
              </div>
              <div class="footer">
                  <div class="control-button">
                      <div class="item colorButton"><img src="images/colors.png" alt=""><span>黑色</span></div>
                      <div class="item sizeButton"><img src="images/size.png" alt=""><span>中筆</span></div>
                      <div class="item bgButton"><img src="images/bg.png" alt=""><span>背景</span></div>
                      <div class="item rubberButton"><img src="images/rubber.png" alt=""><span>擦掉</span></div>
                      <div class="item historyButton"><img src="images/history.png" alt=""><span>歷史</span></div>
                      <div class="item clearButton"><img src="images/clear.png" alt=""><span>清屏</span></div>
                      <div class="item saveButton"><img src="images/save.png" alt=""><span>保存</span></div>
                  </div>
                  <div class="pop-up colors-panel">
                      <div class="title">筆觸顏色<span class="closeBtn"></span></div>
                      <div class="colors">
                          <div class="lineColors">
                              <div><span class="red" data-text="紅色" data-color="#ff0000"></span></div>
                              <div><span class="blue" data-text="藍色" data-color="#0000ff"></span></div>
                              <div><span class="green" data-text="綠色" data-color="#00ff00"></span></div>
                              <div><span class="black" data-text="黑色" data-color="#000000"></span></div>
                              <div><span class="orange" data-text="橙色" data-color="#ff6302"></span></div>
                          </div>
                          <div class="lineColors">
                              <div><span class="red" data-text="紅色" data-color="#ff0000"></span></div>
                              <div><span class="blue" data-text="藍色" data-color="#0000ff"></span></div>
                              <div><span class="green" data-text="綠色" data-color="#00ff00"></span></div>
                              <div><span class="black" data-text="黑色" data-color="#000000"></span></div>
                              <div><span class="orange" data-text="橙色" data-color="#ff6302"></span></div>
                          </div>
                      </div>
                  </div>
                  <div class="pop-up size-panel">
                      <div class="title">筆觸大小<span class="closeBtn"></span></div>
                      <div class="sizes">
                          <div class="lineSizes"><span data-lineWidth="10" data-text="大筆" class="big"></span></div>
                          <div class="lineSizes"><span data-lineWidth="30" data-text="中筆" class="middle"></span></div>
                          <div class="lineSizes"><span data-lineWidth="50" data-text="小筆" class="small"></span></div>
                      </div>
                  </div>
                  <div class="pop-up bg-panel">
                      <div class="title">推薦背景<span class="closeBtn"></span></div>
                      <div class="list">
                          <img src="images/white.jpg" alt="" />
                          <img src="images/white.jpg" alt="" />
                          <img src="images/white.jpg" alt="" />
                          <img src="images/white.jpg" alt="" />
                          <img src="images/white.jpg" alt="" />
                      </div>
                  </div>
                  <!-- 添加橡皮檫列表和歷史記錄列表樣式 -->
                  <div class="pop-up rubber-panel">
                      <div class="title">橡皮檫大小<span class="closeBtn"></span></div>
                      <div class="rubbers">
                          <div class="first">大小:</div>
                          <div class="second"><input type="range" min="1" max="50" value="25" step="1" name="大小" /></div>
                          <div class="last"><span class="rubberSize">25</span>像素</div>
                      </div>
                  </div>
                  <div class="pop-up history-panel">
                      <div class="title">歷史記錄<span class="closeBtn"></span></div>
                      <div class="history">
                          <div class="lineBox"></div>
                      </div>
                  </div>
              </div>
              <div class="offImgs" style="display: none;"></div>
              <script src="js/jquery.min.js"></script>
              <script src="js/handWriting.js"></script>
          </body>
          
          
          </html>
          html,
          body,
          .wrapper {
              height: 100%
          }
          
          
          .wrapper {
              position: relative;
              padding-bottom: 60px;
              box-sizing: border-box
          }
          
          
          .wrapper .offCanvas,
          .wrapper .canvas {
              position: absolute;
              top: 0;
              left: 0
          }
          
          
          .footer {
              position: absolute;
              bottom: 0;
              width: 100%;
              height: 60px;
              background-color: #fff;
              box-shadow: 0 0 10px 3px #e2e2e2;
              overflow: hidden;
          }
          
          
          .footer .control-button {
              display: flex;
              height: 100%
          }
          
          
          .control-button .item {
              flex: 1;
              text-align: center
          }
          
          
          .control-button .item img {
              width: 22px;
              height: 22px;
              margin: 8px auto 5px;
              display: block;
          }
          
          
          .control-button .item span {
              color: #2e344a;
              font-size: 12px
          }
          
          
          
          
          
          
          /*后面添加*/
          /*筆觸設(shè)置*/
          .footer .pop-up{display:none;height:130px;padding:0 15px}
          .pop-up .title{font-size:14px;color:#eb4985;margin:10px 0 15px;text-align:center}
          .pop-up .title .closeBtn{background:url("../images/close.png") no-repeat;background-size:100%;width:20px;height:20px;float:right}
          .pop-up .colors{overflow:hidden}
          .pop-up .lineColors div{width:20%;float:left;margin:6px 0}
          .pop-up .lineColors span{display:block;width:28px;height:28px;margin:auto;border-radius:50%}
          .pop-up .lineColors span.red{background-color:#f00}
          .pop-up .lineColors span.blue{background-color:#00f}
          .pop-up .lineColors span.green{background-color:#0f0}
          .pop-up .lineColors span.black{background-color:#000}
          .pop-up .lineColors span.orange{background-color:#ff6302}
          .pop-up .sizes{margin-top:20px}
          .pop-up .sizes .lineSizes{height:30px;cursor:pointer}
          .pop-up .sizes .big{display:block;height:10px;width:100%;background-color:#eb4985;border-radius:3px}
          .pop-up .sizes .middle{display:block;height:6px;width:100%;background-color:#eb4985;border-radius:3px}
          .pop-up .sizes .small{display:block;height:3px;width:100%;background-color:#eb4985;border-radius:3px}
          .pop-up .list{height:80px;line-height:80px}
          .pop-up .list img{width:20%;float:left;padding:5px;box-sizing:border-box}
          /*橡皮檫樣式*/
          .rubbers {
              display: flex;
              color: #2e344a;
              font-size: 14px;
              margin-top: 40px;
          }
          .rubbers div {
              flex: 1;
          }
          .rubbers .second {
              flex: 5;
          }
          .rubbers .second input { /*滑動條的樣式*/
              width: 100%;
              -webkit-appearance: none;
              height: 3px;
              border-radius: 5px;
              vertical-align: super;
              background-color: #2e344a;
          }
          .rubbers .second input::-webkit-slider-thumb { /*滑動條的樣式*/
              -webkit-appearance: none;
              height: 25px;
              width: 25px;
              background-color: #eb4985;
              border-radius: 50%;
          }
          .rubbers .last {
              text-align: right;
          }
          
          
          
          
          .history-panel .history {
              overflow-x: scroll;
              -webkit-overflow-scrolling: touch;
          }
          .history-panel .lineBox img {
              width: 70px;
              height: 70px;
              border: 1px solid #2e344a;
              margin-right: 8px;
          }

          實現(xiàn)原理

          $(function() {
              var offCanvas=$('.offCanvas')[0]; // 用于更換背景圖
              var offCtx=offCanvas.getContext('2d');
              var canvas=$('.canvas')[0]; // 用于涂鴉
              var ctx=canvas.getContext('2d');
          
          
              var lastCoordinate=null; // 前一個坐標(biāo)
              var lastTimestamp=0; // 前一個時間戳
              var lastLineWidth=-1; // 用于線光滑過度
              var point=null; // 存儲鼠標(biāo)或觸發(fā)坐標(biāo)
              var sizeWidth=30; // 中筆觸計算值
              var strokeColor='#000'; // 筆觸顏色默認黑色
              var imgSrc=null; // 背景圖片地址
              var imgArray=[]; // 存儲背景圖和涂鴉圖
              var rubberSize=25; // 存儲橡皮檫大小
              var flag=true; // 用于判斷涂鴉還是擦除
              var footerHeight=$('.footer').height(); // 獲取底部高度
          
          
              offCanvas.width=$(window).width();
              offCanvas.height=$(window).height() - footerHeight;
              canvas.width=$(window).width();
              canvas.height=$(window).height() - footerHeight;
          
          
              // 選擇顏色
              $('.lineColors span').click(function() {
                  strokeColor=$(this).attr('data-color'); // 獲取顏色值,用于替換筆觸顏色
                  var colorName=$(this).attr('data-text'); // 獲取顏色文字,用于替換操作欄文字
                  $('.colorButton span').html(colorName); // 替換操作欄文字
          
          
                  animatePanel('.colors-panel', '-130px', '.control-button', '60px'); // 收起顏色列表顯示操作欄
              });
              // 選擇大小
              $('.lineSizes span').click(function() {
                  sizeWidth=$(this).attr('data-lineWidth'); // 獲取大小值,用于計算筆觸大小
                  var sizeName=$(this).attr('data-text'); // 獲取大小文字,用于替換操作欄文字
                  $('.sizeButton span').html(sizeName); // 替換操作欄文字
          
          
                  animatePanel('.size-panel', '-130px', '.control-button', '60px'); // 收起大小列表顯示操作欄
              });    
              // canvas觸摸事件
              $('.canvas').on('touchstart', function(event) {
                  point={ x: event.originalEvent.targetTouches[0].clientX, y: event.originalEvent.targetTouches[0].clientY };
                  lastCoordinate=windowToCanvas(point.x, point.y);
                  lastTimestamp=new Date().getTime();
              });
              $('.canvas').on('touchmove', function(event) {
                  point={ x: event.originalEvent.targetTouches[0].clientX, y: event.originalEvent.targetTouches[0].clientY };
                  var curCoordinate=windowToCanvas(point.x, point.y);        
          
          
                  if (flag) { // 涂鴉
                      var curTimestamp=new Date().getTime();
                      var s=calcDistance(lastCoordinate, curCoordinate); // 計算兩點之間的距離         
                      var t=curTimestamp - lastTimestamp; // 計算兩點之間的時間差
                      var curLineWidth=caleLineWidth(s, t, sizeWidth);
          
          
                      drawLine(ctx, lastCoordinate.x, lastCoordinate.y, curCoordinate.x, curCoordinate.y, curLineWidth, strokeColor);
          
          
                      lastCoordinate=curCoordinate; // 現(xiàn)在坐標(biāo)替換前一個坐標(biāo)
                      lastTimestamp=curTimestamp;
                      lastLineWidth=curLineWidth;
                  } else { // 擦掉
                      ctx.save();
                      ctx.beginPath();
                      ctx.arc(curCoordinate.x, curCoordinate.y, rubberSize/2, 0, Math.PI * 2);
                      ctx.clip();
                      ctx.clearRect(curCoordinate.x - rubberSize/2, curCoordinate.y - rubberSize/2, rubberSize, rubberSize); // 清除涂鴉畫布內(nèi)容
                      ctx.restore();
                  }
              });
              $('.canvas').on('touchend', function() {
                  var imageSrc=canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream'); // 畫布轉(zhuǎn)換為圖片地址
                  $('.lineBox').append('<img src="' + imageSrc + '" />');
                  var boxWidth=$('.lineBox img').length * 80; // 80為圖片寬度(72)+間隔(8)
                  $('.lineBox').css({ // 設(shè)置lineBox寬度
                      width: boxWidth + 'px'
                  });
              });
          
          
              // 根據(jù)不同速度計算線的寬度函數(shù)
              function caleLineWidth(s, t, brushWidth) {
                  var v=s / t; // 獲取速度
                  // 聲明最大最小速度和最大最小邊界
                  var maxVelocity=10,
                      minVelocity=0.1,
                      maxLineWidth=Math.min(30, canvas.width / brushWidth), // 避免手機端線條太粗
                      minLineWidth=1,
                      resultLineWidth; // 用于返回的線寬度
          
          
                  if (v <=minVelocity) {
                      resultLineWidth=maxLineWidth;
                  } else if (v >=maxVelocity) {
                      resultLineWidth=minLineWidth;
                  } else {
                      resultLineWidth=maxLineWidth - (v - minVelocity) / (maxVelocity - minVelocity) * (maxLineWidth - minLineWidth);
                  }
                  if (lastLineWidth==-1) { // 開始時候
                      return resultLineWidth;
                  } else {
                      return resultLineWidth * 2 / 3 + lastLineWidth * 1 / 3; // lastLineWidth占得比重越大越平滑
                  }
              }
              // 計算兩點之間的距離函數(shù)
              function calcDistance(lastCoordinate, curCoordinate) {
                  var distance=Math.sqrt(Math.pow(curCoordinate.x - lastCoordinate.x, 2) + Math.pow(curCoordinate.y - lastCoordinate.y, 2));
                  return distance;
              }
              // 坐標(biāo)轉(zhuǎn)換
              function windowToCanvas(x, y) {
                  var bbox=canvas.getBoundingClientRect();
                  return { x: x - bbox.left, y: y - bbox.top };
              }
              // 繪制直線
              function drawLine(context, x1, y1, x2, y2, /*optional*/ lineWidth, /*optional*/ strokeColor) {
                  context.beginPath();
                  context.lineTo(x1, y1);
                  context.lineTo(x2, y2);
          
          
                  context.lineWidth=lineWidth;
                  context.lineCap='round'; // 線與線交合不會產(chǎn)生空隙
                  context.lineJoin='round';
                  context.strokeStyle=strokeColor; // 默認筆觸黑色
          
          
                  context.stroke();
              }
              // 選擇背景
              $('.bg-panel img').click(function() {
                  imgSrc=$(this).attr('src'); // 獲取圖片src
                  drawImg(imgSrc); // 畫圖
          
          
                  animatePanel('.bg-panel', '-130px', '.control-button', '60px');
              });
              // 繪制圖像到畫布
              function drawImg(changeValue) {
                  offCtx.clearRect(0, 0, canvas.width, canvas.height); // 先清除畫布
                  var changeImg=new Image();
                  // changeImg.crossOrigin='Anonymous';
                  changeImg.src=changeValue;
                  changeImg.onload=function() {
                      offCtx.drawImage(changeImg, 0, 0, canvas.width, canvas.height);
                  };
              }
              // 清屏
              $('.clearButton').click(function() {
                  ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除涂鴉畫布內(nèi)容
                  offCtx.clearRect(0, 0, canvas.width, canvas.height); // 清除背景圖畫布內(nèi)容
              });
              // 保存涂鴉效果
              $('.saveButton').click(function() {
                  // toDataURL兼容大部分瀏覽器,缺點就是保存的文件沒有后綴名
                  if (imgSrc) { // 存在背景圖才執(zhí)行
                      imgArray.push(offCanvas.toDataURL('image/png').replace('image/png', 'image/octet-stream'));
                  }
                  imgArray.push(canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream'));
          
          
                  compositeGraph(imgArray);
              });
              /**
               * [離屏合成圖]
               * @param  {[type]} imgArray   [背景圖畫布和涂鴉畫布的地址數(shù)組]
               */
              function compositeGraph(imgArray) {
                  // 下載后的文件名
                  var filename='canvas_' + (new Date()).getTime() + '.png';
          
          
                  var compositeCanvas=document.createElement('canvas');
                  compositeCanvas.width=canvas.width;
                  compositeCanvas.height=canvas.height;
                  var compositeCtx=compositeCanvas.getContext('2d');
                  $.each(imgArray, function(index, val) {
                      $('.offImgs').append('<img src="' + val + '" />'); // 增加img元素用于獲取合成
                  });
                  $.each($('.offImgs img'), function(index, val) {
                      val.onload=function() {
                          compositeCtx.drawImage(val, 0, 0); // 循環(huán)繪制圖片到離屏畫布
                      };
                  });
                  var timer=setTimeout(function() {
                      var compositeImg=compositeCanvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
                      saveFile(compositeImg, filename);
                      timer=null; // 注銷定時器
                  }, 50);
              }
              /**
               * 模擬鼠標(biāo)點擊事件進行保存
               * @param  {String} data     要保存到本地的圖片數(shù)據(jù)
               * @param  {String} filename 文件名
               */
              function saveFile(data, filename) {
                  var saveLink=document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
                  saveLink.href=data;
                  saveLink.download=filename; // download只兼容chrome和firefox,需要兼容全部瀏覽器,只能用服務(wù)器保存
          
          
                  var event=document.createEvent('MouseEvents');
                  event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
                  saveLink.dispatchEvent(event);
              }
              // 點擊顏色按鈕彈出顏色列表
              $('.colorButton').click(function() {
                  animatePanel('.control-button', '-60px', '.colors-panel', '130px');
                  flag=true; // 點擊顏色時候變?yōu)橥盔f狀態(tài)
              });
              // 點擊顏色列表的關(guān)閉按鈕
              $('.colors-panel .closeBtn').click(function() {
                  animatePanel('.colors-panel', '-130px', '.control-button', '60px');
              });
              // 點擊大小按鈕彈出大小列表
              $('.sizeButton').click(function() {
                  animatePanel('.control-button', '-60px', '.size-panel', '130px');
                  flag=true; // 點擊大小時候變?yōu)橥盔f狀態(tài)
              });
              // 點擊大小列表的關(guān)閉按鈕
              $('.size-panel .closeBtn').click(function() {
                  animatePanel('.size-panel', '-130px', '.control-button', '60px');
              });
              // 點擊背景按鈕彈出背景列表
              $('.bgButton').click(function() {
                  animatePanel('.control-button', '-60px', '.bg-panel', '130px');
              });
              // 點擊背景列表的關(guān)閉按鈕
              $('.bg-panel .closeBtn').click(function() {
                  animatePanel('.bg-panel', '-130px', '.control-button', '60px');
              });
              // 點擊擦掉按鈕彈出橡皮檫大小列表
              $('.rubberButton').click(function() {
                  animatePanel('.control-button', '-60px', '.rubber-panel', '130px');
                  flag=false; // 點擊擦掉時候變?yōu)橄鹌ら郀顟B(tài)
              });
              // 點擊橡皮檫大小列表的關(guān)閉按鈕
              $('.rubber-panel .closeBtn').click(function() {
                  animatePanel('.rubber-panel', '-130px', '.control-button', '60px');
              });
              // 拖動滑動條獲取數(shù)值
              $('.rubbers .second input').on('touchmove', function() {
                  rubberSize=$(this)[0].value;
                  $('.rubberSize').html(rubberSize);
              });
              // 點擊歷史按鈕彈出歷史記錄列表
              $('.historyButton').click(function() {
                  animatePanel('.control-button', '-60px', '.history-panel', '130px');
              });
              // 點擊歷史記錄列表的關(guān)閉按鈕
              $('.history-panel .closeBtn').click(function() {
                  animatePanel('.history-panel', '-130px', '.control-button', '60px');
              });
              // 點擊歷史記錄圖片繪制到畫布
              $('.lineBox').on('click', 'img', function() { // 事件委托
                  ctx.clearRect(0, 0, canvas.width, canvas.height);
                  ctx.drawImage($(this)[0], 0, 0, canvas.width, canvas.height); // 繪制點擊的圖片到畫布
              });
          
          
          
          
          
          
              // 底部操作欄和彈出框交互函數(shù)
              function animatePanel(fName, fHeight, sName, sHeight) {
                  $(fName).slideUp(300);
                  $('.footer').animate({'bottom': fHeight}, 300);
                  var timer=setTimeout(function() {
                      $(sName).slideDown(500);
                      $('.footer').animate({'bottom': 0, 'height': sHeight}, 500);
                      timer=null;
                  }, 0);
              }
              // 阻止手機滑動時拖動頁面
              $('.wrapper').on('touchmove', function(event) {
                  event.preventDefault();
              });
          });

          聲明變量和初始化數(shù)據(jù),具體用途說明都已經(jīng)有備注,主要分析重點:

          1、offCanvas用于更換背景圖的畫布,所以寬高跟涂鴉畫布(canvas)一致,默認空白;

          2、背景圖畫布(offCanvas)和涂鴉畫布(canvas)的高度都需要減去footerHeight,避免被底部操作欄遮住;

          3、imgSrc設(shè)置背景圖片地址,也用于判斷是否有背景圖;

          4、imgArray存儲背景圖和涂鴉圖,用于循環(huán)添加到元素img;

          5、rubberSize設(shè)置橡皮檫默認大小,該值跟html中input[type="range"]的value值一致,后面用于計算清除區(qū)域;

          6、flag用于判斷是涂鴉還是擦除(true為涂鴉,false為擦除);

          7、strokeColor設(shè)置默認筆觸的顏色,跟首頁導(dǎo)航欄底部顯示的文字對應(yīng);

          8、imgSrc存儲背景圖片地址,用于繪制圖片到畫布。


          分析基本函數(shù)(重點):

          1、caleLineWidth根據(jù)不同速度計算線的寬度函數(shù),因為涂鴉過程速度快慢會影響線的寬度,為了更逼真,增加該函數(shù),可根據(jù)實際情況對里面數(shù)據(jù)進行修改;

          2、calcDistance計算兩點之間的距離函數(shù),這個是用于caleLineWidth(距離/時間),一個簡單的兩點計算公式(兩邊長平方后相加再開方);

          3、windowToCanvas坐標(biāo)轉(zhuǎn)換函數(shù),屏幕坐標(biāo)轉(zhuǎn)換為在畫布上面的坐標(biāo),不然畫出來的線會有偏移;其實該實例是滿屏(width: 100%),是可以不用轉(zhuǎn)換,主要是為了給不是滿屏?xí)r候用的。

          // 根據(jù)不同速度計算線的寬度函數(shù)
          function caleLineWidth(s, t, brushWidth) {
              var v=s / t; // 獲取速度
              // 聲明最大最小速度和最大最小邊界
              var maxVelocity=10,
                  minVelocity=0.1,
                  maxLineWidth=Math.min(30, canvas.width / brushWidth), // 避免手機端線條太粗
                  minLineWidth=1,
                  resultLineWidth; // 用于返回的線寬度
              if (v <=minVelocity) { resultLineWidth=maxLineWidth; } else if (v >=maxVelocity) {
                  resultLineWidth=minLineWidth;
              } else {
                  resultLineWidth=maxLineWidth - (v - minVelocity) / (maxVelocity - minVelocity) * (maxLineWidth - minLineWidth);
              }
              if (lastLineWidth==-1) { // 開始時候
                  return resultLineWidth;
              } else {
                  return resultLineWidth * 2 / 3 + lastLineWidth * 1 / 3; // lastLineWidth占得比重越大越平滑
              }
          }
          // 計算兩點之間的距離函數(shù)
          function calcDistance(lastCoordinate, curCoordinate) {
              var distance=Math.sqrt(Math.pow(curCoordinate.x - lastCoordinate.x, 2) + Math.pow(curCoordinate.y - lastCoordinate.y, 2));
              return distance;
          }
          // 坐標(biāo)轉(zhuǎn)換
          function windowToCanvas(x, y) {
              var bbox=canvas.getBoundingClientRect();
              return { x: x - bbox.left, y: y - bbox.top };
          }

          4、函數(shù)saveFile,因為canvas沒辦法直接保存為圖片,所以下面的代碼利用了模擬鼠標(biāo)點擊事件進行保存,且能自定義文件名;

          /**
           * 模擬鼠標(biāo)點擊事件進行保存
           * @param  {String} data     要保存到本地的圖片數(shù)據(jù)
           * @param  {String} filename 文件名
           */
          function saveFile(data, filename) {
              var saveLink=document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
              saveLink.href=data;
              saveLink.download=filename; // download只兼容chrome和firefox,需要兼容全部瀏覽器,只能用服務(wù)器保存
          
          
              var event=document.createEvent('MouseEvents');
              event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
              saveLink.dispatchEvent(event);
          }

          5、添加阻止拖動函數(shù),詳細代碼如下圖;因為H5在手機滑動頁面時候,頁面會被拖動,導(dǎo)致跟手指涂鴉沖突,體驗不好,所以需要增加該函數(shù),阻止頁面被拖動;

          // 阻止手機滑動時拖動頁面
          $('.wrapper').on('touchmove', function(event) {
              event.preventDefault();
          });

          6、添加底部按鈕和彈出列表的交互效果(代碼如下圖),為了提高交互體驗,使用了animate、setTimeout和slideUp\Down,并寫成一個函數(shù),便于多處調(diào)用,代碼主要意思就是先向下隱藏設(shè)置的欄目然后再向上顯示需要的欄目;

          // 底部操作欄和彈出框交互函數(shù)
          function animatePanel(fName, fHeight, sName, sHeight) {
              $(fName).slideUp(300);
              $('.footer').animate({ 'bottom': fHeight }, 300);
              var timer=setTimeout(function() {
                  $(sName).slideDown(500);
                  $('.footer').animate({ 'bottom': 0, 'height': sHeight }, 500);
                  timer=null;
              }, 0);
          }


          原理過程分析:

          1、涂鴉實現(xiàn)過程,簡單說就是記錄觸摸時的坐標(biāo)和滑動時的坐標(biāo),然后利用這兩個坐標(biāo)進行畫線,從而實現(xiàn)涂鴉效果,詳細分析如下:

          第一步,觸摸時候記錄觸摸時坐標(biāo)并轉(zhuǎn)換為(windowToCanvas函數(shù))canvas的坐標(biāo)(lastCoordinate),并且保存當(dāng)前的時間戳(lastTimestamp);

          第二步,滑動時記錄滑動到的坐標(biāo)并轉(zhuǎn)換為canvas坐標(biāo)(curCoordinate),并且保存當(dāng)前的時間戳(curTimestamp);再把lastCoordinate作為開始坐標(biāo),curCoordinate作為結(jié)束坐標(biāo)進行畫線(drawLine函數(shù)),并把curCoordinate賦值給lastCoordinate,curTimestamp賦值給lastTimestamp;所以滑動時候,都是起始點--第一點--第二點--...--最后結(jié)束的點,這樣兩點兩點畫線,從而產(chǎn)生滑動過程中的一條線,比較符合實際情況,直接計算起始點--結(jié)束點的線是不符合實際情況;最后為了符合慢的時候筆觸比較大,快的時候筆觸比較小,利用了函數(shù)curLineWidth進行即時計算,里面的數(shù)值可以自己根據(jù)實際情況調(diào)節(jié)。

          2、清屏功能比較簡單,原理就是點擊清屏按鈕(clearButton)時候,清除(clearRect)掉涂鴉畫布(ctx)和背景圖畫布(offCtx)的內(nèi)容;

          3、保存功能實現(xiàn)過程,簡單說就是把涂鴉畫布和背景圖畫布的內(nèi)容合成到另一個畫布,然后把該畫布內(nèi)容保存成圖片到本地,詳細分析如下(點擊保存按鈕(saveButton)時候):

          首先把涂鴉畫布和背景圖畫布的內(nèi)容轉(zhuǎn)換成圖片存儲到數(shù)組imgArray;

          然后把imgArray傳值給函數(shù)compositeGraph,該函數(shù)首先把數(shù)組imgArray內(nèi)容循環(huán)轉(zhuǎn)化成html中元素img的內(nèi)容(該內(nèi)容是隱藏的),然后循環(huán)該元素img輪流繪畫到離屏畫布上面,最后把該離屏畫布轉(zhuǎn)化成圖片并利用函數(shù)saveFile保存成圖片到本地。

          注意:

          圖片需要使用onload(val.onload),不然圖片未準(zhǔn)備就執(zhí)行,會顯示空白;

          轉(zhuǎn)換離屏畫布為圖片和執(zhí)行函數(shù)saveFile需要使用定時器,不然也會導(dǎo)致保存空白圖片。

          4、橡皮檫功能實現(xiàn)過程,簡單說就是獲取滑動過程中的坐標(biāo)點,然后利用clearRect清除坐標(biāo)點周圍的涂鴉內(nèi)容;詳細分析如下(紅色框部分):

          跟基本功能代碼加了flag區(qū)分,else部分為擦掉功能實現(xiàn)代碼;

          利用了畫布清除功能ctx.clearRect對滑動坐標(biāo)點周圍矩形進行清除,因為坐標(biāo)點是圓心,所以清除的起始坐標(biāo)(curCoordinate)需要滑動坐標(biāo)點減去半徑(rubberSize/2);

          直接用矩形擦除,過程會有鋸齒,為了達到更好效果,特意在上面加了圓形(ctx.arc)剪切(ctx.clip),使擦除效果比較光滑。

          5、歷史記錄功能實現(xiàn)過程,簡單說就是手指離開屏幕時候,把當(dāng)前畫布內(nèi)容轉(zhuǎn)化為圖片地址,然后新增元素img(src為該圖片地址)插入歷史記錄列表;詳細分析如下(手指離開屏幕時候(touchend)):

          把當(dāng)前畫布內(nèi)容轉(zhuǎn)化為圖片地址,然后新增元素img(src為該圖片地址)插入歷史記錄列表;

          當(dāng)操作次數(shù)多了之后,歷史記錄上的圖片會一直增加,為了讓超過一屛的圖片能正常滑動顯示,所以需要實時計算全部圖片的寬度(+間隔)的值boxWidth,然后賦值lineBox。

          6、實現(xiàn)可更換筆觸顏色和大小的功能,詳細代碼如下圖:

          查看代碼可看出滑動過程中會調(diào)用函數(shù)drawLine,且函數(shù)有參數(shù)curLineWidth(筆觸大小)和strokeColor(筆觸顏色),所以只需要選擇顏色和大小的時候,替換這兩個參數(shù)的值就可以實現(xiàn)功能,選擇對應(yīng)的值就是(sizeWidth(用于計算)和strokeColor);

          為了便于用戶知道選擇了什么顏色和那個大小,也實現(xiàn)了選擇回填;

          最后實現(xiàn)了點擊后關(guān)閉彈出框顯示操作欄。

          7、實現(xiàn)可更換背景圖功能,詳細代碼如下圖:

          實現(xiàn)原理就是利用點擊背景圖列表的圖片獲取src,然后使用canvas的drawImage功能,把圖片繪畫到offCtx(該畫布是處于涂鴉畫布的下面),從而實現(xiàn)更換背景圖功能;

          Tips:背景圖列表的圖片可改為縮略圖加上描述名稱有利于體驗;繪畫新背景之前一定要清除畫布(clearRect),不然性能會有問題。


          注意事項

          1、由于該實例是用于手機端,所以使用觸摸事件,如果要用于PC端,改為點擊事件即可,但要注意增加判斷點擊后才能涂鴉,不然會導(dǎo)致未點擊就能涂鴉;

          2、toDataURL有跨域問題,所以需要發(fā)布到服務(wù)器上,才能正常使用;


          主站蜘蛛池模板: 中文字幕在线无码一区| 国产一区麻豆剧传媒果冻精品| 一区二区免费视频| 国产午夜精品一区二区三区小说 | 国产精品一区二区四区| 日本一区二区三区不卡在线视频| 国产对白精品刺激一区二区| 国产精品久久一区二区三区| 国产小仙女视频一区二区三区| 国产乱码精品一区二区三区麻豆| 免费无码A片一区二三区| 一区二区三区在线观看中文字幕 | 亚洲一区视频在线播放| 一区二区福利视频| 久久精品一区二区| 国产福利一区二区三区视频在线| 日本中文字幕一区二区有码在线| 日韩免费视频一区二区| 无码AV中文一区二区三区| 国产91大片精品一区在线观看| 国产日本亚洲一区二区三区| 亚洲另类无码一区二区三区| 激情无码亚洲一区二区三区 | 国产福利91精品一区二区三区| 久久无码AV一区二区三区| 国产SUV精品一区二区88| 精品一区狼人国产在线| 日韩国产一区二区| 午夜肉伦伦影院久久精品免费看国产一区二区三区| 亚洲色无码专区一区| 亚洲国产精品一区二区第四页| 日本精品啪啪一区二区三区| 色欲AV无码一区二区三区| 亚洲国产美国国产综合一区二区 | 国产一区二区三区乱码网站| 高清一区二区三区日本久| 日本一区视频在线播放| 精品国产亚洲一区二区在线观看 | 免费一区二区三区| 一区二区三区福利视频免费观看| 国产日韩精品一区二区三区|