整合營銷服務商

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

          免費咨詢熱線:

          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


          主站蜘蛛池模板: 色一乱一伦一区一直爽| 一本大道东京热无码一区| 日韩一区二区在线免费观看| 亚洲一区二区三区国产精品无码| 久久综合九九亚洲一区| 久久精品免费一区二区| 成人h动漫精品一区二区无码| 国产在线第一区二区三区| 91一区二区三区| 亚洲一区二区影视| 日韩A无码AV一区二区三区| 精品亚洲一区二区| 糖心vlog精品一区二区三区| 波多野结衣电影区一区二区三区| 成人区精品一区二区不卡| 亚洲综合在线成人一区| 日本一区二区三区在线视频| 国产AV午夜精品一区二区入口| 一区二区传媒有限公司| 好湿好大硬得深一点动态图91精品福利一区二区| 久久久久久一区国产精品| 国产精品合集一区二区三区| 亚洲福利视频一区二区| 国产香蕉一区二区精品视频 | 中文字幕日本精品一区二区三区| 无码精品人妻一区二区三区人妻斩| 蜜桃无码一区二区三区| 无码人妻AⅤ一区二区三区水密桃| 亚洲国产一区在线| 亚洲AV成人精品日韩一区18p| 少妇激情一区二区三区视频| 日本人真淫视频一区二区三区| 亚洲电影唐人社一区二区| 国产精品亚洲不卡一区二区三区 | 亚洲中文字幕丝袜制服一区 | 精品一区二区无码AV| 无码人妻一区二区三区免费| 美女啪啪一区二区三区| 一区二区三区电影在线观看| 亚洲精品色播一区二区| 久久久精品人妻一区亚美研究所 |