精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

圖形編輯器開發:實現縮放圖形

開發 前端
旋轉度數通常要配合一個變換中心(Origin),這個可以作為一個屬性讓用戶設置。但我更建議將 x、y、Width、Height 形成的 矩形的中點 作為旋轉中心,這樣更簡單一些,減少用戶的心智負擔,也防止出現用戶設置一些奇怪 Origin 的場景。

編輯器 github 地址:

https://github.com/F-star/suika

線上體驗:

https://blog.fstars.wang/app/suika/

圖形的屬性

圖形有幾個重要的基礎屬性,會經常被用到,我們在實現縮放圖形前需要理清一下它們。

  • x / y
  • width / height
  • rotation

位置和大小

x 和 y 為圖形的左上角位置,注意是旋轉前的。

x、y 旋轉后我們叫做 rotatedX、rotatedY,屬性面板中會用到。

width 和 height 為圖形的寬高,這個沒什么好說的。

另外,有些圖形有些特殊,它的 x、y、width、height 是要通過其他屬性計算出來的,比如貝塞爾曲線。

旋轉

rotation 為圖形的旋轉度數,通常使用 弧度單位。

因為弧度是數學計算中的常客,各種 API 都是要求提供弧度的,比如內置的 Math.sin() 方法。

你存角度自然也是可以,但不推薦,但計算時多了一層多余的單位轉換,且丟失一些微小的精度。

當然 UI 層還是要展示角度,因為是面向用戶的,對于數據和 UI 不統一的問題,在 UI 層做一個轉換即可。

旋轉度數通常要配合一個變換中心(origin),這個可以作為一個屬性讓用戶設置。

但我更建議將 x、y、width、height 形成的 矩形的中點 作為旋轉中心,這樣更簡單一些,減少用戶的心智負擔,也防止出現用戶設置一些奇怪 origin 的場景。

下圖中,紅色矩形是藍色矩陣順時針旋轉 45 度得到。

旋轉度數還要考慮 旋轉方向、基準角度、取值范圍 問題。

(因為弧度不直觀,后面會用角度來描述,但數據層依舊還是用的弧度)

  • 旋轉方向:設置旋轉后,圖形是會往順時針方向還是逆時針方向旋轉。
  • 基準角度:朝向哪里是 0 度。
  • 取值范圍:通常為 [0, 360) 和 (-180, 180]。二者其實等價,只是顯示有區別,后者其實只是前者減去 180 度。

通常這些編輯器自己決定就好。像我的項目,向上表示 0 度,順時針方向為旋轉方向,方向取值為 [0, 360)。

一些編輯器是支持用戶自己設置的,比如 AutoCAD 可通過圖形單位命令,設置旋轉方向和基準角度。

圖片

縮放實現思路

進入正題,對圖形進行縮放。

接下來會以通過右下角(也叫東南 se 方向) 縮放控制點縮放為例進行講解。

交互邏輯:

選擇工具下,當光標落在右下角的縮放控制點上時,光標會變成縮放樣式(這個不是本文核心,不講)。

此時按下鼠標,然后進行拖拽,即可對圖形以左上角為縮放中心,進行縮放。

實現思路:更新 width 和 height,然后確定參照點,修正 x  和 y。

按下鼠標時,我們要把當前圖形的 x、y、width、height、rotation 記錄下來。之后的縮放是基于這個初始狀態進行的。

const mousedown = (e) => {
  // ...
  
  // 縮放前圖形的屬性,之后我們會直接更新圖形屬性,導致原來的屬性丟失,所以要記錄下這個快照。
  prevElement = {
    x: item.x,
    y: item.y,
    width: item.width,
    height: item.height,
    rotation: item.rotation ?? 0,
  }
}

拖拽時,調用我們將要實現的 movePoint 方法,去更新這個圖形。

const drag = (e) = {
  // ...
  
  selectElement.movePoint(
    'se', // 縮放控制點類型:右下(或東南)
    lastPoint, // 當前光標位置(基于場景坐標系)
    prevElement, // 縮放前的屬性快照
  );
}

下面就是核心方法 movePoint 的實現邏輯了。

更新 width 和 height

首先是更新矩形寬高。

因為有一個旋轉,所以算法不會這么直觀。

我們要意識到這里有一個變換。看到的圖形,是做過變換(基于矩形中心旋轉)之后的,但我們需要修改的 width、height、x、y 則是旋轉前的。

所以我們需要把光標位置給旋轉回來,然后再減去 x 和 y 去得到真正的 width 和 height。

看看代碼

class Graph {
  // ...

  // 根據縮放點更新圖形
  movePoint(type, newPos, oldBox) {
    // 1. 計算 width 和 height
    // 計算縮放中心(也就是矩形的中點)
    const cx = oldBox.x + oldBox.width / 2;
    const cy = oldBox.y + oldBox.height / 2;

    // 計算反向旋轉的光標位置
    const { x: posX, y: poxY } = transformRotate(
      newPos.x,
      newPos.y,
      -(oldBox.rotation || 0), // 注意這里是負數
      cx,
      cy
    );
    
    let width = 0;
    let height = 0;
    if (type === 'se') {
      // 參照點為左上角(x 和 y)
      // 新的寬高自然就是光標位置減去 x、y
      width = posX - oldBox.x;
      height = poxY - oldBox.y;
    }
    // 其他控制點的邏輯暫且省略...
    
    // 2. 計算 x 和 y
    // ...
  }
}

看看只更新寬高的效果。

可以看到是有問題的,因為修改寬高后,矩形的中心點也發生了變化,導致縮放中心錯誤。所以我們要修正一下 x 和 y。

修正 x 和 y

接著我們就要修正 x 和 y 的值。

重點就一句話:縮放前的參考點和縮放后的參考點的位置要保持一致。這個參考點其實就是圖形縮放過程中的縮放中心。

對于右下角縮放控制點,它的縮放中心就是左上角,即 x 和 y 經過旋轉的位置。

class Graph {
  // ...

  movePoint(type, newPos, oldBox) {
    // 1. 計算 width 和 height
    // ...
    

    // 2. 計算 x 和 y

    // 設置參照點,不同縮放類型的參照點不同
    let prevOriginX = 0;
    let prevOriginY = 0;
    let originX = 0;
    let originY = 0;
    if (type === "se") {
      prevOriginX = oldBox.x;
      prevOriginY = oldBox.y;
      originX = oldBox.x;
      originY = oldBox.y;
    }
    // 其他縮放類型暫且省略

    // 縮放前的參考點位置
    const { x: prevRotatedOriginX, y: prevRotatedOriginY } = transformRotate(
      prevOriginX,
      prevOriginY,
      oldBox.rotation || 0,
      cx,
      cy
    );
    // 縮放后的參考點位置
    const { x: rotatedOriginX, y: rotatedOriginY } = transformRotate(
      originX,
      originY,
      oldBox.rotation || 0,
      oldBox.x + width / 2, // 旋轉中心是新的
      oldBox.y + height / 2
    );
    // 計算新舊兩個參考點的差值,對 x、y 進行補正
    const dx = rotatedOriginX - prevRotatedOriginX;
    const dy = rotatedOriginY - prevRotatedOriginY;
    const x = oldBox.x - dx;
    const y = oldBox.y - dy;
  }
}

width 和 height 可能為負數,這里要做一個標準化,然后賦值給圖形屬性即可。

this.setAttrs(
  normalizeRect({
    x,
    y,
    width,
    height,
  }),
);

其他縮放控制點

對于其他類型縮放控制點,比如左上、右上、左下縮放控制點,它們的大框架是一樣的,只是 width 和 height 計算方式不同,以及參考點不同。

不同類型下 width 和 height 的設置:

let width = 0;
let height = 0;
if (type === 'se') { // 右下
  width = posX - oldBox.x;
  height = poxY - oldBox.y;
} else if (type === 'ne') { // 右上
  width = posX - oldBox.x;
  height = oldBox.y + oldBox.height - poxY;
} else if (type === 'nw') {
  width = oldBox.x + oldBox.width - posX;
  height = oldBox.y + oldBox.height - poxY;
} else if (type === 'sw') {
  width = oldBox.x + oldBox.width - posX;
  height = poxY - oldBox.y;
}

新舊參考點設置:

let prevOriginX = 0;
let prevOriginY = 0;
let originX = 0;
let originY = 0;
if (type === 'se') {
  prevOriginX = oldBox.x; // 右下縮放點,參考點為左上角
  prevOriginY = oldBox.y;
  originX = oldBox.x;
  originY = oldBox.y;
} else if (type === 'ne') { // 右上縮放點,參考點為左下角
  prevOriginX = oldBox.x;
  prevOriginY = oldBox.y + oldBox.height;
  originX = oldBox.x;
  originY = oldBox.y + height;
} else if (type === 'nw') {
  prevOriginX = oldBox.x + oldBox.width;
  prevOriginY = oldBox.y + oldBox.height;
  originX = oldBox.x + width;
  originY = oldBox.y + height;
} else if (type === 'sw') {
  prevOriginX = oldBox.x + oldBox.width;
  prevOriginY = oldBox.y;
  originX = oldBox.x + width;
  originY = oldBox.y;
}

暫時沒實現正北、正南、正西、正東的邏輯,邏輯大差不差。

鎖定縮放比

按住 shift 可以鎖定縮放比。

做法是對比新舊圖形寬高比,將 width 和 height 其中一個進行修正即可。注意正負號。

方法需要多傳一個 keepRatio 的參數:

class Graph {
  // ...

  movePoint(type, newPos, oldBox, keepRatio = false) {
    // 1. 計算 width 和 height
    // ...
    
    if (keepRatio) {
      const ratio = oldBox.width / oldBox.height;
      const newRatio = Math.abs(width / height);
      if (newRatio > ratio) {
        height = (Math.sign(height) * Math.abs(width)) / ratio;
      } else {
        width = Math.sign(width) * Math.abs(height) * ratio;
      }
    }
    
    // 2. 計算 x 和 y
    // ...
  }
}

貌似沒考慮除數 height 為 0 的情況..

優化點

本文的實現是考慮的是比較簡單的縮放圖形場景,一些更復雜的場景并未實現。

縮放還有另一種策略,就是會產生 反向顛倒 的縮放。要實現這個效果,需要引入縮放屬性,復雜度會提升很多。

另外就是選中多個圖形,然后縮放的場景我沒實現。這種場景下,通常是要鎖定寬高比的。

否則就會出現圖形的斜切效果,這個如果要實現,我們還要引入斜切屬性,復雜度再一次提升。

下面是 Figma 的效果,真是讓人頭扁。

按住 Alt 實現圖形中心縮放也沒做,這個比較簡單,有空再做。

讀者如果看懂我這篇文章,心里應該有思路的:width、height 的計算要加入圖形中點參數,參照點設置為圖形中點。

責任編輯:姜華 來源: 前端西瓜哥
相關推薦

2023-09-26 07:39:21

2023-07-07 13:56:01

圖形編輯器畫布縮放

2024-01-03 08:43:17

圖形編輯器旋轉控制點縮放控制點

2023-09-07 08:24:35

圖形編輯器開發繪制圖形工具

2023-08-31 11:32:57

圖形編輯器contain

2023-04-07 08:02:30

圖形編輯器對齊功能

2023-02-01 09:21:59

圖形編輯器標尺

2024-01-08 08:30:05

光標圖形編輯器開發游標

2023-09-11 09:02:31

圖形編輯器模塊間的通信

2023-04-10 08:45:44

圖形編輯器排列移動功能

2023-07-31 08:46:07

圖形編輯器圖形自動對齊

2023-10-10 16:04:30

圖形編輯器格式轉換

2023-10-08 08:11:40

圖形編輯器快捷鍵操作

2023-08-28 08:10:50

Hex圖形編輯器

2023-02-09 07:02:30

圖形編輯器修改圖形

2023-02-06 16:59:57

Canvas編輯器

2023-01-18 08:30:40

圖形編輯器元素

2023-02-02 14:07:00

圖形編輯器Canvas

2023-10-20 08:02:25

圖形編輯器前端

2023-05-09 08:15:32

圖形編輯器撤銷重做功能
點贊
收藏

51CTO技術棧公眾號

久久av高潮av| 国产精品高清网站| 亚洲国产果冻传媒av在线观看| 97超碰免费在线| 91美女在线观看| 国产精品一区二区三区成人| 日韩欧美中文字幕视频| 麻豆国产欧美一区二区三区r| 日韩欧中文字幕| 中文字幕久久综合| 天堂中文网在线| 麻豆专区一区二区三区四区五区| 欧美激情中文字幕在线| 西西444www无码大胆| 日本一区二区三区播放| 色婷婷久久一区二区三区麻豆| 久久久一二三四| 午夜激情在线视频| 国产一级精品在线| 国产精品高潮呻吟久久av野狼 | 久久综合给合| 色天使久久综合网天天| 国产精品国三级国产av| 91免费在线| www.成人网.com| 91精品综合久久久久久五月天| 伊人手机在线视频| 欧美日韩亚洲一区| 最新91在线视频| 在线午夜精品自拍| 亚洲视频导航| 午夜在线视频免费| 成人污污视频在线观看| 国产欧美精品日韩精品| 亚洲第一网站在线观看| 一本久道久久综合婷婷鲸鱼| 欧美人与性动交| 精品在线观看一区| 日韩国产欧美| 在线日韩精品视频| 国外色69视频在线观看| 激情小说欧美色图| av国产精品| 日本乱人伦一区| 99视频在线免费播放| 色操视频在线| 亚洲综合清纯丝袜自拍| 国产日韩欧美大片| 免费观看在线午夜影视| 国产精品久久久久精k8| 亚洲国产精品一区二区第四页av| 日韩av资源| 久久伊人蜜桃av一区二区| 国产乱码一区| 肥臀熟女一区二区三区| 国产精品一区2区| 91在线观看欧美日韩| 一级做a爱片久久毛片| 美女一区二区三区| 成人国产精品一区二区| 91精品国产乱码久久久久| 麻豆成人91精品二区三区| 国产玖玖精品视频| 99在线精品视频免费观看20| 国产高清不卡一区| 国产精品theporn88| 天堂av一区二区三区| 91麻豆免费看| 欧美一区国产一区| 日本综合在线| 亚洲精品伦理在线| 成年人网站国产| 女海盗2成人h版中文字幕| 黑人巨大精品欧美一区免费视频| 久久婷婷五月综合色国产香蕉| 亚洲美女久久精品| 欧美日韩精品综合在线| 欧美国产日韩在线视频| 韩国精品福利一区二区三区| 国产视频精品va久久久久久| 国产综合精品在线| 性欧美欧美巨大69| 欧美激情亚洲国产| 亚洲 欧美 成人| 久久精品国产一区二区三| 亚洲一区久久久| 天天干天天操av| 国产婷婷色一区二区三区四区| 亚洲一区二区四区| av白虎一区| 欧美丝袜丝nylons| 中文字幕 欧美 日韩| 欧美日韩大片免费观看| 色综久久综合桃花网| 久久伊人成人网| 久久中文在线| 91精品国自产在线观看| 精品成人一区二区三区免费视频| 国产精品高潮呻吟| 99色这里只有精品| 2019年精品视频自拍| 日韩久久久精品| 日韩一级av毛片| 自拍偷拍欧美| 国产精品第8页| 丰满少妇被猛烈进入| 亚洲国产精华液网站w| 国产一区二区片| 韩国精品视频在线观看| 亚洲精品国产品国语在线| 成人18视频免费69| 美女网站久久| 粉嫩高清一区二区三区精品视频 | 欧美成人免费电影| 欧美一级久久久| 91精品久久久久久久久久久久| 亚洲黄色av| 亚洲一区二区三区乱码aⅴ| 九色在线观看| 亚洲成人精品在线观看| √天堂资源在线| 国产精品嫩模av在线| 欧美激情亚洲综合一区| 一区二区精品国产| 国产精彩视频在线观看| 蜜桃av一区二区| 久久久久久九九| 丝袜美腿av在线| 欧美美女网站色| 国产真实乱人偷精品人妻| 亚洲高清免费| 风间由美一区二区三区| 成人ww免费完整版在线观看| 欧美色国产精品| 亚洲a v网站| 久久精品午夜| 久久久国产精品一区二区三区| 在线中文字幕视频观看| 欧美日韩一区二区三区在线看| 国产成人精品无码免费看夜聊软件| 亚洲激情二区| 国产 高清 精品 在线 a| 八戒八戒神马在线电影| 51久久夜色精品国产麻豆| 国产大屁股喷水视频在线观看| 日韩av二区在线播放| 日本欧美精品久久久| 成人美女大片| 精品视频久久久| 97久久久久久久| 97精品超碰一区二区三区| www国产精品内射老熟女| 秋霞综合在线视频| 欧洲成人午夜免费大片| 免费一级在线观看| 在线免费观看日韩欧美| 欧美黄色高清视频| 蜜臀久久久99精品久久久久久| 日韩在线导航| 日韩综合av| 欧美成人手机在线| 亚洲国产精品久久久久爰性色| 夜夜嗨av一区二区三区四季av| av影片在线播放| 妖精视频成人观看www| 鲁片一区二区三区| 亚洲承认视频| 久久精品国产成人精品| 精品久久在线观看| 激情成人在线视频| 美国黑人一级大黄| 国产精品影视在线| 波多野结衣之无限发射| 精品freesex老太交| 国产精品一香蕉国产线看观看| 欧美jizzhd69巨大| 日韩精品一区二区三区在线观看 | 同心难改在线观看| 日本高清成人免费播放| 亚洲女人毛茸茸高潮| 国产盗摄一区二区三区| 春日野结衣av| 日韩成人综合| 国产私拍一区| 成人性片免费| 欧美激情久久久| 蝌蚪视频在线播放| 欧美一区二区三区在| 国产 欧美 日韩 在线| 国产精品色婷婷久久58| 一本色道久久hezyo无码| 日韩专区一卡二卡| 2018中文字幕第一页| 精品国产乱码久久久久久蜜坠欲下 | 日本一区二区不卡高清更新| 麻豆changesxxx国产| 亚洲va欧美va人人爽成人影院| 久久久久久久久久久网站| 国产在线视频网| 欧美成人一区二区三区| 亚洲高清视频免费观看| 亚洲精品视频在线| 亚洲第一综合网| 成人高清在线视频| 日本 片 成人 在线| 99精品久久久| 精品国产一区二区三区在线| 久草精品在线| 国产精品v欧美精品v日韩| 韩日精品一区| 69av在线播放| 亚洲丝袜精品| 色婷婷av一区二区三区久久| 污视频在线免费观看| 91精品国产综合久久久久久 | 中文精品在线观看| 国产精品一区二区x88av| 国产又大又黄又粗又爽| 国产情侣久久| 精品成在人线av无码免费看| 手机在线电影一区| 欧美性大战久久久久| 激情小说一区| av资源站久久亚洲| 精品亚洲a∨| 国产成人在线一区| 性欧美又大又长又硬| 国内揄拍国内精品少妇国语| 91小视频xxxx网站在线| 色七七影院综合| 国产精品久久一区二区三区不卡| 亚洲国产中文字幕在线观看| 性一交一乱一乱一视频| 欧美一区二区三区影视| 国产露脸无套对白在线播放| 欧美日韩精品三区| 中文在线字幕av| 在线观看成人小视频| 日本a级c片免费看三区| 色又黄又爽网站www久久| 国产性xxxx高清| 午夜av电影一区| 国产一级大片在线观看| 亚洲一区二区在线观看视频| 青青草原在线免费观看视频| 一区二区三区在线观看动漫| 极品久久久久久| 亚洲男人的天堂网| 中文字幕av免费在线观看| **欧美大码日韩| 免费中文字幕日韩| 亚洲精品视频一区| 精品一区二区三区四| 亚洲国产精品精华液网站| 日本一级淫片色费放| 午夜成人免费电影| 久久久蜜桃一区二区| 欧美综合天天夜夜久久| 在线观看av大片| 91精品国产综合久久精品麻豆| 一区二区的视频| 欧美一区二区三区系列电影| 精品二区在线观看| 亚洲国产日韩欧美在线动漫| 亚洲欧美综合一区二区| 亚洲色图50p| 无遮挡动作视频在线观看免费入口| 久久精品视频va| 欧美韩日亚洲| 欧美在线影院在线视频| 日本美女久久| 亚洲综合社区网| 精品综合久久88少妇激情| 欧美一区二区三区四区五区六区 | 欧美日韩卡一| 成人免费看黄网站| 亚洲精品一区在线| 久久久久久九九九九| 欧美最新另类人妖| 亚洲色婷婷久久精品av蜜桃| 亚洲久久一区| 美女黄色片视频| 国产精品一二三四五| 久久精品综合视频| 国产精品成人网| 日韩激情一区二区三区| 91精品办公室少妇高潮对白| 国产精品伊人久久 | 国产一级片在线| 久久亚洲春色中文字幕| 激情黄产视频在线免费观看| 国产精品偷伦免费视频观看的| 中文在线综合| 天天综合色天天综合色hd| 欧美日韩国产欧| 黄色国产小视频| 福利一区二区在线| 欧美亚洲色综久久精品国产| 亚洲一区二区视频| 做爰无遮挡三级| 亚洲第一av在线| 麻豆网站在线观看| 国产不卡在线观看| 粉嫩久久久久久久极品| 亚洲美女自拍偷拍| 首页综合国产亚洲丝袜| av天堂一区二区| 中文字幕中文字幕中文字幕亚洲无线| 91美女免费看| 日韩你懂的在线播放| 在线观看国产原创自拍视频| 91a在线视频| 欧美三级一区| 亚洲欧美精品在线观看| 99精品视频免费观看视频| 欧美日韩理论片| 国产日韩精品一区二区三区在线| 国产一级免费av| 欧美一级久久久久久久大片| 亚洲欧美视频一区二区| 国产999精品久久久| 国产精品一线| 91.com在线| 国产一本一道久久香蕉| 久艹在线观看视频| 欧美影视一区二区三区| 黄色软件在线| 青青a在线精品免费观看| 国产乱论精品| 妞干网在线观看视频| 国产91露脸合集magnet| 青娱乐在线视频免费观看| 7777精品久久久大香线蕉| 91最新在线| 国产精品免费在线免费 | 久久最新免费视频| 久久精品久久99精品久久| www色com| 在线观看视频一区二区| 欧美视频免费一区二区三区| 97香蕉久久超级碰碰高清版| 久久夜色精品国产噜噜av小说| 少妇一晚三次一区二区三区| 国产精品88888| 免费在线一区二区三区| 日韩精品一区二区三区蜜臀| av电影高清在线观看| 91精品国产99久久久久久红楼| 欧美阿v一级看视频| 免费黄色在线播放| 亚洲成人av免费| 亚洲 国产 欧美 日韩| 国产69久久精品成人| 亚洲天堂日韩在线| av五月天在线| 国产精品久久看| 国产三级伦理片| 欧美国产日本高清在线| 国产精品极品| 男人天堂999| 中文字幕av一区二区三区| 一本色道久久综合熟妇| 久久这里只有精品视频首页| 日韩欧美久久| 无码精品a∨在线观看中文| 2023国产一二三区日本精品2022| 午夜精品免费观看| 日韩在线小视频| 精品久久免费| 日韩欧美亚洲天堂| 日本一区二区视频在线| 国产又黄又粗又硬| 欧美激情综合色| 国产精品一区高清| 手机在线免费毛片| 亚洲成av人**亚洲成av**| 欧美亚洲日本| 成人欧美在线视频| 亚洲高清免费| 你懂得视频在线观看| 9191成人精品久久| 成人免费观看在线观看| 台湾成人av| 粉嫩aⅴ一区二区三区四区五区| 91在线视频在线观看| 日韩中文综合网| 精品综合久久88少妇激情| www.com黄色片| 亚洲午夜电影网| 成年女人的天堂在线| 99电影在线观看| 久久亚洲美女| 久久久久香蕉视频| 国产一区二区日韩精品欧美精品| 久久久久毛片免费观看| 久久久精品在线视频| 亚洲精品国产一区二区三区四区在线 | 国产一二三av| 亚洲精品二三区| 成人短视频软件网站大全app|