簡介
Adobe Photoshop有兩個非常專業(yè)的用來設(shè)置如投影、斜面與浮雕等特效的控件:一個是角度選擇器,另一個是角度與高度選擇器(如上圖所示)。
本文將帶領(lǐng)讀者創(chuàng)建兩個自定義控件,來模仿Photoshop中這兩個控件的外觀和行為。
基礎(chǔ)知識——數(shù)學(xué)
畢達(dá)哥拉斯定理
(即勾股定理,為尊重原文,以下簡稱畢氏定理。盡管有點繞口。——野比注)
利用畢氏定理,我們可以計算直角三角形的斜邊(最長邊)。計算公式為。這樣,斜邊c就等于
。
單位圓
鑒于接下來的工作和角度及圓有關(guān),我們先熟悉一下單位圓的形式是很有好處的。單位圓就是以(0,0)為圓心,半徑為1的圓。在常規(guī)網(wǎng)格(指畫布——野比注)中,0度(的坐標(biāo))從(1,0)這點(右)開始,按逆時針方向增大。因此,90度是(0,1),180度是(-1,0),270度是(0,-1),最后360度和0點重合。
三角函數(shù)
這里我們只需要知道三個基本的三角函數(shù):sin、cos和tan(正弦、余弦和正切——野比注)。如果我們還記得SOH-CAH-TOA(譯注+)的話,我們就知道,(直角)三角形的正弦等于對邊比上斜邊,余弦等于鄰邊比上斜邊,正切等于對邊比上鄰邊。
同樣,我們知道反三角函數(shù)用來計算未知角度。
譯注+:
SOH-CAH-TOA是老外用來記憶三角函數(shù)的口訣。其中:O為opposite(對邊),H為Hypotenuse(斜邊),A為Adjacent(鄰邊)。
SOH: Sine = Opposite ÷ Hypotenuse
CAH: Cosine = Adjacent ÷ Hypotenuse
TOA: Tangent = Opposite ÷ Adjacent
常用函數(shù)
我們制作的自定義控件都會用到下面這兩個重要的函數(shù)(方法):
一個函數(shù)接收角度和半徑作為參數(shù),返回圍繞某個原點的相應(yīng)點位置。(簡單來說,就是把角度轉(zhuǎn)換為點)
一個完成相反的功能,以點(X, Y)作為參數(shù),找到最匹配的角度。
第一個函數(shù)要簡單些:
private PointF DegreesToXY(float degrees, float radius, Point origin) { PointF xy = new PointF(); double radians = degrees * Math.PI / 180.0; xy.X = (float)Math.Cos(radians) * radius + origin.X; xy.Y = (float)Math.Sin(-radians) * radius + origin.Y; return xy; }
要注意的是首先我們需要把角度換算成弧度。一般來說,我們只需要在單位圓中進行研究:
該函數(shù)已知角度和半徑,利用三角函數(shù),我們算出X和Y值,然后在加上給定的原點初始坐標(biāo)即可。
還應(yīng)看到,函數(shù)代碼中用到的是Y分量的負(fù)值,這是因為計算機顯示器上網(wǎng)格是上下顛倒的(向下為正)。
第二個函數(shù)的功能是把用戶在控件上點擊的點位置轉(zhuǎn)換為相應(yīng)的角度值。這稍稍麻煩點,因為我們不得不考慮添加一些東西。限于文章篇幅,我這里貼出部分代碼:
private float XYToDegrees(Point xy, Point origin) { double angle = 0.0; if (xy.Y < origin.Y) { if (xy.X > origin.X) { angle = (double)(xy.X - origin.X) / (double)(origin.Y - xy.Y); angle = Math.Atan(angle); angle = 90.0 - angle * 180.0 / Math.PI; } else if (xy.X < origin.X) { //如此這般 } } else if (xy.Y > origin.Y) { //如此這般 } if (angle > 180) angle -= 360; //控制角度范圍 return (float)angle; }
該函數(shù)主要通過檢查鼠標(biāo)相對中心點的位置,確定其所在象限。一旦我們知道了象限,就可以利用三角函數(shù)(反正切)計算出角度。
如果角度大于180度,則減去360度。這樣就和Photoshop一樣,把角度控制在-180度和180度之間。當(dāng)然,這一步可以不做,不加這行代碼控件一樣能用。
制作控件
繪制控件
這兩個控件的背景相同:
用寬度為2的Pen繪制外圈圓
用40%(約100)不透明度的白色填充
控件中心是3x3像素的正方形
protected override void OnPaint(PaintEventArgs e) { //... //Draw g.SmoothingMode = SmoothingMode.AntiAlias; g.DrawEllipse(outline, drawRegion); g.FillEllipse(fill, drawRegion); //...光標(biāo) g.SmoothingMode = SmoothingMode.HighSpeed; g.FillRectangle(Brushes.Black, originSquare); //... }
注意SmoothMode屬性。在繪制圓圈時將該屬性設(shè)置為AntiAlias(抗鋸齒),這樣看起來既光滑又專業(yè)。但是如果畫正方形時也用抗鋸齒,就會顯得模糊難看,所以將SmoothMode設(shè)置為HighSpeed(高速),這樣畫出的正方形邊緣整齊犀利。
根據(jù)控件不同,光標(biāo)也有不同繪制方法。角度選擇器比較簡單,只需要從圓心到DegreesToXY函數(shù)返回的點連一條直線即可。角度與高度選擇器則是在這點上繪制一個1x1的矩形,然后在周圍繪制一個十字型光標(biāo)。
處理用戶點擊
多虧我們有了XYToDegrees函數(shù),處理用戶點擊變得特別簡單。為了讓我們的控件用起來和Photoshop一模一樣,我們需要設(shè)置MouseDown和MouseMove事件。這樣,各項數(shù)值將實時更新。這里是一個附注函數(shù)的代碼:
private int findNearestAngle(Point mouseXY) { int thisAngle = (int)XYToDegrees(mouseXY, origin); if (thisAngle != 0) return thisAngle; else return -1; }
高度控件需要額外的處理,就是找到中心點和鼠標(biāo)點擊點的距離:
private int findAltitude(Point mouseXY) { float distance = getDistance(mouseXY, origin); int alt = 90 - (int)(90.0f * (distance / origin.X)); if (alt < 0) alt = 0; return alt; }
在Photoshop中,選擇點(指鼠標(biāo)點擊點)在圓心時,高度為90,在邊緣處則為0。這樣,我們可以通過找到點擊點到圓心距離和半徑高度比值來計算出高度。然后,用90減去該值(實際上是按90到0來翻轉(zhuǎn)一下)。
自定義事件
為了讓我們的自定義控件更加專業(yè),需要控件能夠在數(shù)值發(fā)生變化時以編程方式進行提醒。這就是我們要設(shè)置事件的原因。
例如,像這樣給角度變化添加一個事件:
public delegate void AngleChangedDelegate(); public event AngleChangedDelegate AngleChanged;
然后,我們要做的就是每次變更Angle屬性時,調(diào)用AngleChanged()(需要先判斷是否為null)。
限制與改進
閃爍
沒有閃爍!只需要在制作控件時設(shè)置DoubleBuffered屬性為true,.NET Framework 2.0會處理剩下的工作,保證控件能流暢的重繪。
尺寸
因為控件使用基于半徑(圓)的數(shù)學(xué)計算方法,因此需要保證控件的長度和寬度相等。
顏色
我是照著Photoshop的樣子來的,所以并沒包含背景顏色、外圈顏色這些屬性。但是,瀏覽下代碼,你會發(fā)現(xiàn)改成你喜歡的顏色或者讓顏色可以動態(tài)修改并不是什么難事。
結(jié)論
我建議你下載項目文件(或者至少下載DEMO),這樣你可以看到這倆控件用起來很爽。
協(xié)議
本文及有關(guān)代碼、程序均基于CPOL(Codeproject Open License)協(xié)議。
更多Photoshop樣式的角度和高度選擇器控件 相關(guān)文章請關(guān)注PHP中文網(wǎng)!
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com