日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

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

來源: 責編: 時間:2023-10-20 10:02:58 369觀看
導讀編輯器 github 地址:https://github.com/F-star/suika線上體驗:https://blog.fstars.wang/app/suika/圖形的屬性圖形有幾個重要的基礎屬性,會經常被用到,我們在實現縮放圖形前需要理清一下它們。x / ywidth / heightrotat

RT328資訊網——每日最新資訊28at.com

編輯器 github 地址:RT328資訊網——每日最新資訊28at.com

https://github.com/F-star/suikaRT328資訊網——每日最新資訊28at.com

線上體驗:RT328資訊網——每日最新資訊28at.com

https://blog.fstars.wang/app/suika/RT328資訊網——每日最新資訊28at.com

圖形的屬性

圖形有幾個重要的基礎屬性,會經常被用到,我們在實現縮放圖形前需要理清一下它們。RT328資訊網——每日最新資訊28at.com

  • x / y
  • width / height
  • rotation

位置和大小

x 和 y 為圖形的左上角位置,注意是旋轉前的。RT328資訊網——每日最新資訊28at.com

x、y 旋轉后我們叫做 rotatedX、rotatedY,屬性面板中會用到。RT328資訊網——每日最新資訊28at.com

width 和 height 為圖形的寬高,這個沒什么好說的。RT328資訊網——每日最新資訊28at.com

另外,有些圖形有些特殊,它的 x、y、width、height 是要通過其他屬性計算出來的,比如貝塞爾曲線。RT328資訊網——每日最新資訊28at.com

旋轉

rotation 為圖形的旋轉度數,通常使用 弧度單位。RT328資訊網——每日最新資訊28at.com

因為弧度是數學計算中的常客,各種 API 都是要求提供弧度的,比如內置的 Math.sin() 方法。RT328資訊網——每日最新資訊28at.com

你存角度自然也是可以,但不推薦,但計算時多了一層多余的單位轉換,且丟失一些微小的精度。RT328資訊網——每日最新資訊28at.com

當然 UI 層還是要展示角度,因為是面向用戶的,對于數據和 UI 不統一的問題,在 UI 層做一個轉換即可。RT328資訊網——每日最新資訊28at.com

旋轉度數通常要配合一個變換中心(origin),這個可以作為一個屬性讓用戶設置。RT328資訊網——每日最新資訊28at.com

但我更建議將 x、y、width、height 形成的 矩形的中點 作為旋轉中心,這樣更簡單一些,減少用戶的心智負擔,也防止出現用戶設置一些奇怪 origin 的場景。RT328資訊網——每日最新資訊28at.com

下圖中,紅色矩形是藍色矩陣順時針旋轉 45 度得到。RT328資訊網——每日最新資訊28at.com

RT328資訊網——每日最新資訊28at.com

旋轉度數還要考慮 旋轉方向、基準角度、取值范圍 問題。RT328資訊網——每日最新資訊28at.com

(因為弧度不直觀,后面會用角度來描述,但數據層依舊還是用的弧度)RT328資訊網——每日最新資訊28at.com

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

通常這些編輯器自己決定就好。像我的項目,向上表示 0 度,順時針方向為旋轉方向,方向取值為 [0, 360)。RT328資訊網——每日最新資訊28at.com

一些編輯器是支持用戶自己設置的,比如 AutoCAD 可通過圖形單位命令,設置旋轉方向和基準角度。RT328資訊網——每日最新資訊28at.com

圖片RT328資訊網——每日最新資訊28at.com

縮放實現思路

進入正題,對圖形進行縮放。RT328資訊網——每日最新資訊28at.com

接下來會以通過右下角(也叫東南 se 方向) 縮放控制點縮放為例進行講解。RT328資訊網——每日最新資訊28at.com

RT328資訊網——每日最新資訊28at.com

交互邏輯:RT328資訊網——每日最新資訊28at.com

選擇工具下,當光標落在右下角的縮放控制點上時,光標會變成縮放樣式(這個不是本文核心,不講)。RT328資訊網——每日最新資訊28at.com

此時按下鼠標,然后進行拖拽,即可對圖形以左上角為縮放中心,進行縮放。RT328資訊網——每日最新資訊28at.com

實現思路:更新 width 和 height,然后確定參照點,修正 x  和 y。RT328資訊網——每日最新資訊28at.com

按下鼠標時,我們要把當前圖形的 x、y、width、height、rotation 記錄下來。之后的縮放是基于這個初始狀態進行的。RT328資訊網——每日最新資訊28at.com

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

拖拽時,調用我們將要實現的 movePoint 方法,去更新這個圖形。RT328資訊網——每日最新資訊28at.com

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

下面就是核心方法 movePoint 的實現邏輯了。RT328資訊網——每日最新資訊28at.com

更新 width 和 height

首先是更新矩形寬高。RT328資訊網——每日最新資訊28at.com

因為有一個旋轉,所以算法不會這么直觀。RT328資訊網——每日最新資訊28at.com

我們要意識到這里有一個變換。看到的圖形,是做過變換(基于矩形中心旋轉)之后的,但我們需要修改的 width、height、x、y 則是旋轉前的。RT328資訊網——每日最新資訊28at.com

所以我們需要把光標位置給旋轉回來,然后再減去 x 和 y 去得到真正的 width 和 height。RT328資訊網——每日最新資訊28at.com

RT328資訊網——每日最新資訊28at.com

看看代碼RT328資訊網——每日最新資訊28at.com

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    // ...  }}

看看只更新寬高的效果。RT328資訊網——每日最新資訊28at.com

RT328資訊網——每日最新資訊28at.com

可以看到是有問題的,因為修改寬高后,矩形的中心點也發生了變化,導致縮放中心錯誤。所以我們要修正一下 x 和 y。RT328資訊網——每日最新資訊28at.com

修正 x 和 y

接著我們就要修正 x 和 y 的值。RT328資訊網——每日最新資訊28at.com

重點就一句話:縮放前的參考點和縮放后的參考點的位置要保持一致。這個參考點其實就是圖形縮放過程中的縮放中心。RT328資訊網——每日最新資訊28at.com

對于右下角縮放控制點,它的縮放中心就是左上角,即 x 和 y 經過旋轉的位置。RT328資訊網——每日最新資訊28at.com

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 可能為負數,這里要做一個標準化,然后賦值給圖形屬性即可。RT328資訊網——每日最新資訊28at.com

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

其他縮放控制點

對于其他類型縮放控制點,比如左上、右上、左下縮放控制點,它們的大框架是一樣的,只是 width 和 height 計算方式不同,以及參考點不同。RT328資訊網——每日最新資訊28at.com

不同類型下 width 和 height 的設置:RT328資訊網——每日最新資訊28at.com

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;}

新舊參考點設置:RT328資訊網——每日最新資訊28at.com

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;}

暫時沒實現正北、正南、正西、正東的邏輯,邏輯大差不差。RT328資訊網——每日最新資訊28at.com

鎖定縮放比

按住 shift 可以鎖定縮放比。RT328資訊網——每日最新資訊28at.com

做法是對比新舊圖形寬高比,將 width 和 height 其中一個進行修正即可。注意正負號。RT328資訊網——每日最新資訊28at.com

方法需要多傳一個 keepRatio 的參數:RT328資訊網——每日最新資訊28at.com

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 的情況..RT328資訊網——每日最新資訊28at.com

優化點

本文的實現是考慮的是比較簡單的縮放圖形場景,一些更復雜的場景并未實現。RT328資訊網——每日最新資訊28at.com

縮放還有另一種策略,就是會產生 反向顛倒 的縮放。要實現這個效果,需要引入縮放屬性,復雜度會提升很多。RT328資訊網——每日最新資訊28at.com

另外就是選中多個圖形,然后縮放的場景我沒實現。這種場景下,通常是要鎖定寬高比的。RT328資訊網——每日最新資訊28at.com

否則就會出現圖形的斜切效果,這個如果要實現,我們還要引入斜切屬性,復雜度再一次提升。RT328資訊網——每日最新資訊28at.com

下面是 Figma 的效果,真是讓人頭扁。RT328資訊網——每日最新資訊28at.com

RT328資訊網——每日最新資訊28at.com

按住 Alt 實現圖形中心縮放也沒做,這個比較簡單,有空再做。RT328資訊網——每日最新資訊28at.com

讀者如果看懂我這篇文章,心里應該有思路的:width、height 的計算要加入圖形中點參數,參照點設置為圖形中點。RT328資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-14344-0.html圖形編輯器開發:實現縮放圖形

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 深入理解 Netty FastThreadLocal

下一篇: 23種軟件設計模式綜述

標簽:
  • 熱門焦點
  • MIX Fold3包裝盒泄露 新機本月登場

    小米的全新折疊屏旗艦MIX Fold3將于本月發布,近日該機的真機包裝盒在網上泄露。從圖上來看,新的MIX Fold3包裝盒在外觀設計方面延續了之前的方案,變化不大,這也是目前小米旗艦
  • 盧偉冰長文解析K60至尊版 對Redmi有著里程碑式的意義

    在今天的Redmi后性能時代戰略發布會結束之后,Redmi總經理盧偉冰又帶來了一篇長文,詳解了為什么 Redmi 要開啟后性能時代?為什么選擇和 MediaTek、Pixelworks 深度合作?以及后性
  • 三言兩語說透設計模式的藝術-簡單工廠模式

    一、寫在前面工廠模式是最常見的一種創建型設計模式,通常說的工廠模式指的是工廠方法模式,是使用頻率最高的工廠模式。簡單工廠模式又稱為靜態工廠方法模式,不屬于GoF 23種設計
  • CSS單標簽實現轉轉logo

    轉轉品牌升級后更新了全新的Logo,今天我們用純CSS來實現轉轉的新Logo,為了有一定的挑戰性,這里我們只使用一個標簽實現,將最大化的使用CSS能力完成Logo的繪制與動畫效果。新logo
  • SpringBoot中使用Cache提升接口性能詳解

    環境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架從 3.1 開始,對 Spring 應用程序提供了透明式添加緩存的支持。和事務支持一樣,抽象緩存允許一致地使用各
  • 讓我們一起聊聊文件的操作

    文件【1】文件是什么?文件是保存數據的地方,是數據源的一種,比如大家經常使用的word文檔、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存數據,它既可以保
  • 每天一道面試題-CPU偽共享

    前言:了不起:又到了每天一到面試題的時候了!學弟,最近學習的怎么樣啊 了不起學弟:最近學習的還不錯,每天都在學習,每天都在進步! 了不起:那你最近學習的什么呢? 了不起學弟:最近在學習C
  • 引領旗艦級影像能力向中端機普及 OPPO K11 系列發布 1799 元起

    7月25日,OPPO正式發布K系列新品—— OPPO K11 。此次 K11 在中端手機市場長期被忽視的影像板塊發力,突破性地搭載索尼 IMX890 旗艦大底主攝,支持 OIS
  • AI藝術欣賞體驗會在上海梅賽德斯奔馳中心音樂俱樂部上演

    光影交錯的鏡像世界,虛實幻化的視覺奇觀,虛擬偶像與真人共同主持,這些場景都出現在2019世界人工智能大會的舞臺上。8月29日至31日,“AI藝術欣賞體驗會”在上海
Top 主站蜘蛛池模板: 邵武市| 沭阳县| 克山县| 乐亭县| 奉化市| 大关县| 洞口县| 湄潭县| 县级市| 和平县| 玉环县| 汶川县| 肥城市| 育儿| 遂平县| 积石山| 武汉市| 山丹县| 内丘县| 普兰店市| 汨罗市| 宾阳县| 金乡县| 离岛区| 峨边| 依兰县| 包头市| 红桥区| 中方县| 安化县| 呼玛县| 涿鹿县| 渑池县| 申扎县| 安西县| 霍林郭勒市| 南陵县| 韶山市| 包头市| 松溪县| 武宣县|