PhotoShop中的自由变换UI实现

2 篇文章 0 订阅

PhotoShop中的自由变换UI

PS的自由变换UI如下
PS的自由变换

1. 旋转

在这里插入图片描述
如上图,旋转是在ps的自由变换中最常见的操作之一,灰色矩形经过旋转变为绿色矩形

1.1 数学建模求解

一个矩形的变换由以下参数记录左上角坐标(rectX, rectY),矩形宽高(rectW, rectH),矩形旋转角度(rectA,此处的旋转角是相对于B点的)。把图形旋转的变换进行数学建模,得到下面的图:
在这里插入图片描述
本节计算皆以B点作为被拖动点,即自由变换的左上角的点

其中:

  • BDEF为要变换的矩形基准,点A是对角线的交点,B(rectX, rectY),BD=rectW,BF=rectH,∠BAM=rectA
  • M(Xm, Ym)点是鼠标坐标(拖动B点围绕A点旋转rectA 度后的位置),连接AM交圆A于点C(A即旋转中心,C即旋转基准点)

转换为数学问题就是:

  • 由图,已知 B ( r e c t X , r e c t Y ) B(rectX, rectY) B(rectX,rectY) B D = r e c t W BD=rectW BD=rectW B F = r e c t H BF=rectH BF=rectH,$M(Xm, Ym) $。
  • 求rectA,即求: ∠ B A C ∠BAC BAC

1.2 求解过程

1.2.1 求A(Xa, Ya)

已知矩形顶点和宽高,求对角线交点

  • X a = r e c t X + r e c t W / 2 Xa = rectX + rectW / 2 Xa=rectX+rectW/2
  • Y a = r e c t Y + r e c t H / 2 Ya = rectY + rectH / 2 Ya=rectY+rectH/2
1.2.2 求rectA(∠BAC)

有三点ABC,求∠BAC,有公式:
c o s A = A B 2 + A B 2 − B C 2 2 ∗ A B ∗ A C cos A =\frac{AB^{2}+AB^{2}-BC^{2}}{2*AB*AC} cosA=2ABACAB2+AB2BC2

所以:

  • A B 2 AB^{2} AB2 = ( X a − r e c t X ) 2 (Xa - rectX)^{2} (XarectX)2 + ( Y a − r e c t Y ) 2 (Ya - rectY)^{2} (YarectY)2
  • A M 2 AM^{2} AM2 = ( X a − X m ) 2 (Xa - Xm)^{2} (XaXm)2 + ( Y a − Y m ) 2 (Ya - Ym)^{2} (YaYm)2
  • B M 2 BM^{2} BM2 = ( X m − r e c t X ) 2 (Xm - rectX)^{2} (XmrectX)2 + ( Y m − r e c t Y ) 2 (Ym - rectY)^{2} (YmrectY)2
  • c o s A = ( A B 2 + A B 2 − B M 2 ) / ( 2 ∗ A B 2 ∗ A M 2 ) cos A = (AB^{2}+AB^{2}-BM^{2})/(2*\sqrt{AB^{2}*AM^{2}}) cosA=(AB2+AB2BM2)/(2AB2AM2 )
  • r e c t A = a c o s ( c o s A ) ∗ 180 / π rectA = acos(cosA) * 180 / \pi rectA=acos(cosA)180/π
判断象限

因为cos无法判断计算出来的角是否大于180度,所以需要根据鼠标位置判断角度
这里写图片描述
如图, ∠ B A C 1 = 150 ° ∠BAC_{1} = 150° BAC1=150° ∠ B A C 2 = 210 ° ∠BAC_{2} = 210° BAC2=210°,但根据夹角公式算出来的cos是一样的,都是 3 / 2 \sqrt{3}/2 3 /2

判断的方式实际上也很简单,根据M的坐标判断M在直线AB上面还是下面即可

  • l A B : y = r e c t H r e c t W ( x − r e c t X ) + r e c t Y l_{AB}: y = \frac{rectH}{rectW}(x - rectX) + rectY lAB:y=rectWrectH(xrectX)+rectY
  • M ( X m , Y m ) M(Xm, Ym) M(Xm,Ym)代入方程即可
if ((rectH / rectW)*(Xm - rectX) + rectY < Ym) {
    rectA = 360 - rectA
}
1.2.3 求C(Xc, Yc)

( x , y ) (x, y) (x,y)围绕点 ( x 0 , y 0 ) (x_{0}, y_{0}) (x0,y0)旋转 α α α度,有公式:
x ′ = ( x − x 0 ) c o s α − ( y − y 0 ) s i n α + x 0 y ′ = ( x − x 0 ) s i n α + ( y − y 0 ) c o s α + y 0 x&#x27; = (x - x_{0})cos α - (y - y_{0})sin α + x_{0} \\ y&#x27; = (x - x_{0})sin α + (y - y_{0})cos α + y_{0} x=(xx0)cosα(yy0)sinα+x0y=(xx0)sinα+(yy0)cosα+y0

所以:

  • A = r e c t A ∗ π / 180 A = rectA * \pi / 180 A=rectAπ/180
  • X c = ( r e c t X − X a ) ∗ c o s A − ( r e c t Y − Y a ) ∗ s i n A + X a Xc = (rectX - Xa)*cos A - (rectY - Ya)*sin A + Xa Xc=(rectXXa)cosA(rectYYa)sinA+Xa
  • Y c = ( r e c t X − X a ) ∗ s i n A + ( r e c t Y − Y a ) ∗ c o s A + Y a Yc = (rectX - Xa)*sin A + (rectY - Ya)*cos A + Ya Yc=(rectXXa)sinA+(rectYYa)cosA+Ya

1.3 对于D、E、F点的处理

从数学建模图可轻易看出,D、E、F三点与B共圆,所以计算这三点旋转后的坐标,只需要在计算前把rectA加上对角线的夹角即可

B: A = r e c t A A = rectA A=rectA
D: A = r e c t A − 2 ∗ a t a n ( r e c t W / r e c t H ) ∗ π / 180 A = rectA - 2*atan(rectW/rectH) * \pi / 180 A=rectA2atan(rectW/rectH)π/180
E: A = r e c t A − 180 A = rectA - 180 A=rectA180
F: A = r e c t A − 180 − 2 ∗ a t a n ( r e c t W / r e c t H ) ∗ π / 180 A = rectA - 180 - 2*atan(rectW/rectH) * \pi / 180 A=rectA1802atan(rectW/rectH)π/180

1.4 代码

//鼠标拖拽旋转,计算图片旋转角度
function getAngle(mx, my) {
	var ox = rectX + rectW / 2
	var oy = rectY + rectH / 2
	var ax = rectX
	var ay = rectY
	var bx = mx
	var by = my

	// cosA = (AB^2+AC^2-BC^2)/(2*AB*AC) 三点求夹角
	var AB2 = (ax - ox) * (ax - ox) + (ay - oy) * (ay - oy)
	var AC2 = (ox - bx) * (ox - bx) + (oy - by) * (oy - by)
	var BC2 = (ax - bx) * (ax - bx) + (ay - by) * (ay - by)
	var cosA = (AB2 + AC2 - BC2) / (2 * Math.sqrt(AB2 * AC2))
	var A = Math.acos(cosA) * 180 / Math.PI

	// y = k(x-x0)+y0 判断在直线上面还是下面
	var k = rectW / rectH
	if ((bx - rectX) / k + rectY < by) {
		A = 360 - A
	}

	return A
}

//topLeft_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
	var A = getAngle(mouse_x, mouse_y)
	rectA = rectW>0? A: -A
}

//topRight_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
	var A = getAngle(mouse_x, mouse_y)
	var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI
	rectA = rectW>0? A - p: -A - p
}

//bottomLeft_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
	var A = getAngle(mouse_x, mouse_y)
	var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI + 180
	rectA = rectW>0? A - p: -A - p
}

//bottomRight_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
	var A = getAngle(mouse_x, mouse_y)
	A -= 180
	rectA = rectW>0? A: -A
}

2 对角缩放&等比缩放

在这里插入图片描述
在PS中,按住shift拖动四角的控制点,可以进行等比缩放,旋转角度不变

2.1 数学建模求解

在上一节中,我们已经求出了旋转角rectA( ∠ B A C ∠BAC BAC),以及旋转后的B点坐标$ C(Xc, Yc) $。现在根据可以鼠标坐标M,计算出拖移控制点P的坐标,然后算出缩放比例,从而算出缩放后的矩形基准(矩形B’D’E’F’为缩放后的矩形基准),及求B’坐标(rectX’, rectY’)及B’D’(rectW’)和B’F’(rectH’)
如图,紫色矩形是绿色矩形缩放后的图形,紫色矩形旋转-rectA度后即为矩形B’D’E’F’:
在这里插入图片描述

本节计算皆以B点作为锚点,对角点E点作为被拖动点,即缩放自由变换的右下角的点
转换为数学问题就是:

  • 由图,已知 B ( r e c t X , r e c t Y ) B(rectX, rectY) B(rectX,rectY) B D = r e c t W BD=rectW BD=rectW B F = r e c t H BF=rectH BF=rectH ∠ B A C = r e c t A ∠BAC=rectA BAC=rectA C ( X c , Y c ) C(Xc, Yc) C(Xc,Yc) M ( X m , Y m ) M(Xm, Ym) M(Xm,Ym)
  • 紫色矩形围绕A’逆时针旋转rectA度后即为矩形B’D’E’F’
  • 过M点作AC的垂线,垂足为P。作A’为线段PC的中点,缩放比例 t = A ′ C A C t=\frac{A&#x27;C}{AC} t=ACAC
  • B ′ ( r e c t X ′ , r e c t Y ′ ) B&#x27;(rectX&#x27;, rectY&#x27;) B(rectX,rectY) B ′ D ′ ( r e c t W ′ ) B&#x27;D&#x27;(rectW&#x27;) BD(rectW) B ′ F ′ ( r e c t H ′ ) B&#x27;F&#x27;(rectH&#x27;) BF(rectH)

2.2 求解过程

2.2.1 B’是直线BC上一点

由条件可知: ∠ B A C = ∠ B ′ A ′ C = r e c t A , A B = A C = 2 A ′ C = 2 A ′ B ∠BAC = ∠B&#x27;A&#x27;C=rectA,AB=AC=2A&#x27;C=2A&#x27;B BAC=BAC=rectAAB=AC=2AC=2AB
所以, △ A B C ∼ △ A ′ B ′ C \triangle ABC \sim \triangle A&#x27;B&#x27;C ABCABC
可得:B’是直线BC上一点

因此, t = A ′ C A C = B ′ C B C t= \frac{A&#x27;C}{AC} = \frac{B&#x27;C}{BC} t=ACAC=BCBC
易证: △ C B F ∼ △ C B ′ F ′ , △ C B D ∼ △ C B ′ D ′ \triangle CBF \sim \triangle CB&#x27;F&#x27;,\triangle CBD \sim \triangle CB&#x27;D&#x27; CBFCBFCBDCBD
可得: t = A ′ C A C = B ′ C B C = B ′ F B F = B ′ D B D t= \frac{A&#x27;C}{AC} = \frac{B&#x27;C}{BC} = \frac{B&#x27;F}{BF} = \frac{B&#x27;D}{BD} t=ACAC=BCBC=BFBF=BDBD

2.2.2 求P(Xp, Yp)

求直线 l 1 : y = k 1 x + b 1 l_{1}:y = k_{1}x+b_{1} l1:y=k1x+b1 与直线 l 2 : y = k 2 x + b 2 l_{2}:y = k_{2}x+b_{2} l2:y=k2x+b2 的交点 P ( X p , Y p ) P(Xp, Yp) P(Xp,Yp),有公式:
X p = b 2 − b 1 k 1 − k 2 Y p = k 1 X p + b 1 Xp = \frac{ b_{2} - b_{1}}{ k_{1}- k_{2}}\\ Yp = k_{1}Xp+b_{1} Xp=k1k2b2b1Yp=k1Xp+b1

所以:

  • k 1 = ( Y a − Y c ) / ( X a − X c ) k1 = (Ya - Yc) / (Xa - Xc) k1=(YaYc)/(XaXc)
  • b 1 = Y c − k 1 ∗ X c b1 = Yc - k1 * Xc b1=Yck1Xc
  • k 2 = − 1 / k 1 k2 = -1 / k1 k2=1/k1
  • b 2 = Y p − k 2 ∗ X p b2 = Yp - k2 * Xp b2=Ypk2Xp
  • X p = ( b 2 − b 1 ) / ( k 1 − k 2 ) Xp = (b2 - b1) / (k1 - k2) Xp=(b2b1)/(k1k2)
  • Y p = k 1 ∗ X p + b 1 Yp = k1 * Xp + b1 Yp=k1Xp+b1
2.2.3 计算缩放比例t

因为
t = C G C P = C P 2 ∗ A C = X p − X c 2 ∗ ( X a − X c ) t = \frac{CG}{CP} = \frac{CP}{2*AC} = \frac{Xp-Xc}{2*(Xa-Xc)} t=CPCG=2ACCP=2(XaXc)XpXc

  • t = ( X p − X c ) / ( X a − X c ) / 2 t = (Xp - Xc) / (Xa - Xc) / 2 t=(XpXc)/(XaXc)/2
2.2.4 计算结果

由2.2.1和2.2.3可知: $ t= \frac{A’C}{AC} = \frac{B’C}{BC} = \frac{B’F}{BF} = \frac{B’D}{BD} = \frac{CG}{CP}= \frac{Xp-Xc}{2*(Xa-Xc)}$
所以:
B ′ ( r e c t X ′ , r e c t Y ′ ) = t ∗ ( C − B ) + C B ′ D ′ = t ∗ B D B ′ F ′ = t ∗ B F B&#x27;(rectX&#x27;, rectY&#x27;) = t*(C-B) + C \\ B&#x27;D&#x27; = t*BD \\ B&#x27;F&#x27; = t*BF B(rectX,rectY)=t(CB)+CBD=tBDBF=tBF

  • r e c t X ′ = t ∗ ( r e c t X − X c ) + X c rectX&#x27; = t * (rectX - Xc) + Xc rectX=t(rectXXc)+Xc
  • r e c t Y ′ = t ∗ ( r e c t Y − Y c ) + Y c rectY&#x27; = t * (rectY - Yc) + Yc rectY=t(rectYYc)+Yc
  • r e c t W ′ = t ∗ r e c t W rectW&#x27; = t * rectW rectW=trectW
  • r e c t H ′ = t ∗ r e c t H rectH&#x27; = t * rectH rectH=trectH

2.3 对于其他三点的处理

和旋转一样,对其他三点进行角度偏转就好了

B: A = r e c t A + 180 A = rectA + 180 A=rectA+180
D: A = r e c t A + 180 + 2 ∗ a t a n ( r e c t W / r e c t H ) ∗ π / 180 A = rectA + 180 + 2*atan(rectW/rectH) * \pi / 180 A=rectA+180+2atan(rectW/rectH)π/180
E: A = r e c t A A = rectA A=rectA
F: A = r e c t A + 2 ∗ a t a n ( r e c t W / r e c t H ) ∗ π / 180 A = rectA + 2*atan(rectW/rectH) * \pi / 180 A=rectA+2atan(rectW/rectH)π/180

2.4 代码

//鼠标拖拽对角线缩放,计算图片缩放比例
function setScaleDiagonal(Xm, Ym, angle) {
	//计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
	var A = angle * Math.PI / 180
	var Xa = rectX + rectW / 2
	var Ya = rectY + rectH / 2
	var Xc = (rectX - Xa) * Math.cos(A) - (rectY - Ya) * Math.sin(A) + Xa
	var Yc = (rectX - Xa) * Math.sin(A) + (rectY - Ya) * Math.cos(A) + Ya

	//鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
	var k1 = (Ya - Yc) / (Xa - Xc)
	var b1 = Yc - k1 * Xc
	var k2 = -1 / k1
	var b2 = Ym - k2 * Xm
	var Xp = (b2 - b1) / (k1 - k2)
	var Yp = k1 * Xp + b1

	//计算缩放比例
	var t = (Xp - Xc) / (Xa - Xc) / 2
	//console.log("setScaleDiagonal", Xc, Yc, Xp ,Yp, A, t)

	//应用到变换
	rectX = t * (rectX - Xc) + Xc
	rectY = t * (rectY - Yc) + Yc
	rectW = t * rectW
	rectH = t * rectH
}


//topLeft_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
	setScaleDiagonal(mouse_x, mouse_y, rectA + 180)
}

//topRight_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
	var A = 180 + rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
	setScaleDiagonal(mouse_x, mouse_y, A)
}

//bottomLeft_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
	var A = rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
	setScaleDiagonal(mouse_x, mouse_y, A)
}

//bottomRight_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
	setScaleDiagonal(mouse_x, mouse_y, rectA)
}

3 上下左右的缩放&不等比缩放

在这里插入图片描述
在PS中,按住shift拖动上下左右四边的控制点,可以进行不等比缩放,旋转角度不变

3.1 数学建模求解

在第一节中,我们已经求出了旋转角rectA( ∠ B A C ∠BAC BAC),以及旋转后的B点坐标$ C(Xc, Yc) $。现在根据可以鼠标坐标M,计算出拖移控制点P的坐标,然后算出缩放比例t,从而算出缩放后的矩形基准(矩形B’D’E’F’为缩放后的矩形基准),及求B’坐标(rectX’, rectY’)及B’D’(rectW’)和B’F’(rectH’)
如图,紫色矩形是绿色矩形缩放后的图形,紫色矩形围绕A’旋转-rectA度后即为矩形B’D’E’F’:
在这里插入图片描述

本节计算皆以L点作为锚点,对称点L’点作为被拖动点,即缩放自由变换的右边的点
转换为数学问题就是:

  • 由图,已知 B ( r e c t X , r e c t Y ) B(rectX, rectY) B(rectX,rectY) B D = r e c t W BD=rectW BD=rectW B F = r e c t H BF=rectH BF=rectH ∠ B A C = r e c t A ∠BAC=rectA BAC=rectA C ( X c , Y c ) C(Xc, Yc) C(Xc,Yc) M ( X m , Y m ) M(Xm, Ym) M(Xm,Ym)
  • 紫色矩形围绕A’逆时针旋转rectA度后即为矩形B’D’E’F’
  • 作CG中点L,过AL作直线交JK于L’
  • 过M点作AL的垂线,垂足为P。作A’为线段PL的中点,缩放比例 t = L P L L ′ t=\frac{LP}{LL&#x27;} t=LLLP
  • 作直线BG、CD相交于H点,过点H作HI⊥BD,垂足为I,交B’D’于I’,连接FI’
  • B ′ ( r e c t X ′ , r e c t Y ′ ) B&#x27;(rectX&#x27;, rectY&#x27;) B(rectX,rectY) B ′ D ′ ( r e c t W ′ ) B&#x27;D&#x27;(rectW&#x27;) BD(rectW) B ′ F ′ ( r e c t H ′ ) B&#x27;F&#x27;(rectH&#x27;) BF(rectH)

3.2 求解过程

3.2.1 B’是直线BH上一点

证明略(不知道怎么证,总之是对的)

所以, t = L P L L ′ = B ′ D ′ B D = B ′ F ′ B F t = \frac{LP}{LL&#x27;} = \frac{B&#x27;D&#x27;}{BD} = \frac{B&#x27;F&#x27;}{BF} t=LLLP=BDBD=BFBF

3.2.2 L是直线HI上一点,求L(Xl, Yl)

证明略(不知道怎么证,总之是对的)

点L相当于BF的中点R围绕A旋转rectA度后的位置
所以
$ Xr = rectX - Xa \
Yr = Ya \
Xl = Xa + (Xr - Xa) * Math.cos(A) - (Yr - Ya)*sin A \
Yl = Ya + (Xr - Xa) * Math.sin(A) + (Yr - Ya)*cos A
$

化简后:

  • X l = X a + ( r e c t X − X a ) ∗ M a t h . c o s ( A ) Xl = Xa + (rectX - Xa) * Math.cos(A) Xl=Xa+(rectXXa)Math.cos(A)
  • Y l = Y a + ( r e c t X − X a ) ∗ M a t h . s i n ( A ) Yl = Ya + (rectX - Xa) * Math.sin(A) Yl=Ya+(rectXXa)Math.sin(A)
3.2.3 计算缩放比例t

参见2.2.2和2.2.3
求得 P ( X p , Y p ) P(Xp, Yp) P(Xp,Yp) t t t

特别的:
当Ya=Yl时(rect=0或rect=180)
t = ( X m − X l ) / ( X a − X l ) / 2 t = (Xm - Xl) / (Xa - Xl) /2 t=(XmXl)/(XaXl)/2

当Xa=Xl时(rect=90或rect=270)
t = ( Y m − Y l ) / ( Y a − Y l ) / 2 t = (Ym - Yl) / (Ya - Yl) /2 t=(YmYl)/(YaYl)/2

3.2.4 计算结果

计算BI的长: B I = X l − X b = X l − r e c t X BI = Xl - Xb = Xl - rectX BI=XlXb=XlrectX
计算HI的长: H I = B I ∗ t a n ( ∠ B H I ) = B I ∗ t a n ( π − r e c t A 2 ) HI = BI * tan(∠BHI) = BI * tan(\frac{\pi - rectA}{2}) HI=BItan(BHI)=BItan(2πrectA)

所以:

  • r e c t X ′ = r e c t X + ( 1 − t ) ∗ B I rectX&#x27; = rectX + (1 - t) * BI rectX=rectX+(1t)BI
  • r e c t Y ′ = r e c t Y − ( 1 − t ) ∗ H I rectY&#x27; = rectY - (1 - t) * HI rectY=rectY(1t)HI
  • r e c t W ′ = r e c t W ∗ t rectW&#x27; = rectW * t rectW=rectWt
  • r e c t H ′ = r e c t H rectH&#x27; = rectH rectH=rectH

3.3 左缩放、上缩放和下缩放

如下图,计算方法是一样的,但是基准点有变化,但都是为了计算BI和HI的长度
在这里插入图片描述
在这里插入图片描述

3.4 代码


var direction_left = 0
var direction_right = 1
var direction_top = 2
var direction_bottom = 3

//鼠标拖拽水平缩放,计算图片缩放比例
function setScaleXY(Xm, Ym, angle, direction) {
	//计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
	var A = angle * Math.PI / 180
	var Xa = rectX + rectW / 2
	var Ya = rectY + rectH / 2

	//缩放基准点基准点
	var Xl = 0
	var Yl = 0

	if(direction === direction_left)
	{
		Xl = Xa + (rectX + rectW - Xa) * Math.cos(A)
		Yl = Ya + (rectX + rectW - Xa) * Math.sin(A)
	}
	else if(direction === direction_right)
	{
		Xl = Xa + (rectX - Xa) * Math.cos(A)
		Yl = Ya + (rectX - Xa) * Math.sin(A)
	}
	else if(direction === direction_top)
	{
		Xl = Xa - (rectY + rectH - Ya) * Math.sin(A)
		Yl = Ya + (rectY + rectH - Ya) * Math.cos(A)
	}
	else if(direction === direction_bottom)
	{
		Xl = Xa - (rectY - Ya) * Math.sin(A)
		Yl = Ya + (rectY - Ya) * Math.cos(A)
	}

	//计算缩放比例
	var t = 0
	if(Xa != Xl && Ya != Yl)
	{
		//鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
		var k1 = (Ya - Yl) / (Xa - Xl)
		var b1 = Yl - k1 * Xl
		var k2 = -1 / k1
		var b2 = Ym - k2 * Xm
		var Xp = (b2 - b1) / (k1 - k2)
		//var Yp = k1 * Xp + b1

		if(Xp === Xl || Xa === Xl) return
		t = (Xp - Xl) / (Xa - Xl) /2
	}
	else if(Ya === Yl)
	{
		if(Xm === Xl || Xa === Xl) return
		t = (Xm - Xl) / (Xa - Xl) /2
	}
	else if(Xa === Xl)
	{
		if(Ym === Yl || Ya === Yl) return
		t = (Ym - Yl) / (Ya - Yl) /2
	}

	//应用变换
	var BI = 0
	if(direction === direction_left)
	{
		if(Ya === Yl)
		{
			rectX = rectX + (1 - t) * rectW
			rectW = t * rectW
		}
		else
		{
			BI = (1 - t) * (Xl - rectX)
			rectY = rectY + BI * Math.tan(A / 2)
			rectX = rectX + BI
			rectW = t * rectW
		}
	}
	else if(direction === direction_right)
	{
		if(Ya === Yl)
		{
			rectW = t * rectW
		}
		else
		{
			BI = (1 - t) * (Xl - rectX)
			rectY = rectY - BI * Math.tan((Math.PI - A) / 2)
			rectX = rectX + BI
			rectW = t * rectW
		}
	}
	else if(direction === direction_top)
	{
		if(Xa === Xl)
		{
			rectY = rectY + (1 - t) * rectH
			rectH = t * rectH
		}
		else
		{
			BI = (1 - t) * (Yl - rectY)
			rectX = rectX - BI * Math.tan(A / 2)
			rectY = rectY + BI
			rectH = t * rectH
		}
	}
	else if(direction === direction_bottom)
	{
		if(Xa === Xl)
		{
			rectH = t * rectH
		}
		else
		{
			BI = (1 - t) * (Yl - rectY)
			rectX = rectX + BI * Math.tan((Math.PI - A) / 2)
			rectY = rectY + BI
			rectH = t * rectH
		}
	}
}

//四个方向共用,只需改变第三个参数即可
onMouseMove(mouse_x, mouse_y)
{
	setScaleXY(mouse_x, mouse_y, rectA, direction_left)
}

4 完整实现代码

4.1 效果预览

在这里插入图片描述

4.2 代码

这是在QT qml界面实现的代码

import QtQuick 2.1


Item {
    id: item
    anchors.fill: parent
    rotation: 0

    property real widthScale: 1.0
    property real heightScale: 1.0
    property real aspectRatio: 0.0
    property int handleSize: 10
    property int borderSize: 2
    property alias uiRectangle: uiRectangle
    property color uiColor: Qt.rgba(255, 255, 255, 1)
    property color handleColor: 'transparent'
    //property color handleColor: Qt.rgba(0, 255, 0, 0.5)

    property real rectX: 0
    property real rectY: 0
    property real rectW: 0
    property real rectH: 0
    property real rectA: 0
    property int coursor: 0
    property real mouse_x: 0
    property real mouse_y: 0

    property int direction_left: 0
    property int direction_right: 1
    property int direction_top: 2
    property int direction_bottom: 3

    property int state_none: 0
    property int state_drag: 1
    property int state_control_point: 2
    property int state_rotate: 3


    signal rectChanged()
    signal updateCursor()

    function image(path) {
        return "file:///" + RESOURCE_PATH + "/icon/" + path
    }

    function transformPointX(x, y, sA, cA) {
        return x * cA - y * sA + rectX + rectW / 2
    }

    function transformPointY(x, y, sA, cA) {
        return x * sA + y * cA + rectY + rectH / 2
    }

    //更新UI
    function updateRect() {
        if(rectW > 0){uiRectangle.x = rectX; uiRectangle.width = rectW}
        else{uiRectangle.x = rectX + rectW; uiRectangle.width = -rectW}

        if(rectH > 0){uiRectangle.y = rectY; uiRectangle.height = rectH}
        else{uiRectangle.y = rectY + rectH; uiRectangle.height = -rectH}

        uiRectangle.rotation = rectA

        //rectChanged() //发送信号通知上层更新滤镜参数
    }

    //更新控制点坐标
    function updateHandle() {
        var cx = rectW / 2
        var cy = rectH / 2

        var hs = handleSize
        var hs2 = handleSize / 2
        var xhs = handleSize
        var xhs2 = handleSize / 2
        var yhs = handleSize
        var yhs2 = handleSize / 2

        var A = rectA * Math.PI / 180
        var sA = Math.sin(A)
        var cA = Math.cos(A)

        if(rectW < 0){xhs = -xhs; xhs2 = -xhs2}
        if(rectH < 0){yhs = -yhs; yhs2 = -yhs2}

        topLeft_RotateHandle.x = transformPointX(-cx - xhs, -cy - yhs, sA, cA) - hs
        topLeft_RotateHandle.y = transformPointY(-cx - xhs, -cy - yhs, sA, cA) - hs
        topRight_RotateHandle.x = transformPointX(cx + xhs, -cy - yhs, sA, cA) - hs
        topRight_RotateHandle.y = transformPointY(cx + xhs, -cy - yhs, sA, cA) - hs
        bottomLeft_RotateHandle.x = transformPointX(-cx - xhs, cy + yhs, sA, cA) - hs
        bottomLeft_RotateHandle.y = transformPointY(-cx - xhs, cy + yhs, sA, cA) - hs
        bottomRight_RotateHandle.x = transformPointX(cx + xhs, cy + yhs, sA, cA) - hs
        bottomRight_RotateHandle.y = transformPointY(cx + xhs, cy + yhs, sA, cA) - hs

        topLeft_ScaleHandle.x = transformPointX(-cx + xhs2, -cy + yhs2, sA, cA) - hs2
        topLeft_ScaleHandle.y = transformPointY(-cx + xhs2, -cy + yhs2, sA, cA) - hs2
        topRight_ScaleHandle.x = transformPointX(cx - xhs2, -cy + yhs2, sA, cA) - hs2
        topRight_ScaleHandle.y = transformPointY(cx - xhs2, -cy + yhs2, sA, cA) - hs2
        bottomLeft_ScaleHandle.x = transformPointX(-cx + xhs2, cy - yhs2, sA, cA) - hs2
        bottomLeft_ScaleHandle.y = transformPointY(-cx + xhs2, cy - yhs2, sA, cA) - hs2
        bottomRight_ScaleHandle.x = transformPointX(cx - xhs2, cy - yhs2, sA, cA) - hs2
        bottomRight_ScaleHandle.y = transformPointY(cx - xhs2, cy - yhs2, sA, cA) - hs2

        left_ScaleHandle.x = transformPointX(-cx + xhs2, 0, sA, cA) - hs2
        left_ScaleHandle.y = transformPointY(-cx + xhs2, 0, sA, cA) - hs2
        right_ScaleHandle.x = transformPointX(cx - xhs2, 0, sA, cA) - hs2
        right_ScaleHandle.y = transformPointY(cx - xhs2, 0, sA, cA) - hs2
        top_ScaleHandle.x = transformPointX(0, -cy + yhs2, sA, cA) - hs2
        top_ScaleHandle.y = transformPointY(0, -cy + yhs2, sA, cA) - hs2
        bottom_ScaleHandle.x = transformPointX(0, cy - yhs2, sA, cA) - hs2
        bottom_ScaleHandle.y = transformPointY(0, cy - yhs2, sA, cA) - hs2

        rectChanged() //发送信号通知上层更新滤镜参数
    }

    //初始化数据
    function setHandles(rect, angle) {
        rectX = Math.round(rect.x * widthScale)
        rectY = Math.round(rect.y * heightScale)
        rectW = Math.round(rect.width * widthScale)
        rectH = Math.round(rect.height * heightScale)
        rectA = angle
        updateRect()
        updateHandle()
    }

    //鼠标拖拽旋转,计算图片旋转角度
    function getAngle(mx, my) {
        var ox = rectX + rectW / 2
        var oy = rectY + rectH / 2
        var ax = rectX
        var ay = rectY
        var bx = mx
        var by = my

        // cosA = (AB^2+AC^2-BC^2)/(2*AB*AC) 三点求夹角
        var AB2 = (ax - ox) * (ax - ox) + (ay - oy) * (ay - oy)
        var AC2 = (ox - bx) * (ox - bx) + (oy - by) * (oy - by)
        var BC2 = (ax - bx) * (ax - bx) + (ay - by) * (ay - by)
        var cosA = (AB2 + AC2 - BC2) / (2 * Math.sqrt(AB2 * AC2))
        var A = Math.acos(cosA) * 180 / Math.PI

        // y = k(x-x0)+y0 判断在直线上面还是下面
        var k = rectW / rectH
        if ((bx - rectX) / k + rectY < by) {
            A = 360 - A
        }

        return A
    }

    //鼠标光标控制
    function setCoursor(mx, my, state) {
        var ox = rectX + rectW / 2
        var oy = rectY + rectH / 2

        var angle = mx === x? Math.PI / 2: Math.atan2(my - oy, mx - ox)
        angle = angle*180 / Math.PI
        angle += 22.5
        if(state === state_control_point) angle += 90
        angle = angle<0? angle + 360: angle

        if(state === state_none) coursor = Qt.ArrowCursor
        else if(state === state_drag) coursor = Qt.SizeAllCursor
        else if(state === state_rotate || state === state_control_point)
        {
            if(0 < angle && angle <= 45){ coursor = Qt.SizeVerCursor }
            else if(45 < angle && angle <= 90){ coursor = Qt.SizeBDiagCursor }
            else if(90 < angle && angle <= 135){ coursor = Qt.SizeHorCursor }
            else if(135 < angle && angle <= 180){ coursor = Qt.SizeFDiagCursor }
            else if(180 < angle && angle <= 225){ coursor = Qt.SizeVerCursor }
            else if(225 < angle && angle <= 270){ coursor = Qt.SizeBDiagCursor }
            else if(270 < angle && angle <= 315){ coursor = Qt.SizeHorCursor }
            else if(angle > 315 || angle <= 0){ coursor = Qt.SizeFDiagCursor }
            else {coursor = Qt.ArrowCursor}
        }
        else coursor = Qt.ArrowCursor

        positionMouseArea.cursorShape = coursor
        topLeft_RotateHandle_MouseArea.cursorShape = coursor
        topRight_RotateHandle_MouseArea.cursorShape = coursor
        bottomLeft_RotateHandle_MouseArea.cursorShape = coursor
        bottomRight_RotateHandle_MouseArea.cursorShape = coursor
        topLeft_ScaleHandle_MouseArea.cursorShape = coursor
        topRight_ScaleHandle_MouseArea.cursorShape = coursor
        bottomLeft_ScaleHandle_MouseArea.cursorShape = coursor
        bottomRight_ScaleHandle_MouseArea.cursorShape = coursor
        left_ScaleHandle_MouseArea.cursorShape = coursor
        right_ScaleHandle_MouseArea.cursorShape = coursor
        top_ScaleHandle_MouseArea.cursorShape = coursor
        bottom_ScaleHandle_MouseArea.cursorShape = coursor

        updateCursor() //发送信号通知上层更新光标图标
    }

    //鼠标拖拽对角线缩放,计算图片缩放比例
    function setScaleDiagonal(Xm, Ym, angle) {
        //计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
        var A = angle * Math.PI / 180
        var Xa = rectX + rectW / 2
        var Ya = rectY + rectH / 2
        var Xc = (rectX - Xa) * Math.cos(A) - (rectY - Ya) * Math.sin(A) + Xa
        var Yc = (rectX - Xa) * Math.sin(A) + (rectY - Ya) * Math.cos(A) + Ya

        //鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
        var k1 = (Ya - Yc) / (Xa - Xc)
        var b1 = Yc - k1 * Xc
        var k2 = -1 / k1
        var b2 = Ym - k2 * Xm
        var Xp = (b2 - b1) / (k1 - k2)
        var Yp = k1 * Xp + b1

        //计算缩放比例
        var t = (Xp - Xc) / (Xa - Xc) / 2
        //console.log("setScaleDiagonal", Xc, Yc, Xp ,Yp, A, t)

        //应用到变换
        rectX = t * (rectX - Xc) + Xc
        rectY = t * (rectY - Yc) + Yc
        rectW = t * rectW
        rectH = t * rectH
    }

    //鼠标拖拽水平缩放,计算图片缩放比例
    function setScaleXY(Xm, Ym, angle, direction) {
        //计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
        var A = angle * Math.PI / 180
        var Xa = rectX + rectW / 2
        var Ya = rectY + rectH / 2

        //缩放基准点基准点
        var Xl = 0
        var Yl = 0

        if(direction === direction_left)
        {
            Xl = Xa + (rectX + rectW - Xa) * Math.cos(A)
            Yl = Ya + (rectX + rectW - Xa) * Math.sin(A)
        }
        else if(direction === direction_right)
        {
            Xl = Xa + (rectX - Xa) * Math.cos(A)
            Yl = Ya + (rectX - Xa) * Math.sin(A)
        }
        else if(direction === direction_top)
        {
            Xl = Xa - (rectY + rectH - Ya) * Math.sin(A)
            Yl = Ya + (rectY + rectH - Ya) * Math.cos(A)
        }
        else if(direction === direction_bottom)
        {
            Xl = Xa - (rectY - Ya) * Math.sin(A)
            Yl = Ya + (rectY - Ya) * Math.cos(A)
        }

        //计算缩放比例
        var t = 0
        if(Xa != Xl && Ya != Yl)
        {
            //鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
            var k1 = (Ya - Yl) / (Xa - Xl)
            var b1 = Yl - k1 * Xl
            var k2 = -1 / k1
            var b2 = Ym - k2 * Xm
            var Xp = (b2 - b1) / (k1 - k2)
            //var Yp = k1 * Xp + b1

            if(Xp === Xl || Xa === Xl) return
            t = (Xp - Xl) / (Xa - Xl) /2
        }
        else if(Ya === Yl)
        {
            if(Xm === Xl || Xa === Xl) return
            t = (Xm - Xl) / (Xa - Xl) /2
        }
        else if(Xa === Xl)
        {
            if(Ym === Yl || Ya === Yl) return
            t = (Ym - Yl) / (Ya - Yl) /2
        }

        //应用变换
        var BI = 0
        if(direction === direction_left)
        {
            if(Ya === Yl)
            {
                rectX = rectX + (1 - t) * rectW
                rectW = t * rectW
            }
            else
            {
                BI = (1 - t) * (Xl - rectX)
                rectY = rectY + BI * Math.tan(A / 2)
                rectX = rectX + BI
                rectW = t * rectW
            }
        }
        else if(direction === direction_right)
        {
            if(Ya === Yl)
            {
                rectW = t * rectW
            }
            else
            {
                BI = (1 - t) * (Xl - rectX)
                rectY = rectY - BI * Math.tan((Math.PI - A) / 2)
                rectX = rectX + BI
                rectW = t * rectW
            }
        }
        else if(direction === direction_top)
        {
            if(Xa === Xl)
            {
                rectY = rectY + (1 - t) * rectH
                rectH = t * rectH
            }
            else
            {
                BI = (1 - t) * (Yl - rectY)
                rectX = rectX - BI * Math.tan(A / 2)
                rectY = rectY + BI
                rectH = t * rectH
            }
        }
        else if(direction === direction_bottom)
        {
            if(Xa === Xl)
            {
                rectH = t * rectH
            }
            else
            {
                BI = (1 - t) * (Yl - rectY)
                rectX = rectX + BI * Math.tan((Math.PI - A) / 2)
                rectY = rectY + BI
                rectH = t * rectH
            }
        }
    }

    Rectangle {
        id: uiRectangle
        color: 'transparent' //Qt.rgba(255, 0, 0, 0.5)
        border.width: borderSize
        border.color: uiColor
        rotation: 0

        MouseArea {
            id: positionMouseArea
            anchors.fill: parent
            hoverEnabled: true
            drag.target: uiRectangle
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    rectX = rectW > 0? uiRectangle.x: uiRectangle.x - rectW
                    rectY = rectH > 0? uiRectangle.y: uiRectangle.y - rectH
                    updateRect()
                }
                setCoursor(0, 0, state_drag)
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onExited:{setCoursor(0, 0, state_none)}
        }

        Image {
            source: image("rotate_arrow_tl.png")
            x: handleSize - this.width
            y: handleSize - this.height
        }

        Image {
            source: image("rotate_arrow_tr.png")
            x: uiRectangle.width - handleSize
            y: handleSize - this.height
        }

        Image {
            source: image("rotate_arrow_bl.png")
            x: handleSize - this.width
            y: uiRectangle.height - handleSize
        }

        Image {
            source: image("rotate_arrow_br.png")
            x: uiRectangle.width - handleSize
            y: uiRectangle.height - handleSize
        }

        Rectangle {
            color: uiColor
            x: (uiRectangle.width - handleSize) / 2
            y: (uiRectangle.height - handleSize) / 2
            width: handleSize
            height: handleSize
            radius: width / 2
        }

        Rectangle {
            color: uiColor
            x: 0
            y: 0
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: uiRectangle.width - handleSize
            y: 0
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: 0
            y: uiRectangle.height - handleSize
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: uiRectangle.width - handleSize
            y: uiRectangle.height - handleSize
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: (uiRectangle.width - handleSize) / 2
            y: 0
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: (uiRectangle.width - handleSize) / 2
            y: uiRectangle.height - handleSize
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: 0
            y: (uiRectangle.height - handleSize) / 2
            width: handleSize
            height: handleSize
        }

        Rectangle {
            color: uiColor
            x: uiRectangle.width - handleSize
            y: (uiRectangle.height - handleSize) / 2
            width: handleSize
            height: handleSize
        }
    }


    Rectangle {
        id: topLeft_RotateHandle
        color: handleColor
        width: handleSize * 2
        height: handleSize * 2
        MouseArea {
            id: topLeft_RotateHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_rotate)

                if(pressedButtons === Qt.LeftButton)
                {
                    var A = getAngle(mouse_x, mouse_y)
                    rectA = rectW>0? A: -A
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onExited:{setCoursor(0, 0, state_none)}
        }
    }

    Rectangle {
        id: topRight_RotateHandle
        color: handleColor
        width: handleSize * 2
        height: handleSize * 2
        MouseArea {
            id: topRight_RotateHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_rotate)

                if(pressedButtons === Qt.LeftButton)
                {
                    var A = getAngle(mouse_x, mouse_y)
                    var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI
                    rectA = rectW>0? A - p: -A - p
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onExited:{setCoursor(0, 0, state_none)}
        }
    }

    Rectangle {
        id: bottomLeft_RotateHandle
        color: handleColor
        width: handleSize * 2
        height: handleSize * 2
        MouseArea {
            id: bottomLeft_RotateHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_rotate)

                if(pressedButtons === Qt.LeftButton)
                {
                    var A = getAngle(mouse_x, mouse_y)
                    var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI + 180
                    rectA = rectW>0? A - p: -A - p
                    updateRect()
                }
            }
            onReleased: {
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onExited:{setCoursor(0, 0, state_none)}
        }
    }

    Rectangle {
        id: bottomRight_RotateHandle
        color: handleColor
        width: handleSize * 2
        height: handleSize * 2
        MouseArea {
            id: bottomRight_RotateHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_rotate)

                if(pressedButtons === Qt.LeftButton)
                {
                    var A = getAngle(mouse_x, mouse_y)
                    A -= 180
                    rectA = rectW>0? A: -A
                    updateRect()
                }
            }
            onReleased: {
                //rectA = 90
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onExited:{setCoursor(0, 0, state_none)}
        }
    }


    Rectangle {
        id: topLeft_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: topLeft_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleDiagonal(mouse_x, mouse_y, rectA + 180)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

    Rectangle {
        id: topRight_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: topRight_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    var A = 180 + rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleDiagonal(mouse_x, mouse_y, A)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

    Rectangle {
        id: bottomLeft_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: bottomLeft_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    var A = rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleDiagonal(mouse_x, mouse_y, A)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

    Rectangle {
        id: bottomRight_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: bottomRight_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleDiagonal(mouse_x, mouse_y, rectA)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }


    Rectangle {
        id: left_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: left_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleXY(mouse_x, mouse_y, rectA, direction_left)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

    Rectangle {
        id: right_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: right_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleXY(mouse_x, mouse_y, rectA, direction_right)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

    Rectangle {
        id: top_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: top_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleXY(mouse_x, mouse_y, rectA, direction_top)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

    Rectangle {
        id: bottom_ScaleHandle
        color: handleColor
        width: handleSize
        height: handleSize
        MouseArea {
            id: bottom_ScaleHandle_MouseArea
            anchors.fill: parent
            hoverEnabled: true
            onPositionChanged: {
                if(pressedButtons === Qt.LeftButton)
                {
                    mouse_x = mouseX + parent.x
                    mouse_y = mouseY + parent.y
                    setScaleXY(mouse_x, mouse_y, rectA, direction_bottom)
                    updateRect()
                }
            }
            onReleased: {
                updateRect()
                updateHandle()
                setCoursor(0, 0, state_none)
            }
            onEntered: {
                mouse_x = mouseX + parent.x
                mouse_y = mouseY + parent.y
                setCoursor(mouse_x, mouse_y, state_control_point)
            }
        }
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值