圖形編輯器:圖形和輔助線繪制的坐標問題
大家好,我是前端西瓜哥。今天看看繪制圖形和輔助線時,坐標轉換的一些注意點。
項目地址,歡迎 star:
??https://github.com/F-star/suika??
線上體驗:
視口轉場景:
場景轉視口:
圖形的繪制
場景很大,但能畫的范圍其實就視口大小。所以,我們需要將使用了場景坐標的圖形的位置,轉換為視口坐標,再繪制。
有一樣非常低效的做法,就是生成一個非常大的 Canvas 畫布,將其中的圖形全部都畫出來,然后用一個 div 容器裝下,然后給 div 設置 overflow: scroll。然后調整一下 div 的 scrollLeft 和 scrollTop 就好。不推薦,效率很差。
對于圖形我們的做法是在繪制圖形前,先做矩陣變換,讓之后繪制的所有像素都自動做一個轉換,不用自己一個個手動轉換。
有的朋友看著前面的 sceneCoordsToViewportCoords 方法,有:
于是認為 ctx 的變換對應的寫法是這樣的:
這個寫法思路是對的,但細節有錯誤。因為 ctx.scale 的縮放中心因為前面的ctx.tranlate 從 (0, 0) 變成了 (-viewport.x, -viewport.y) 。
正確的寫法其實是縮放時調整一下縮放中心,縮放后再移回去,即:
然后你會發現,第一行和第二行抵消了,于是化簡得到:
輔助線的繪制
上面的效果,是無差別給之后繪制的所有圖形做縮放。也就是說,zoom 變大時,線寬(ctx.lineWidth)也會跟著變大。
圖形編輯器要繪制的除了圖形外,還有非常重要的一樣東西:輔助線。
(輔助線的坐標我們也是用場景坐標系的)
對于輔助線,我們希望 zoom 改變時,還能讓線寬保持原來的 1px,還有讓控制點的尺寸不變,如下圖效果:
縮放功能演示
解決方案是,我們自己算輔助線上的點在視口坐標的位置,不借助 ctx.scale 和 ctx.translate。
首先用 ctx.setTransform 將變換矩陣重置,將之前 ctx.scale 等造成的影響消除掉。
然后就是用前面寫好的 sceneCoordsToViewportCoords 方法轉換一下,得到視口坐標系下的位置,然后進行繪制即可。
其實就是局部做坐標系轉換,比如坐標會轉換、線寬不轉換。其實也有另一種思路,就是讓線寬除以 zoom,或尺寸除以 zoom,都可以。
結尾
場景坐標和視口坐標轉換,貫穿于整個編輯器項目,還是很重要的,要好好消化。