多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > Android翻頁效果原理實現之曲線的實現

Android翻頁效果原理實現之曲線的實現

來源:程序員人生   發布時間:2015-01-21 08:29:31 閱讀次數:7191次

尊重原創轉載請注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵權必究!

炮兵鎮樓

上1節我們通過引入折線實現了頁面的折疊翻轉效果,有了前面兩節的基礎呢其實曲線的實現可以變得非常簡單,為何這么說呢?由于曲線不過就是在折線的基礎上對Path加入了曲線的實現,進而只是影響了我們的Region區域,而其他的甚么事件啊、滑動計算啊之類的幾近都是不變的對吧,說白了就是對現有的折線View進行update改造,雖然是改造,但是我們該如何下手呢?首先我們來看看現實中翻頁的效果應當是怎樣的呢?如果大家身旁有書或本子乃至1張紙也行,嘗試以不同的方式去翻動它,你會發現除我們前面兩節曾提到過的1些限制外,還有1些special的現象:

1、翻起來的區域從側面來看是1個有弧度的區域,如圖所示側面圖:


而我們將依照第1節中的約定疏忽這部份弧度的表現,由于從正俯視的角度我們壓根看不到弧度的效果,So~我們強迫讓其與頁面平行:


2、根據拖拽點距離頁面高度的不同,我們可以得到不同的卷曲度:


而其在我們正俯視點的表現則是曲線的弧度不同:


一樣的,我們依照第1節的約定,為了簡化問題,我們將拖拽點距離頁面的高度視為1個定值使在我們正俯視點表現的曲線出發點從距離控件交點1/4處開始:


3、如上1節末所說,在曲折的區域圖象也會有相似的扭曲效果

OK,大致的1個分析就是這樣,我們根據分析結果可以得出下面的1個分析圖:


由上圖配合我們上面的分析我們可知:DB = 1/4OB,FA = 1/4OA,而點F和點D分別為兩條曲線(如無特殊聲明,我們所說的曲線均為貝賽爾曲線,下同)的出發點(固然你也能夠說是終點無所謂),這時候,我們以點A、B為曲線的控制點并以其為端點分別沿著x軸和y軸方向作線段AG、BC,另AG = AF、BC = BD,并令點G、C分別為曲線的終點,這樣,我們的這兩條2階貝塞爾曲線就非常非常的特殊,例如上圖中的曲線DC,它是由起始點D、C和控制點B構成,而BD = BC,也就是說3角形BDC是的等腰3角形,進1步地說就是曲線DC的兩條控制桿力臂相等,進1步地我們可以推斷出曲線DC的頂點J一定在直線DC的中垂線上,更進1步地我們可以根據《自定義控件其實很簡單5/12》所說的2階貝塞爾曲線公式得出當且僅當t = 0.5時曲線的端點恰好會在頂點J上,由此我們可以非常非常簡單地得到曲線的頂點坐標。好了,YY歸YY我們還是要回歸到具體的操作中來,首先,我們要計算出點G、F、D、C的坐標值,這4點坐標也相當easy,就拿F點坐標來講,我們過點F分別作OM、AM的垂線:


由于FA = 1/4OA,那末我們可以得到F點的x坐標Fx = a + 3/4MA,y坐標Fy = b + 3/4OM,而G點的x坐標Gx = a + MA - 1/4x;其他兩點D、C就不多扯了,那末在代碼中如何體現呢?首先,為了便于視察效果,我們先注釋掉圖片的繪制:

/* * 如果坐標點在原點(即還沒產生觸碰時)則繪制第1頁 */ if (mPointX == 0 && mPointY == 0) { // canvas.drawBitmap(mBitmaps.get(mBitmaps.size() - 1), 0, 0, null); return; } // 省略大量代碼 //drawBitmaps(canvas);
并繪制線條:

canvas.drawPath(mPath, mPaint);
在上1節中我們在生成Path時將情況分為了兩種:

if (sizeLong > mViewHeight) { //………………………… } else { //………………………… }
一樣,我們也分開處理兩種情況,那末針對sizeLong > mViewHeight的時候此時控件頂部的曲線效果已是看不到了,我們只需斟酌底部的曲線效果:

// 計算曲線出發點 float startXBtm = btmX2 - CURVATURE * sizeShort; float startYBtm = mViewHeight; // 計算曲線終點 float endXBtm = mPointX + (1 - CURVATURE) * (tempAM); float endYBtm = mPointY + (1 - CURVATURE) * mL; // 計算曲線控制點 float controlXBtm = btmX2; float controlYBtm = mViewHeight; // 計算曲線頂點 float bezierPeakXBtm = 0.25F * startXBtm + 0.5F * controlXBtm + 0.25F * endXBtm; float bezierPeakYBtm = 0.25F * startYBtm + 0.5F * controlYBtm + 0.25F * endYBtm; /* * 生成帶曲線的4邊形路徑 */ mPath.moveTo(startXBtm, startYBtm); mPath.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPath.lineTo(mPointX, mPointY); mPath.lineTo(topX1, 0); mPath.lineTo(topX2, 0); mPath.lineTo(bezierPeakXBtm, bezierPeakYBtm);
該部份的實際效果以下:


PS:為了便于大家對參數的理解,我對每個點的坐標都重新給予了1個援用其命名也淺顯易懂,實際進程可以省略這1步簡化代碼

而當sizeLong <= mViewHeight時這時候候不但底部有曲線效果,右邊也有:

/* * 計算參數 */ float leftY = mViewHeight - sizeLong; float btmX = mViewWidth - sizeShort; // 計算曲線出發點 float startXBtm = btmX - CURVATURE * sizeShort; float startYBtm = mViewHeight; float startXLeft = mViewWidth; float startYLeft = leftY - CURVATURE * sizeLong; /* * 限制左邊曲線出發點 */ if (startYLeft <= 0) { startYLeft = 0; } /* * 限制右邊曲線出發點 */ if (startXBtm <= 0) { startXBtm = 0; } // 計算曲線終點 float endXBtm = mPointX + (1 - CURVATURE) * (tempAM); float endYBtm = mPointY + (1 - CURVATURE) * mL; float endXLeft = mPointX + (1 - CURVATURE) * mK; float endYLeft = mPointY - (1 - CURVATURE) * (sizeLong - mL); // 計算曲線控制點 float controlXBtm = btmX; float controlYBtm = mViewHeight; float controlXLeft = mViewWidth; float controlYLeft = leftY; // 計算曲線頂點 float bezierPeakXBtm = 0.25F * startXBtm + 0.5F * controlXBtm + 0.25F * endXBtm; float bezierPeakYBtm = 0.25F * startYBtm + 0.5F * controlYBtm + 0.25F * endYBtm; float bezierPeakXLeft = 0.25F * startXLeft + 0.5F * controlXLeft + 0.25F * endXLeft; float bezierPeakYLeft = 0.25F * startYLeft + 0.5F * controlYLeft + 0.25F * endYLeft; /* * 生成帶曲線的3角形路徑 */ mPath.moveTo(startXBtm, startYBtm); mPath.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPath.lineTo(mPointX, mPointY); mPath.lineTo(endXLeft, endYLeft); mPath.quadTo(controlXLeft, controlYLeft, startXLeft, startYLeft);
效果以下:


Path有了,我們就該斟酌如何將其轉換為Region,在這個進程中呢又1個問題,曲線路徑不像上1節的直線路徑我們可以輕易取得其范圍區域,由于我們的折疊區域其實應當是這樣的:


如圖所示紅色路徑區域,這部份區域則是我們折疊的區域,而事實上我們為了計算方便將整條2階貝賽爾曲線都繪制了出來,也就是說我們的Path除紅色線條部份還包括了藍色線條部份對吧,那末問題來了,如何將這兩部份“做掉”呢?其實方法很多,我們可以在計算的時候就只生成半條曲線,這是方法1我們利用純計算的方式,記得我在該系列文章開頭曾說過翻頁效果的實現可以有兩種方式,1種是純計算而另外一種則是利用圖形的組合思想,如何組合呢?這里對區域的計算我們就不用純計算的方式了,我們嘗試用圖形組合來試試。首先我們將Path轉為Region看看是甚么樣的:

Region region = computeRegion(mPath); canvas.clipRegion(region); canvas.drawColor(Color.RED); // canvas.drawPath(mPath, mPaint);
效果以下:


可以看到我們沒有封閉的Path構成的Region效果,事實呢跟我們需要的區域差距有點大,首先上下兩個月半圓是過剩的,其次目測少了1塊對吧:


如上圖藍色的那塊,那末我們該如何把這塊“補”回來呢?利用圖形組合的思想,我們想法為該Region補1塊矩形:


然后差集掉兩個月半圓不就成了?這部份代碼改動較大,我先貼代碼再說吧:

if (sizeLong > mViewHeight) { // 計算……額……按圖來AN邊~ float an = sizeLong - mViewHeight; // 3角形AMN的MN邊 float largerTrianShortSize = an / (sizeLong - (mViewHeight - mPointY)) * (mViewWidth - mPointX); // 3角形AQN的QN邊 float smallTrianShortSize = an / sizeLong * sizeShort; /* * 計算參數 */ float topX1 = mViewWidth - largerTrianShortSize; float topX2 = mViewWidth - smallTrianShortSize; float btmX2 = mViewWidth - sizeShort; // 計算曲線出發點 float startXBtm = btmX2 - CURVATURE * sizeShort; float startYBtm = mViewHeight; // 計算曲線終點 float endXBtm = mPointX + (1 - CURVATURE) * (tempAM); float endYBtm = mPointY + (1 - CURVATURE) * mL; // 計算曲線控制點 float controlXBtm = btmX2; float controlYBtm = mViewHeight; // 計算曲線頂點 float bezierPeakXBtm = 0.25F * startXBtm + 0.5F * controlXBtm + 0.25F * endXBtm; float bezierPeakYBtm = 0.25F * startYBtm + 0.5F * controlYBtm + 0.25F * endYBtm; /* * 生成帶曲線的4邊形路徑 */ mPath.moveTo(startXBtm, startYBtm); mPath.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPath.lineTo(mPointX, mPointY); mPath.lineTo(topX1, 0); mPath.lineTo(topX2, 0); /* * 替補區域Path */ mPathTrap.moveTo(startXBtm, startYBtm); mPathTrap.lineTo(topX2, 0); mPathTrap.lineTo(bezierPeakXBtm, bezierPeakYBtm); mPathTrap.close(); /* * 底部月半圓Path */ mPathSemicircleBtm.moveTo(startXBtm, startYBtm); mPathSemicircleBtm.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPathSemicircleBtm.close(); /* * 生成包括折疊和下1頁的路徑 */ //暫時沒用省略掉 // 計算月半圓區域 mRegionSemicircle = computeRegion(mPathSemicircleBtm); } else { /* * 計算參數 */ float leftY = mViewHeight - sizeLong; float btmX = mViewWidth - sizeShort; // 計算曲線出發點 float startXBtm = btmX - CURVATURE * sizeShort; float startYBtm = mViewHeight; float startXLeft = mViewWidth; float startYLeft = leftY - CURVATURE * sizeLong; // 計算曲線終點 float endXBtm = mPointX + (1 - CURVATURE) * (tempAM); float endYBtm = mPointY + (1 - CURVATURE) * mL; float endXLeft = mPointX + (1 - CURVATURE) * mK; float endYLeft = mPointY - (1 - CURVATURE) * (sizeLong - mL); // 計算曲線控制點 float controlXBtm = btmX; float controlYBtm = mViewHeight; float controlXLeft = mViewWidth; float controlYLeft = leftY; // 計算曲線頂點 float bezierPeakXBtm = 0.25F * startXBtm + 0.5F * controlXBtm + 0.25F * endXBtm; float bezierPeakYBtm = 0.25F * startYBtm + 0.5F * controlYBtm + 0.25F * endYBtm; float bezierPeakXLeft = 0.25F * startXLeft + 0.5F * controlXLeft + 0.25F * endXLeft; float bezierPeakYLeft = 0.25F * startYLeft + 0.5F * controlYLeft + 0.25F * endYLeft; /* * 限制右邊曲線出發點 */ if (startYLeft <= 0) { startYLeft = 0; } /* * 限制底部左邊曲線出發點 */ if (startXBtm <= 0) { startXBtm = 0; } /* * 根據底部左邊限制點重新計算貝塞爾曲線頂點坐標 */ float partOfShortLength = CURVATURE * sizeShort; if (btmX >= -mValueAdded && btmX <= partOfShortLength - mValueAdded) { float f = btmX / partOfShortLength; float t = 0.5F * f; float bezierPeakTemp = 1 - t; float bezierPeakTemp1 = bezierPeakTemp * bezierPeakTemp; float bezierPeakTemp2 = 2 * t * bezierPeakTemp; float bezierPeakTemp3 = t * t; bezierPeakXBtm = bezierPeakTemp1 * startXBtm + bezierPeakTemp2 * controlXBtm + bezierPeakTemp3 * endXBtm; bezierPeakYBtm = bezierPeakTemp1 * startYBtm + bezierPeakTemp2 * controlYBtm + bezierPeakTemp3 * endYBtm; } /* * 根據右邊限制點重新計算貝塞爾曲線頂點坐標 */ float partOfLongLength = CURVATURE * sizeLong; if (leftY >= -mValueAdded && leftY <= partOfLongLength - mValueAdded) { float f = leftY / partOfLongLength; float t = 0.5F * f; float bezierPeakTemp = 1 - t; float bezierPeakTemp1 = bezierPeakTemp * bezierPeakTemp; float bezierPeakTemp2 = 2 * t * bezierPeakTemp; float bezierPeakTemp3 = t * t; bezierPeakXLeft = bezierPeakTemp1 * startXLeft + bezierPeakTemp2 * controlXLeft + bezierPeakTemp3 * endXLeft; bezierPeakYLeft = bezierPeakTemp1 * startYLeft + bezierPeakTemp2 * controlYLeft + bezierPeakTemp3 * endYLeft; } /* * 替補區域Path */ mPathTrap.moveTo(startXBtm, startYBtm); mPathTrap.lineTo(startXLeft, startYLeft); mPathTrap.lineTo(bezierPeakXLeft, bezierPeakYLeft); mPathTrap.lineTo(bezierPeakXBtm, bezierPeakYBtm); mPathTrap.close(); /* * 生成帶曲線的3角形路徑 */ mPath.moveTo(startXBtm, startYBtm); mPath.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPath.lineTo(mPointX, mPointY); mPath.lineTo(endXLeft, endYLeft); mPath.quadTo(controlXLeft, controlYLeft, startXLeft, startYLeft); /* * 生成底部月半圓的Path */ mPathSemicircleBtm.moveTo(startXBtm, startYBtm); mPathSemicircleBtm.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPathSemicircleBtm.close(); /* * 生成右邊月半圓的Path */ mPathSemicircleLeft.moveTo(endXLeft, endYLeft); mPathSemicircleLeft.quadTo(controlXLeft, controlYLeft, startXLeft, startYLeft); mPathSemicircleLeft.close(); /* * 生成包括折疊和下1頁的路徑 */ //暫時沒用省略掉 /* * 計算底部和右邊兩月半圓區域 */ Region regionSemicircleBtm = computeRegion(mPathSemicircleBtm); Region regionSemicircleLeft = computeRegion(mPathSemicircleLeft); // 合并兩月半圓區域 mRegionSemicircle.op(regionSemicircleBtm, regionSemicircleLeft, Region.Op.UNION); } // 根據Path生成的折疊區域 Region regioFlod = computeRegion(mPath); // 替補區域 Region regionTrap = computeRegion(mPathTrap); // 令折疊區域與替補區域相加 regioFlod.op(regionTrap, Region.Op.UNION); // 從相加后的區域中剔除掉月半圓的區域取得終究折疊區域 regioFlod.op(mRegionSemicircle, Region.Op.DIFFERENCE); /* * 根據裁剪區域填充畫布 */ canvas.clipRegion(regioFlod); canvas.drawColor(Color.RED);
200行的代碼我們就做了1件事就是正確計算Path,一樣我們還是依照之前的分了兩種情況來計算,第1種情況sizeLong > mViewHeight時,我們先計算替補的這塊區域:


如上代碼46⑷9行

/* * 替補區域Path */ mPathTrap.moveTo(startXBtm, startYBtm); mPathTrap.lineTo(topX2, 0); mPathTrap.lineTo(bezierPeakXBtm, bezierPeakYBtm); mPathTrap.close();
然后計算底部的月半圓Path:


對應代碼54⑸6行

/* * 底部月半圓Path */ mPathSemicircleBtm.moveTo(startXBtm, startYBtm); mPathSemicircleBtm.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPathSemicircleBtm.close();
將當前折疊區域和替補區域相加再減去月半圓Path區域我們就能夠得到正確的折疊區域,對應代碼64行和192⑵01行:

// 計算月半圓區域 mRegionSemicircle = computeRegion(mPathSemicircleBtm); // ………………中間省略巨量代碼……………… // 根據Path生成的折疊區域 Region regioFlod = computeRegion(mPath); // 替補區域 Region regionTrap = computeRegion(mPathTrap); // 令折疊區域與替補區域相加 regioFlod.op(regionTrap, Region.Op.UNION); // 從相加后的區域中剔除掉月半圓的區域取得終究折疊區域 regioFlod.op(mRegionSemicircle, Region.Op.DIFFERENCE);
該情況下我們的折疊區域是醬紫的:


兩1種情況則略微復雜些,除要計算底部,我們還要計算右邊的月半圓Path區域,代碼165⑴74:

/* * 生成底部月半圓的Path */ mPathSemicircleBtm.moveTo(startXBtm, startYBtm); mPathSemicircleBtm.quadTo(controlXBtm, controlYBtm, endXBtm, endYBtm); mPathSemicircleBtm.close(); /* * 生成右邊月半圓的Path */ mPathSemicircleLeft.moveTo(endXLeft, endYLeft); mPathSemicircleLeft.quadTo(controlXLeft, controlYLeft, startXLeft, startYLeft); mPathSemicircleLeft.close(); 替補區域的計算,147⑴51: /* * 替補區域Path */ mPathTrap.moveTo(startXBtm, startYBtm); mPathTrap.lineTo(startXLeft, startYLeft); mPathTrap.lineTo(bezierPeakXLeft, bezierPeakYLeft); mPathTrap.lineTo(bezierPeakXBtm, bezierPeakYBtm); mPathTrap.close(); 區域的轉換,184⑴88: /* * 計算底部和右邊兩月半圓區域 */ Region regionSemicircleBtm = computeRegion(mPathSemicircleBtm); Region regionSemicircleLeft = computeRegion(mPathSemicircleLeft); // 合并兩月半圓區域 mRegionSemicircle.op(regionSemicircleBtm, regionSemicircleLeft, Region.Op.UNION);
終究的計算跟上面第1種情況1樣,效果以下:


結合兩種情況,我們可以得到下面的效果:


然后,我們需要計算“下1頁”的區域,一樣,根據上1節我們的講授,我們先獲得折疊區域和下1頁區域之和再減去折疊區域就能夠得到下1頁的區域:

mRegionNext = computeRegion(mPathFoldAndNext); mRegionNext.op(mRegionFold, Region.Op.DIFFERENCE);
繪制效果以下:


最后,我們結合上兩節,注入數據:

/** * 繪制位圖數據 * * @param canvas * 畫布對象 */ private void drawBitmaps(Canvas canvas) { // 繪制位圖前重置isLastPage為false isLastPage = false; // 限制pageIndex的值范圍 mPageIndex = mPageIndex < 0 ? 0 : mPageIndex; mPageIndex = mPageIndex > mBitmaps.size() ? mBitmaps.size() : mPageIndex; // 計算數據起始位置 int start = mBitmaps.size() - 2 - mPageIndex; int end = mBitmaps.size() - mPageIndex; /* * 如果數據出發點位置小于0則表示當前已到了最后1張圖片 */ if (start < 0) { // 此時設置isLastPage為true isLastPage = true; // 并顯示提示信息 showToast("This is fucking lastest page"); // 強迫重置起始位置 start = 0; end = 1; } /* * 計算當前頁的區域 */ canvas.save(); canvas.clipRegion(mRegionCurrent); canvas.drawBitmap(mBitmaps.get(end - 1), 0, 0, null); canvas.restore(); /* * 計算折疊頁的區域 */ canvas.save(); canvas.clipRegion(mRegionFold); canvas.translate(mPointX, mPointY); /* * 根據長短邊標識計算折疊區域圖象 */ if (mRatio == Ratio.SHORT) { canvas.rotate(90 - mDegrees); canvas.translate(0, -mViewHeight); canvas.scale(⑴, 1); canvas.translate(-mViewWidth, 0); } else { canvas.rotate(-(90 - mDegrees)); canvas.translate(-mViewWidth, 0); canvas.scale(1, ⑴); canvas.translate(0, -mViewHeight); } canvas.drawBitmap(mBitmaps.get(end - 1), 0, 0, null); canvas.restore(); /* * 計算下1頁的區域 */ canvas.save(); canvas.clipRegion(mRegionNext); canvas.drawBitmap(mBitmaps.get(start), 0, 0, null); canvas.restore(); }
終究效果以下:


該部份的代碼就不貼出了,大部份跟上1節相同,由于過兩天要去旅游時間略緊這節略講得粗糙,不過也沒甚么太大的改動,如果大家有不懂的地方可以留言或群里@哥,下1節我們將嘗試實現翻頁時圖象扭曲的效果。

源碼地址:傳送門

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 成人亚洲综合 | 亚洲欧洲日产国码二区在线 | 日本天堂视频在线观看 | 性欧美free | 中文字幕无线精品乱码一区 | 秋霞日韩理论高清在线观看 | 免费观看欧美成人1314w色 | 久久成人性色生活片 | 国产精品亚洲欧美日韩一区在线 | 欧洲性大片xxxxx久久久 | 91综合网| 久久久亚洲欧洲国产 | 亚洲综合日韩精品欧美综合区 | 亚洲精品一区91 | 久久精品国产精品亚洲 | 日本校园春色 | 国产嫩草影院精品免费网址 | 老司机午夜视频在线观看 | 日本免费一区视频 | 噜噜噜在线视频免费观看 | 荷兰videos | 欧美大片天天免费看视频 | 免费观看欧美成人1314w色 | 小说区视频区图片区 | 伊人一本之道 | 波多野结衣在线视频观看 | 成人免费性视频 | 欧美18videossex性欧美 | 最近中文字幕国语完整在线5 | 男女自偷自拍视频免费观看篇 | 欧美亚洲不卡 | 欧美一区精品二区三区 | 就色干综合 | 久久精品免费一区二区视 | 第一福利在线观看永久视频 | 欧美乱人伦中文在线观看不卡 | 国产欧美一区二区精品性色 | 男人在线网址 | 精品国产一区二区三区国产馆 | 欧美精品videossex欧美性 | 国产精品福利在线观看 |