效果預(yù)覽
功能說明
- 使用Canvas繪制元素移動(dòng)動(dòng)效熔萧,極致高效;
- 支持任意方向元素漂移(起點(diǎn)洪唐、終點(diǎn)任意)钻蹬;
- 支持修改元素圖標(biāo)及其大小(單位dp)凭需;
- 支持修改動(dòng)效持續(xù)時(shí)間及動(dòng)畫結(jié)束時(shí)回調(diào)问欠;
- 支持修改元素飄動(dòng)個(gè)數(shù);
使用方式
- 在工程根目錄的build.gradle中添加
allprojects {
repositories {
maven { url 'https://www.jitpack.io' }
...
}
}
- 添加依賴
implementation 'com.gitee.chockqiu:animation-views:1.1'
- 在xml中添加布局
<com.chockqiu.view.ElementFloatTogetherAnimation
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 確定起點(diǎn)及終點(diǎn)粒蜈,開始動(dòng)畫&處理回調(diào)
val p0 = Point().apply {
x = (viewStart.left + viewStart.right) / 2
y = (viewStart.top + viewStart.bottom) / 2
}
val p1 = Point().apply {
x = (endView.left + endView.right) / 2
y = (endView.top + endView.bottom) / 2
}
mElement.startAnimation(p0, p1) {
...
}
PS: 為了起/終點(diǎn)坐標(biāo)與預(yù)期一致顺献,建議將
View
放置在跟起點(diǎn)終點(diǎn)View
同一個(gè)ViewGroup
中,并且動(dòng)效View
的寬高與ViewGroup
一致枯怖,也就是match_parent
(動(dòng)效內(nèi)部坐標(biāo)系是以自身左上角為坐標(biāo)原點(diǎn)進(jìn)行繪制的)注整,如不一致需要進(jìn)行坐標(biāo)轉(zhuǎn)換。
實(shí)現(xiàn)原理
利用二階貝塞爾曲線控制點(diǎn)
決定曲線路徑的特性度硝,通過算法生成每一個(gè)元素的控制點(diǎn)
肿轨,實(shí)現(xiàn)不同元素通過不同的路徑從起點(diǎn)飄向終點(diǎn),再加上不同元素移動(dòng)動(dòng)畫開始的時(shí)間差蕊程,最終組合實(shí)現(xiàn)這么一種效果萝招。
二階貝塞爾曲線原理圖如下:
實(shí)現(xiàn)步驟
1)實(shí)現(xiàn)二階貝塞爾曲線數(shù)學(xué)公式;
具體實(shí)現(xiàn)詳見quadToFunValue(float t, Point p0, Point p1, Point p2)
函數(shù)存捺。
2)通過算法生成每個(gè)元素的控制點(diǎn),為了好看控制點(diǎn)需在起點(diǎn)與終點(diǎn)所在直線的中垂線上隨機(jī)分布曙蒸;
所以最終控制點(diǎn)是一個(gè)與中點(diǎn)M(Mx,My)距離為D的坐標(biāo)捌治,且它所在的直線是起點(diǎn)與終點(diǎn)所在直線的中垂線。
如何計(jì)算控制點(diǎn)坐標(biāo)呢纽窟?
首先通過起點(diǎn)終點(diǎn)所在的直線可以計(jì)算出中垂線的斜率k2:
private double centerk(Point a, Point b) {
//已知A(Ax,Ay),B(Bx,By); 中垂線的斜率為:
//-1/k=-1/[(By-Ay)/(Bx-Ax)]=-(Bx-Ax)/(By-Ay)
if (b.y == a.y) {
return 0;
}
if (b.x == a.x) {
return Double.MAX_VALUE;
}
return -(((b.x - a.x) * 1f / (b.y - a.y)));
}
已知隨機(jī)的確定正值D肖油,可以計(jì)算出控制點(diǎn)與坐標(biāo)中點(diǎn)M(Mx,My)的坐標(biāo)距離dx和dy, 數(shù)學(xué)公式如下:
等式1: dy/dx = k2
等式2: dx*dx + dy*dy = D*D
只要起點(diǎn)/終點(diǎn)確認(rèn), 則k1是一個(gè)定值,那么它的中垂線斜率k2也是定值臂港,再加上隨機(jī)的正值D森枪,經(jīng)過轉(zhuǎn)換可得
dx = D*D/(k2*k2+1)
dy = (D*D*k2*k2)/(k2*k2+1)
/**
* 斜率k時(shí)距離d對(duì)應(yīng)的dx值
*
* @param k 斜率k
* @param d 距離d
* @return
*/
private double dx(double k, double d) {
if (k == 0) {
return 0;
}
if (k == Double.MAX_VALUE) {
return d;
}
double dx = Math.sqrt((d * d) / (k * k + 1));
if (d < 0) {
return -dx;
} else {
return dx;
}
}
/**
* 斜率k時(shí)距離d對(duì)應(yīng)的dy值
*
* @param k 斜率k
* @param d 距離d
* @return
*/
private double dy(double k, double d) {
if (k == 0) {
return d;
}
if (k == Double.MAX_VALUE) {
return 0;
}
double dy = Math.sqrt((d * d) * (k * k) / (k * k + 1));
if (d < 0) {
return -dy;
} else {
return dy;
}
}
在根據(jù)起點(diǎn)(Sx,Sy)视搏、終點(diǎn)(Ex,Ey)的朝向不同,確定dx與dy的符號(hào)县袱,可能是-dx浑娜,-dy,也可能是dx式散,-dy等等
最終得到控制點(diǎn)的精確坐標(biāo)值筋遭;