<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用canvas 實現(xiàn)矩形的移動(點、線、面)

          共 8805字,需瀏覽 18分鐘

           ·

          2021-07-07 10:27

          關(guān)注并將「趣談前端」設(shè)為星標(biāo)

          每早08:30按時推送技術(shù)干貨/優(yōu)秀開源/技術(shù)思維

          前言

          在canvas中實現(xiàn)圖片移動、實現(xiàn)矩形移動,大家可能看的很多了。但是我為什么還要去寫這樣的一篇文章呢,因為筆者曾經(jīng)做到3維圖形下的移動。包括移動一個立方體上的一條邊線、一個面、移動多邊形的一個點。最近一直在寫canvas的相關(guān)的文章,想著復(fù)習(xí)下,讀完本篇文章你可以學(xué)到,通過移動矩形的一個點, 一個條邊線,以及整個面的移動。本篇文章從淺到深,希望你耐心讀下去。

          面的移動

          試想一下,在canvas 下實現(xiàn)移動功能。第一步肯定創(chuàng)建canvas 并對canvas 添加move 事件, 這樣我們實時獲取到我們鼠標(biāo)的位置,然后我們只需要不斷的清除畫布,然后重新畫矩形。就OK了,面的移動初步現(xiàn)。我在下面寫點的移動會把這里重寫了, 這里先寫個快速簡易版本的。
          const canvas = document.getElementById('canvas');
          const ctx = canvas.getContext('2d');
          const width = 100;
          const height = 100;
          drawRect();
          function drawRect(x = 10,y = 10, scale = 1{
              ctx.clearRect( 001800800 );
              const halfwidth = width * scale / 2;
              const halfheight = height * scale / 2;
              ctx.strokeRect(x - halfwidth, y - halfheight,width * scale, height * scale)
          }
          let isMove =  true;
          canvas.addEventListener('mousemove',(e)=>{
              if(!isMove) {
                  return 
              }
              const x = e.clientX;
              const y = e.clientY;
              drawRect(x,y)
          })
          canvas.addEventListener('click',(e)=> {
              isMove = !isMove;
          });
          isMove變量就是個開關(guān),我們總不能一直移動吧,那也太累了,鼠標(biāo)點擊的就暫停。對于上文為什么要x - halfwidth, y - halfheight
          這里和大家解釋下:strokeRect 是從矩形的左上角開始畫的,但是呢我想把矩形放在鼠標(biāo)中心位置, 所以做一個寬度和高度相減就可以完美展示了。

          點的移動

          首先第一個問題就是?我們怎么知道我們選擇的是哪一個點呢,這里我做了一個簡單的判斷就是通過判斷鼠標(biāo)點擊的位置和矩形的每個點的位置作比較,看看哪一個離得近就作為目標(biāo)點。因為我們2d其實點的移動其實也就是找這個點關(guān)聯(lián)的線段, 所以我們只需要重新生成關(guān)聯(lián)的線段就好了,但是這里有一個比較難以處理的地方?就是移動一個半圓?如果我們移動半圓的斷點, 這里就涉及到圓弧的改變了,可能變成橢圓弧或者說用二階、三階貝塞爾曲線去表達(dá)。還有就是移動一個圖形和畫布上其他圖形形成了切割?是不是也要切割算法?其實可以用Clipper去求交并差,感興趣的同學(xué)可以自行去了解一下,但是這些不在本篇文章所想要闡述中。本篇文章所有的例子(只支持直線也就是LineSegment)。
          OK,我們第一步我們得去重新表達(dá)矩形,因為他不夠通用準(zhǔn)確的是重新表達(dá)四邊形, 矩形和正方形只是其中的特列。這里我給出原因?為什么呢一個很簡答的case,移動四邊形的一個點,他可能變成下面這樣:
          Xnip2021-06-22_23-02-28.png
          ok 為了下面好表達(dá)我們新建一個Point2d這個類, 將畫布上的每一個點都用一個實例去表示。
          class Point2d {
              constructor(x,y) {
                  this.x = x || 0;
                  this.y = y || 0;
              }
              clone() {
                  return this.constructor(this.x, this.y);
              }
              add(v) {
                  this.x += v.x;
                  this.y += v.y
                  return this;
              }
             random() {
                  this.x = Math.random() *1800;
                  this.y = Math.random() * 800;
                  return this
              }
          }
          接下來我們就隨便在畫布上移鼠標(biāo)的位置分別加上矩形的長度和寬度,畫出矩形。代碼如下:
          function drawFourPolygon(x, y ,width = 50, height = 50{
              ctx.clearRect( 001800800 );
              ctx.beginPath();
              ctx.moveTo(x- width /2, y - height/2)
              ctx.lineTo(x+ width / 2, y -height/2 )
              ctx.lineTo(x+ width / 2, y + height/2 )
              ctx.lineTo(x - width / 2, y + height/2 )
              ctx.closePath()
              ctx.stroke()
          }
          為了交互更加完美, 鼠標(biāo)第一次點擊確定移動的開始點, 然后 鼠標(biāo)不停地移動就是移動的終止點, 這樣就確定了一個向量。這里為了移動的時候更加明顯我增加了虛線功能,代碼如下
          function drawDashLine(start, end{
                if (!start || !end) {
                    return
                }
                ctx.strokeStyle = 'red';
                ctx.setLineDash( [510] );
                ctx.beginPath();  
                ctx.moveTo( start.x, start.y );
                ctx.lineTo( end.x, end.y );
                ctx.closePath()
                ctx.stroke();
            }
          這里用到的就是canvas setLineDash 這個api 參數(shù)的含義,實線的距離5、空白的距離10 如此往復(fù)的走下去形成虛線。start和end 就是鼠標(biāo)點擊確定的就是start 點, 然后鼠標(biāo)不停的移動就是end點。這里有一個小提醒就是我鼠標(biāo)移動的過程中先畫了虛線,然后又畫了矩形所以呢?矩形我們還是實線。我們這里對畫矩形代碼做了修改,還是把虛線還原過來。
          代碼如下:
           ctx.setLineDash([]); 
          是不是有點感覺了哈哈哈?從畫面上看這還是整體的移動不是點的移動, 由于我畫的圖形以鼠標(biāo)點擊的那個點去畫矩形的,我的下一篇文章會給大家介紹不規(guī)則多邊形點的移動,本篇文章我們還是假設(shè)我移動的是右上角的那個點。OK我們由移動的開始點和結(jié)束點, 可以得到一個移動的向量, 所以我們只要將要移動的點 和這個向量相加。這樣我們是不是實現(xiàn)了點的移動。
           const moveVec = end.clone().sub(start);
           const rightTop = new Point2d(x+ width / 2, y - height/2).clone().add(moveVec)
          這里我改變了右上角的點,但是呢有一個問題就是我們點擊也是走的同一個函數(shù),所以我們得加個開關(guān)去判斷下,主要是用來判讀是第一次點擊還是移動就好了代碼如下:
          ctx.lineTo(isSelect ? rightTop.x : x+ width / 2, isSelect ? rightTop.y : y height/2)

          // 看下click和move 事件 開關(guān)就是isSelect這個變量
          canvas.addEventListener('mousemove',(e)=>{
              if(!isMove) {
                  return 
              }
              const x = e.clientX;
              const y = e.clientY;
              clearRect();
              end = new Point2d(x,y);
              drawDashLine(start,end);
              drawFourPolygon(start)
          })
          canvas.addEventListener('click',(e)=> {
              // 這是一個每次清除畫布的函數(shù)
              clearRect()
              isMove = !isMove;
              const x = e.clientX;
              const y = e.clientY;
              start  = new Point2d(x,y);
              drawFourPolygon(start)
              isSelect = true;
          });
          效果圖如下:
          Jun-23-2021 23-00-57.gif
          哈哈哈是不是十分的絲滑和流暢, 發(fā)現(xiàn)canvas 的畫圖的性能還是非常不錯的。但是還是有一個問題就是,確定結(jié)果,看上面代碼我們確定結(jié)果是有問題的。所以我以按住alt鍵結(jié)束為確定結(jié)果這就十分完美了,代碼就不在這里展現(xiàn)了。

          線的移動

          有了點的移動,線的移動就顯示的十分簡單。線的移動其實就是對應(yīng)的點的移動。我們以右邊這條線為例子:代碼改寫如下:
          function drawFourPolygon( point, width = 50, height = 50{
              if(!point) {
                  return 
              }
              ctx.strokeStyle = 'black'
              ctx.setLineDash([]); 
              ctx.beginPath();
              const { x, y } = point;
              const moveVec = end.clone().sub(start);
              // 其實就是 右上和右下這兩個點同時移動
              const rightTop = new Point2d(x+ width / 2, y - height/2).clone().add(moveVec)
              const rightBottom = new Point2d(x+ width / 2, y + height/2).clone().add(moveVec)
              ctx.moveTo(x- width /2, y - height/2)
              ctx.lineTo(isSelect ? rightTop.x : x+ width / 2, isSelect ? rightTop.y : y - height/2)
              ctx.lineTo(isSelect ? rightBottom.x : x+ width / 2, isSelect ? rightBottom.y : y + height/2
              ctx.lineTo(x - width / 2, y + height/2 )
              ctx.closePath()
              ctx.stroke()
            }
          我們看下效果圖:
          Jun-23-2021 23-13-20.gif

          總結(jié)

          本篇文章主要介紹的2d圖形下最基本的變化移動,無論是線的移動還是面的移動最終都是點的移動。其實移動除了用向量表示還可以用矩陣, 或者說我們旋轉(zhuǎn)移動縮放等等命令都可以用矩陣變化表示。最后還是感謝大家看到最后,碼字不容易,如果看了對你有幫助的, 歡迎點個贊??和關(guān)注。你的支持是我持續(xù)更新好文章的最大動力。

          ?? 看完三件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          • 點個【在看】,或者分享轉(zhuǎn)發(fā),讓更多的人也能看到這篇內(nèi)容

          • 關(guān)注公眾號【趣談前端】,定期分享 工程化 可視化 / 低代碼 / 優(yōu)秀開源。



          Dooring可視化搭建平臺數(shù)據(jù)源設(shè)計剖析

          可視化搭建的一些思考和實踐

          基于Koa + React + TS從零開發(fā)全棧文檔編輯器(進(jìn)階實戰(zhàn))

          從零使用electron搭建桌面端Dooring


          點個在看你最好看

          瀏覽 84
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天综合综合大片 | 日韩三级乱伦 | 91射视频 | 久艹伊人| 日本A片电影免费观看电影大全 |