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