iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Software Development

從零打造網頁簡報編輯器系列 第 11

Day 11|調整大小時如何維持圖片比例

  • 分享至 

  • xImage
  •  

系列:一張投影片背後的工程:從零打造網頁簡報編輯器

對應程式版本:Git tag v0.1.0

版本說明:本文記錄第一版的單一右下角控制點;後續旋轉、翻轉與裁切仍沿用「預覽後提交」的互動方式。

拖曳移動與拖曳調整大小看起來很像,但資料規則不同。文字框可以獨立改寬和高;圖片如果也照做,人物或圖表很快就會被拉扁。第一版因此共用事件流程,卻把尺寸計算留給同一個可設定的純函式。

一個控制點,兩種尺寸規則

選取物件後,右下角會出現調整大小控制點。它和移動物件一樣:按下時記錄起點、移動時預覽、放開時才提交 element/set-frame。差別只在 resizeElement 是否收到 lockAspectRatio

物件 lockAspectRatio 結果
文字框 false 寬與高可各自改變
圖片 true 寬高維持原本比例

ImageElementView 明確傳入 lockAspectRatio: true;文字框不傳這個選項,採用預設的自由調整。事件處理、畫布邊界和最後提交動作則完全共用。

文字框只需要最小尺寸與畫布邊界

文字框的新寬高由原尺寸加上邏輯位移得到。函式會限制一般最小值為 80 × 50,也不允許右下角超出畫布。若物件已經貼近右下角,剩餘空間小於一般最小值時,就以畫布能容納的最大尺寸為準,確保資料仍然合法。

width = clamp(
  size.width + delta.x,
  minimumWidth,
  canvas.width - position.x,
)

這些限制放在純函式中,控制點拖曳與屬性面板輸入都可以共用,不必各寫一組邊界判斷。

圖片先選擇主要移動方向

等比例縮放時,水平與垂直位移可能同時存在。程式先比較兩個方向相對於原尺寸的變化量,較大的方向決定目標寬度:

const widthFromX = size.width + delta.x
const widthFromY = (size.height + delta.y) * aspectRatio
const targetWidth = Math.abs(delta.x / size.width) >=
  Math.abs(delta.y / size.height)
  ? widthFromX
  : widthFromY

取得目標寬度後,再以原比例算出高度。最大寬度同時受畫布剩餘寬度與剩餘高度限制;最小寬度也會依比例換算。這讓拖曳主要朝哪個方向,就以那個方向決定尺寸,不會因為另一個方向的小幅晃動而讓圖片跳動。

測試比例與邊界一起成立

純函式測試以 400 × 200 的圖片驗證:水平增加 200、垂直只增加 10 時,結果是 600 × 300,而不是 600 × 210。另一個案例把 100 × 50 圖片放在 (1400, 800),就算再往右下拖很遠,結果最多仍是 200 × 100,完整留在 1600 × 900 畫布內。

介面測試則確認文字框在 50% 顯示時拖曳控制點後,尺寸從 520 × 150 變為 620 × 200;輸入無效寬度不會破壞既有尺寸,復原也會回到原本的框。尺寸規則同時覆蓋純函式與實際 Pointer Event 路徑。

下一篇會處理另一種精確調整方式:不用拖曳,直接在屬性面板輸入位置、尺寸與文字樣式。

參考資料


上一篇
Day 10|讓拖曳在不同縮放比例下都正確
下一篇
Day 12|用屬性面板調整座標與文字樣式
系列文
從零打造網頁簡報編輯器13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言