整合營銷服務商

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

          免費咨詢熱線:

          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鲁丝一区二区三区| 麻豆天美国产一区在线播放| 亚洲精品国产suv一区88| 国产亚洲福利精品一区二区| 国产一区二区精品尤物| 中文字幕日韩一区二区三区不卡| 亚洲国产精品无码第一区二区三区| 免费无码一区二区三区蜜桃大| 精品伦精品一区二区三区视频| 一区二区视频在线| 精品一区二区三区无码视频| 久久一区二区三区精品| 国产精品一区二区香蕉| 日本在线不卡一区| 秋霞日韩一区二区三区在线观看| 久久国产精品免费一区| 国产精品久久一区二区三区| 一区二区三区四区无限乱码 | 亚洲午夜一区二区电影院| 亚洲AV成人精品一区二区三区| 精品无码综合一区| 亚洲av无码一区二区三区人妖| 国产av福利一区二区三巨| 学生妹亚洲一区二区| 中文字幕Av一区乱码| 日本一区二区三区久久| 色偷偷一区二区无码视频| 午夜福利国产一区二区| 天堂一区人妻无码| 亚洲视频一区调教| 久久亚洲中文字幕精品一区四| 曰韩人妻无码一区二区三区综合部 | 一区二区三区视频在线| 一区二区三区在线视频播放| 国产微拍精品一区二区| 91一区二区在线观看精品| 国产av成人一区二区三区| 波多野结衣av高清一区二区三区|