整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          HTML5 Canvas坐標變換

          HTML5 Canvas坐標變換

          們在使用 Canvas 繪制圖形時,可能會想對繪制的圖形進行變換,例如讓圖形旋轉90度,或者讓圖像縮小放大等,這些效果都可以通過 Canvas API 的坐標軸變換處理功能來實現。

          圖形旋轉

          如果我們要想將圖形進行旋轉,例如下面這張圖片:


          可以通過 rotate() 方法來實現這個效果,rotate() 方法用于旋轉當前的繪圖,帶有一個參數 angle,表示旋轉角度。旋轉的中心點是坐標的原點,是以順時針方向進行旋轉,如果想要以逆時針方向來旋轉,可以將參數設置為負數。

          示例:

          例如上圖的實現代碼如下所示:

          <!DOCTYPE html>
              <html>
                  <head>
                      <meta charset="utf-8">
                      <title>HTML5學習(9xkd.com)</title>
                  </head>
                  <body>
                      <canvas id="mycanvas" width="250px" height="150px" style="border: 1px solid #000;"></canvas>
                      <script>
                          var can=document.getElementById("mycanvas");
                          var ctx=can.getContext("2d");
                          ctx.fillStyle="pink";  // 填充顏色
                          ctx.rotate(20*Math.PI/180);
                          ctx.fillRect(50, 30, 100, 50);
                      </script>
                  </body>
              </html>    
          

          圖形縮放

          圖形縮放可以使用 scale() 方法來實現,可以對圖形進行放大或縮小設置。注意,這個函數有兩個參數,第一個參數為水平方向的縮放倍數,第二個參數為垂直方向的縮放倍數。要將圖形縮小,可以將參數值設置為0到1之間的小數,例如 0.5 表示將圖形縮小一倍。或者也可以將參數值設置為百分數,例如 0.5=50%、1=100%、2=200% 等。

          示例:

          將圖形放大一倍:

          <!DOCTYPE html>
              <html>
                  <head>
                      <meta charset="utf-8">
                      <title>HTML5學習(9xkd.com)</title>
                  </head>
                  <body>
                      <canvas id="mycanvas" width="250px" height="150px" style="border: 1px solid #000;"></canvas>
                      <script>
                          var can=document.getElementById("mycanvas");
                          var ctx=can.getContext("2d");
                          ctx.fillStyle="pink";  // 填充顏色
                          ctx.fillRect(50, 30, 100, 50);
                          ctx.scale(1.5, 1.5);
                          ctx.fillRect(50, 30, 100, 50);
                      </script>
                  </body>
              </html>    
          

          在瀏覽器中的預覽效果:


          圖形平移

          移動圖形可以通過 translate() 方法來實現,這個方法中的第一個參數表示將坐標軸向右邊移動若干個單位,第二個參數表示將坐標軸圓點向下移動若干個單位。

          示例:

          將圖形水平移動50px,向下移動50px:

          <!DOCTYPE html>
              <html>
                  <head>
                      <meta charset="utf-8">
                      <title>HTML5學習(9xkd.com)</title>
                  </head>
                  <body>
                      <canvas id="mycanvas" width="250px" height="150px" style="border: 1px solid #000;"></canvas>
                      <script>
                          var can=document.getElementById("mycanvas");
                          var ctx=can.getContext("2d");
                          ctx.fillStyle="pink";  // 填充顏色
                          ctx.fillRect(50, 30, 100, 50);
                          ctx.translate(50, 50);
                          ctx.fillRect(50, 30, 100, 50);
                      </script>
                  </body>
              </html>    
          

          在瀏覽器中的預覽效果:


          小練習

          我們通過上述學習的三個方法,來實現下圖所示效果:


          實現代碼如下所示:

          <!DOCTYPE html>
              <html>
                  <head>
                      <meta charset="utf-8">
                      <title>HTML5學習(9xkd.com)</title>
                      <script>
                          window.onload=function() {
                          
                          var can=document.getElementById("mycanvas");
                          var ctx=can.getContext("2d");
          
                          ctx.translate(200, 10); 
                          ctx.fillStyle="rgba(219,112,147, 0.7)"; 
                          for(var i=0; i < 50; i++){ 
                              ctx.translate(27, 27); 
                              ctx.scale(0.95, 0.95); 
                              ctx.rotate(Math.PI / 10); 
                              ctx.fillRect(0, 0, 90, 50); 
                          }
                      }     
                      </script>
                  </head>
                  <body>
                      <canvas id="mycanvas" width="400" height="300" style="border: 1px solid #000;"></canvas>
          
                  </body>
              </html>    
          

          總結

          我們在繪制圖形的時候,經常會用到上述幾個方法來進行圖形變化,需要注意的是,這個變化的不是畫布,而是畫布上的畫進行變化。

          查看更多可以點擊鏈接:https://www.9xkd.com/

          家好,我是前端西瓜哥,今天來和大家說說 canvas 怎么做圖形拾取。

          圖形拾取,指的是用戶通過鼠標或手指在圖形界面上能選中圖形的能力。圖形拾取技術是之后的高亮圖形、拖拽圖形、點擊觸發事件的基礎。

          canvas 作為一個過于樸實無華的繪制工具,我們想知道如何讓 canvas 能像 HTML 一樣,知道鼠標點中了哪個 “div”。

          維護節點樹

          canvas 只提供 API 在畫布上繪制形狀,并不知道它之前畫過的圖形是什么,不會保存它們的坐標、寬高等信息。

          所以如果你想讓 canvas 支持將其中的圖形進行編輯,比如拖拽和放大,那就必須自己去維護一棵節點樹。

          類似這樣:

          const tree = {
            type: 'stage',
            children: [
              {
                type: 'rect',
                x: 10, y: 10, w: 100, h: 100,
                fill: 'red',
              },
              {
                type: 'circle',
                x: 0, y: 0, radius: 80,
                stroke: 'yellow',
              }
            ],
          };
          

          然后 canvas 基于此去按層級繪制這些圖形。

          下面我們看看元素拾取的幾種方案。

          方案 1:isPathInPoint

          isPointInPath 是 canvas 原生提供的一個檢測某個點是否在指定路徑內的方法。

          const canvas = document.querySelector("canvas");
          const ctx = canvas.getContext("2d");
          ctx.beginPath(); // 表示路徑的開始
          ctx.rect(30, 30, 100, 50);
          ctx.stroke(); // 如果只是計算,可以不繪制出來
          ctx.isPointInPath(40, 40); // true,在路徑內
          ctx.isPointInPath(10, 10); // false,不在路徑內
          

          線上 demo:

          https://codesandbox.io/s/h7pxsm

          優點:

          1. 原生 API 支持,方便;

          缺點:

          1. 判斷光標點中哪個元素,需要遍歷元素,去調用方法,直到返回 true 為止,性能可能會差一點(可以用四叉樹碰撞檢測,減少需要遍歷的元素數量,但極端情況可能還是會有很多元素,另外可通過包圍盒減少計算量);
          2. 檢測點是否在一條 strokeWidth 較大的線上可能會有錯誤,因為路徑是沒有寬度的;

          方案 2:緩存 Canvas

          根據真正的 canvas 元素,額外創建一個大小相同離屏的緩存 canvas 元素。

          每次我們在主 canvas 上繪制形狀時,也在緩存 canvas 上繪制同樣形狀的純色塊,并用哈希表記錄顏色和對應的圖形對象,比如紅色表示矩形 A,綠色表示矩形 B。

          然后當我們在真實 canvas 上點擊時,我們在 canvas 綁定事件,就可以拿到坐標位置 (x, y),再通過 offScreenCtx.getImageData(x, y, 1, 1) 方法得到緩存 canvas 的對應像素點的顏色值,然后找到它對應的圖形對象,執行其注冊的事件。

          Konva 庫使用了該方案。

          寫了個簡單的線上 demo,你可以嘗試點擊上面那個 canvas 下的圖形,看看控制臺輸出:

          https://codesandbox.io/s/veivt3

          優點:

          1. 能夠快速確定點所在的圖形;
          2. 能夠修改碰撞范圍,比如給一條細的線條進行區域的外擴,讓用戶更好選中這條線條;
          3. 適合圖形量大、重繪較少的場景。

          缺點:

          1. 渲染開銷加倍。每個圖形需要調用兩次 API(頁面上的 canvas 和緩存 canvas 各繪制一次);
          2. 如果圖形頻繁變化,性能會更低。

          方案 3:圖形學算法

          可以用計算機圖形學的算法,去判斷一個點是否在某個形狀內。

          比如:

          (1)點是否在矩形內。

          function isPointInRect(point, rect) {
            return (
              point.x >= rect.x &&
              point.y >= rect.y &&
              point.x <= rect.x + rect.width &&
              point.y <= rect.y + rect.height
            );
          }
          

          (2)點是否在圓形內。

          export function isPointInCircle(point, circle) {
            const dx = point.x - circle.x;
            const dy = point.y - circle.y;
            const dSquare = dx * dx + dy * dy;
            return dSquare <= circle.radius * circle.radius;
          }
          

          還有其他的:通過 “射線法” 判斷點是否在多邊形等。

          優點:

          1. 某種意義上是 isPointInPath 的底層實現,能做到平臺無關;

          缺點:

          1. 和 isPointInPath 方案一樣,需要遍歷圖形檢測;
          2. 實現復雜,簡單圖形還算簡單,但如果涉及到貝塞爾曲線等復雜形狀,實現就會很復雜且性能堪憂(可以考慮用 isPointInPath);
          3. 如果使用了 transform,因為要進行矩陣乘法,性能會有所下降。

          結尾

          總結一下,canvas 的圖形拾取有三種方案:

          1. isPointInPath:canvas 原生提供的 API,能夠知道點是否在路徑內;
          2. 緩存 Canvas:額外使用一個 canvas,每次繪制圖形都在這個 canvas 上繪制純色圖形,記錄映射關系。交互時通過 getImageData 得到顏色值,然后根據映射關系找到對應圖形;
          3. 計算機圖形學算法:自己寫點是否在特定形狀下的算法,本質是 isPointInPath 的底層實現。但復雜圖形碰撞檢測實現起來困難。

          我是前端西瓜哥,歡迎關注我,學習更多知識。

          .canvas-font居中圖解


          canvas


          主站蜘蛛池模板: 日韩人妻一区二区三区免费| 伊人久久大香线蕉AV一区二区 | 国产精品亚洲一区二区无码| 亚洲AV无码一区二区三区人 | 视频一区在线播放| 美女视频一区二区| 国产香蕉一区二区三区在线视频| 无码乱人伦一区二区亚洲| 国产精品制服丝袜一区| 日本韩国黄色一区二区三区 | 无码人妻久久一区二区三区免费丨| 国内精品一区二区三区在线观看| 成人免费视频一区二区| 久久亚洲综合色一区二区三区| 中文字幕一区二区精品区| 国产精品亚洲综合一区在线观看 | 国产福利精品一区二区| 亚洲日韩中文字幕无码一区| 亚洲熟女乱综合一区二区| 亚洲av乱码一区二区三区| 精品国产免费一区二区三区| 日韩一区二区视频在线观看| 全国精品一区二区在线观看| 日本片免费观看一区二区| 亚洲一区二区三区免费| 欧美激情国产精品视频一区二区| 一区二区三区视频免费观看 | 福利片免费一区二区三区| 日本一区二区不卡视频| 欧美亚洲精品一区二区| 一区二区三区视频在线| 动漫精品专区一区二区三区不卡| 亚洲综合无码一区二区三区 | 精品日产一区二区三区手机| 国产精品久久久久一区二区| 亚洲视频在线观看一区| 亚洲国产综合精品一区在线播放| 色婷婷av一区二区三区仙踪林| 国产在线一区二区在线视频| 国产在线一区二区三区在线| 国产激情视频一区二区三区|