鴻蒙OS高級(jí)技巧:打造個(gè)性化動(dòng)態(tài)Swiper效果

前言

在鴻蒙OS的廣闊天地中击敌,開(kāi)發(fā)者們有機(jī)會(huì)創(chuàng)造出令人驚嘆的用戶(hù)體驗(yàn)谓松。最近涡匀,我著手設(shè)計(jì)一款具有獨(dú)特滑動(dòng)效果的Swiper組件盯腌,它在滑動(dòng)時(shí)能夠迅速進(jìn)入視野,同時(shí)巧妙地將舊的cell隱藏到視線之外陨瘩。本文將分享如何利用鴻蒙的Swiper組件腕够,實(shí)現(xiàn)這一引人入勝的動(dòng)態(tài)效果。

56cd252ccd1f4ec5b6a4cf706839dae2-2.gif

一舌劳、設(shè)計(jì)與構(gòu)思

Swiper的設(shè)計(jì)理念是簡(jiǎn)潔而富有動(dòng)感帚湘。每個(gè)cell在滑動(dòng)時(shí)不僅會(huì)逐漸縮小至原始大小的70%,還會(huì)被前一個(gè)cell覆蓋甚淡,創(chuàng)造出一種流暢且連續(xù)的視覺(jué)效果大诸。這種效果的實(shí)現(xiàn),依賴(lài)于精確的動(dòng)畫(huà)控制和布局調(diào)整贯卦。

二资柔、代碼設(shè)計(jì)與實(shí)現(xiàn)思路

實(shí)現(xiàn)這一效果,我們需要對(duì)Swiper組件進(jìn)行深度定制脸侥。這包括對(duì)cell的尺寸建邓、位置和層級(jí)進(jìn)行動(dòng)態(tài)調(diào)整,以及利用貝塞爾曲線來(lái)實(shí)現(xiàn)平滑的動(dòng)畫(huà)效果睁枕。

三官边、控件采用與代碼說(shuō)明

3.1 Swiper組件定制

Swiper組件提供了豐富的API沸手,允許我們對(duì)其行為進(jìn)行精細(xì)控制。以下是一些關(guān)鍵的配置項(xiàng)和它們的作用:

  • itemSpace: 控制cell之間的間距注簿。
  • indicator: 是否顯示指示器契吉。
  • displayCount: 設(shè)置同時(shí)展示的cell數(shù)量。
  • onAreaChange: 當(dāng)Swiper區(qū)域大小變化時(shí)的回調(diào)诡渴。
  • customContentTransition: 自定義內(nèi)容轉(zhuǎn)換動(dòng)畫(huà)捐晶。

Swiper組件基礎(chǔ)配置代碼:

Swiper()
  .itemSpace(12)
  .indicator(false)
  .displayCount(this.DISPLAY_COUNT)
  .padding({left:10, right:10})
  .onAreaChange((oldValue, newValue) => {
    // 處理區(qū)域變化邏輯
  })
  .customContentTransition({
    transition: (proxy) => {
      // 自定義轉(zhuǎn)換邏輯
    }
  });
3.2 Item組件設(shè)置

每個(gè)Item需要根據(jù)其在Swiper中的位置進(jìn)行尺寸、位置和層級(jí)的調(diào)整妄辩。這涉及到初始化相關(guān)變量惑灵,并在aboutToAppear生命周期方法中進(jìn)行設(shè)置。

初始化寬高眼耀,初始化組件數(shù)據(jù):

  @State cw: number = 0;
  @State ch: number = 0;
  
  aboutToAppear(): void {
    initSwipe(...)
  }

  initSwipe(num:number){
    this.translateList = []
    for (let i = 0; i < num; i++) {
      this.scaleList.push(0.8)
      this.translateList.push(0.0)
      this.zIndexList.push(0)
    }
  }
  private MIN_SCALE: number = 0.70
  private DISPLAY_COUNT: number = 4
  private DISPLAY_WIDTH: number = 200
  @State scaleList: number[] = []
  @State translateList: number[] = []
  @State zIndexList: number[] = []

Item尺寸和位置設(shè)置代碼:

LifeStyleItem({lifeStyleResponse: item})
  .scale({ x: this.scaleList[index], y: this.scaleList[index] })
  .translate({ x: this.translateList[index] })
  .zIndex(this.zIndexList[index]);

在 customContentTransition的transition 屬性中設(shè)置屬性:


//scaleList 需要進(jìn)行線性變化
//translateList 位移需要進(jìn)行 數(shù)據(jù)偏移處理和貝塞爾曲線處理
//zIndexList 需要進(jìn)行位置層級(jí)設(shè)置

this.scaleList[proxy.index] = 線性函數(shù)
this.translateList[proxy.index] = - proxy.position * proxy.mainAxisLength + 貝塞爾曲線函數(shù)
this.zIndexList[proxy.index] = proxy.position
3.3 自定義動(dòng)畫(huà)效果

為了實(shí)現(xiàn)平滑的動(dòng)畫(huà)效果英支,我們定義了三次貝塞爾曲線函數(shù)和線性函數(shù)。這些函數(shù)將用于計(jì)算cell在滑動(dòng)過(guò)程中的尺寸哮伟、位置和層級(jí)變化干花。

三次貝塞爾曲線函數(shù):

function cubicBezier8(t, a1, b1, a2, b2) {
  // 計(jì)算三次貝塞爾曲線的值
  
const k1 = 3 * a1;
const k2 = 3 * (a2 - b1) - k1;
const k3 = 1 - k1 - k2;
return k3 * Math.pow(t, 3) + k2 * Math.pow(t, 2) + k1 * t;

}

線性函數(shù):

function chazhi(startPosition, endPosition, startValue, endValue, position) {
  // 計(jì)算線性插值的結(jié)果
 

const range = endPosition - startPosition;
const positionDifference = position - startPosition;
const fraction = positionDifference / range;

const valueRange = endValue - startValue;
const result = startValue + (valueRange * fraction);

return result;

}
3.4 計(jì)算函數(shù)實(shí)現(xiàn)

我們編寫(xiě)了計(jì)算函數(shù)來(lái)確定cell在Swiper中的最終表現(xiàn)。這包括根據(jù)位置計(jì)算尺寸楞黄、位置和層級(jí)池凄。

計(jì)算尺寸和位置的函數(shù):

function calculateValue(width: number, position: number): number {
  const minValue = 0;

  const normalizedPosition = position / 4;

  // 計(jì)算貝塞爾曲線的緩動(dòng)值
  const easedPosition = cubicBezier(normalizedPosition, 0.3, 0.1, 1,  0.05);

  // 根據(jù)緩動(dòng)值計(jì)算最終的變化值
  const value = minValue + (width - minValue) * easedPosition;
  return value;
}

function calculateValueScale(position) {

if (position >= 2.5) {
  // 當(dāng)position大于2時(shí),值固定為0.8
  return 0.8;
} else if (position < 2.5) {
  const startPosition = 2.5;
  const endPosition = -1;
  // 定義返回值的起始值和結(jié)束值
  const startValue = 0.8;
  const endValue = 0.7;
  return chazhi(startPosition,endPosition,startValue,endValue,position)
}

return 0.7;

}

四鬼廓、全部代碼整合

將上述所有代碼片段整合到一個(gè)組件中肿仑,確保Swiper和每個(gè)Item都能夠根據(jù)用戶(hù)的滑動(dòng)操作動(dòng)態(tài)調(diào)整。
代碼如下:


function calculateValue(width: number, position: number): number {
  const minValue = 0;
  const normalizedPosition = position / 4;
  const easedPosition = cubicBezier8(normalizedPosition, 0.3, 0.1, 1,  0.05);
  const value = minValue + (width - minValue) * easedPosition;
  return value;
}

function cubicBezier(t: number, a1: number, b1: number, a2: number, b2: number): number {
  const k1 = 3 * a1;
  const k2 = 3 * (a2 - b1) - k1;
  const k3 = 1 - k1 - k2;
  return k3 * Math.pow(t, 3) + k2 * Math.pow(t, 2) + k1 * t;
}

function calculateValueScale(position: number): number {
  if (position >= 2.5) {
    return 0.8;
  } else if (position < 2.5) {
    const startPosition = 2.5;
    const endPosition = -1;
    const startValue = 0.8;
    const endValue = 0.7;
    return chazhi(startPosition,endPosition,startValue,endValue,position)
  }
  return 0.7;
}

function chazhi(startPosition:number,endPosition:number,startValue:number,endValue:number,position:number):number{

  const range = endPosition - startPosition;
  const positionDifference = position - startPosition;
  const fraction = positionDifference / range;

  const valueRange = endValue - startValue;
  const result = startValue + (valueRange * fraction);

  return result;
}

@Component
struct Banner {
  @State cw: number = 0;
  @State ch: number = 0;
  aboutToAppear(): void {
  initSwipe()
  }

  initSwipe(num:number){
    this.translateList = []
    for (let i = 0; i < num; i++) {
      this.scaleList.push(0.8)
      this.translateList.push(0.0)
      this.zIndexList.push(0)
    }
  }
  private MIN_SCALE: number = 0.70
  private DISPLAY_COUNT: number = 4
  private DISPLAY_WIDTH: number = 200
  @State scaleList: number[] = []
  @State translateList: number[] = []
  @State zIndexList: number[] = []
  
  
  build(){
  Swiper() {
          ForEach(this.lifeStyleList, (item: LifeStyleResponse|null,index) => {
            LifeStyleItem({lifeStyleResponse:item})
              .scale({ x: this.scaleList[index], y: this.scaleList[index] })
              .translate({ x: this.translateList[index] })
              .zIndex(this.zIndexList[index])
          }
          )
        }
        .itemSpace(12)
        .indicator(false)
        .displayCount(this.DISPLAY_COUNT)
        .padding({left:10,right:10})
        .onAreaChange((oldValue,newValue)=>{
          this.cw = new Number(newValue.width).valueOf()
          this.ch = new Number(newValue.height).valueOf()
        })
        .customContentTransition({
          transition :(proxy: SwiperContentTransitionProxy)=>{
            this.scaleList[proxy.index] = calculateValueScale(proxy.position)
            this.translateList[proxy.index] = - proxy.position * proxy.mainAxisLength + calculateValue8(this.cw,proxy.position)
            this.zIndexList[proxy.index] = proxy.position
          }
        })
  }

五桑阶、總結(jié)

通過(guò)本文的深入解析柏副,我們不僅實(shí)現(xiàn)了一個(gè)具有個(gè)性化動(dòng)態(tài)效果的Swiper組件勾邦,還學(xué)習(xí)了如何利用鴻蒙OS的強(qiáng)大API來(lái)定制動(dòng)畫(huà)和布局蚣录。希望這篇文章能夠激發(fā)更多開(kāi)發(fā)者的創(chuàng)造力,共同探索鴻蒙OS的無(wú)限可能眷篇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萎河,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蕉饼,更是在濱河造成了極大的恐慌虐杯,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昧港,死亡現(xiàn)場(chǎng)離奇詭異擎椰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)创肥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)达舒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)值朋,“玉大人,你說(shuō)我怎么就攤上這事巩搏∽虻牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵贯底,是天一觀的道長(zhǎng)丰辣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)禽捆,這世上最難降的妖魔是什么笙什? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮胚想,結(jié)果婚禮上得湘,老公的妹妹穿的比我還像新娘。我一直安慰自己顿仇,他們只是感情好淘正,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著臼闻,像睡著了一般鸿吆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上述呐,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天惩淳,我揣著相機(jī)與錄音,去河邊找鬼乓搬。 笑死思犁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的进肯。 我是一名探鬼主播激蹲,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼江掩!你這毒婦竟也來(lái)了学辱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤环形,失蹤者是張志新(化名)和其女友劉穎策泣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體抬吟,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萨咕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了火本。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片危队。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蓄喇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出交掏,到底是詐尸還是另有隱情妆偏,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布盅弛,位于F島的核電站钱骂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挪鹏。R本人自食惡果不足惜见秽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讨盒。 院中可真熱鬧解取,春花似錦、人聲如沸返顺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遂鹊。三九已至振乏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秉扑,已是汗流浹背慧邮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舟陆,地道東北人误澳。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像秦躯,于是被迫代替她去往敵國(guó)和親忆谓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容