《你的性格主導(dǎo)色》揭秘

介紹

《你的性格主導(dǎo)色》是今年網(wǎng)易云音樂(lè)前端團(tuán)隊(duì)開(kāi)發(fā)的一款測(cè)試用戶主導(dǎo)色的 H5 應(yīng)用,上線后反響很好姻政,刷爆了微博和朋友圈。

項(xiàng)目的主要開(kāi)發(fā)者 imyzf 發(fā)表了一篇文章《官方揭秘岂嗓!你的顏色是這樣算出來(lái)的》汁展,解釋了一些動(dòng)效和最后主導(dǎo)色的計(jì)算方面的問(wèn)題。但由于涉及到了具體的業(yè)務(wù)厌殉,所以作者沒(méi)有開(kāi)源出源碼食绿,但是熱心的作者給了很多的提示。我就是根據(jù)這些提示公罕,揭秘了我比較感興趣的部分器紧。

在線 Demo

由于一直沒(méi)有在生產(chǎn)環(huán)境中使用Vue3.0vite,所以源碼部分我使用了 Vue3.0+vite實(shí)現(xiàn)楼眷。

頁(yè)面預(yù)加載

答題類(lèi)頁(yè)面與一般的 H5 頁(yè)面的不同之處在于铲汪,用戶的操作路徑是確定的,即每個(gè)頁(yè)面的下一頁(yè)路由是固定的摩桶,所以在 router 層面做了優(yōu)化桥状,提前預(yù)加載了下一個(gè)頁(yè)面

由于活動(dòng)頁(yè)面使用了大量的視頻和動(dòng)效等,所以想在用戶閱讀選擇題目的過(guò)程中把下一頁(yè)的頁(yè)面渲染完畢硝清,這樣切換到下一頁(yè)面的時(shí)候會(huì)很流暢辅斟,體驗(yàn)很好。

最初就想著怎么利用 vue-router 完成頁(yè)面的預(yù)加載芦拿。但是搞了一圈發(fā)現(xiàn)士飒,都是基于webpack或者vite的懶加載查邢,提前加載了一些資源,并不會(huì)提前渲染出頁(yè)面酵幕。

后來(lái)通過(guò)看vue-router文檔扰藕,才找到了靈感,利用命名視圖芳撒,同時(shí)展示 2 個(gè)視圖邓深,使用css隱藏下一頁(yè),這時(shí)候雖然不顯示笔刹,但是頁(yè)面已經(jīng)渲染出來(lái)了芥备。

通過(guò)修改router-viewname 屬性,完成頁(yè)面的切換舌菜。也就是說(shuō)萌壳,其實(shí)我的路由是沒(méi)有變化的。

// App.vue
<template>
  <router-view :name="currentViewName"></router-view>
  <router-view :name="nextViewName"></router-view>
</template>

// 注意 日月,這里使用兩個(gè) viewName 完成了頁(yè)面的跳轉(zhuǎn)袱瓮,next 的頁(yè)面被預(yù)加載
const currentViewName = computed(() => store.getters.currentViewName);
const nextViewName = computed(() => store.getters.nextViewName);

// router的定義部分
const routes = [
  {
    path: '/',
    components: {
      default: Index1,
      index2: Index2,
      session1: Session1,
      session2: Session2,
      session5: Session5
    }
  }
];

看上面的代碼,Index1爱咬、Index2Session1等其實(shí)就是每一頁(yè)的組件了尺借,通過(guò)修改currentViewNamenextViewName就可以達(dá)到頁(yè)面切換的目的。

最終的效果是下圖這樣的台颠,下一頁(yè)已經(jīng)提前渲染出來(lái):

image

翻頁(yè)動(dòng)效

作者提示說(shuō)使用canvas實(shí)現(xiàn)了頁(yè)面切換時(shí)候的幕布拉動(dòng)效果褐望,主要運(yùn)用了最核心的 canvas APIbezierCurveTo

通過(guò)查詢得知串前,bezierCurveTo 需要 3 個(gè) 點(diǎn)用來(lái)繪制三次貝賽爾曲線,在線體驗(yàn)

看下圖实蔽,想要實(shí)現(xiàn)拉動(dòng)動(dòng)畫(huà)荡碾,P1 P2 P3X軸坐標(biāo)需要持續(xù)變化,然后繪制曲線局装,就能夠?qū)崿F(xiàn)拉動(dòng)的效果了坛吁。

image

我這里使用了比較輕量的JavaScript 動(dòng)畫(huà)庫(kù)animejs,用來(lái)控制上面幾個(gè)點(diǎn)的持續(xù)移動(dòng)铐尚。3 個(gè)動(dòng)畫(huà)效果分別移動(dòng)了P1 P2 P3X軸坐標(biāo) 拨脉,再配合曲線的繪制,就達(dá)到了基本的拉動(dòng)幕布效果宣增。

  const heights = [0, 0.5 * pageHeight, pageHeight];
  points = {
    p1: {
      x: pageWidth,
      y: heights[0]
    },
    p2: {
      x: pageWidth,
      y: heights[1]
    },
    p3: {
      x: pageWidth,
      y: heights[2]
    },
    p4: {
      x: pageWidth,
      y: heights[2]
    },
    p5: {
      x: pageWidth,
      y: heights[0]
    }
  };

  // P1點(diǎn)的變化
  anime({
    targets: points.p1,
    x: 0,
    easing: 'easeInQuart',
    delay: 50,
    duration: 500
  });

  // P2點(diǎn)的變化
  anime({
    targets: points.p2,
    x: 0,
    easing: 'easeInSine',
    duration: 500
  });

  anime({
    targets: points.p2,
    y: 0.6 * pageHeight,
    easing: 'easeInSine',
    duration: 500
  });

  // P3點(diǎn)的變化
  anime({
    targets: points.p3,
    x: 0,
    easing: 'easeInQuart',
    delay: 50,
    duration: 500
  });

  // 畫(huà)曲線
  anime({
    duration: 550,
    update: function () {
      // 清除上一次的繪制
      ctx.clearRect(0, 0, pageWidth, pageHeight);
      ctx.beginPath();
      ctx.moveTo(points.p1.x, points.p1.y);
      // 幕布的上半?yún)^(qū)域
      ctx.bezierCurveTo(
        points.p1.x,
        points.p1.y,
        points.p2.x,
        points.p2.y - 0.2 * pageHeight,
        points.p2.x,
        points.p2.y
      );
      // 幕布的下半?yún)^(qū)域
      ctx.bezierCurveTo(
        points.p2.x,
        points.p2.y + 0.2 * pageHeight,
        points.p3.x,
        points.p3.y,
        points.p3.x,
        points.p3.y
      );
      // 已拉動(dòng)部分的矩形區(qū)域
      ctx.lineTo(points.p4.x, points.p4.y);
      ctx.lineTo(points.p5.x, points.p5.y);
      ctx.closePath();
      ctx.fill();
      ctx.strokeStyle = '#f1f1f1';
      ctx.stroke();
    }
  });

最終完成的效果是這樣的:

image

這個(gè)動(dòng)效由于每一頁(yè)都需要使用玫膀,所以考慮完成一個(gè)通用的全局組件。

考慮到使用的時(shí)候一般組件需要寫(xiě)到vue 模板上面爹脾,很不方便帖旨,所以最好通過(guò)一個(gè)全局函數(shù)直接顯示這段動(dòng)效箕昭,類(lèi)似于showAnimation();

首先需要完成一個(gè)獨(dú)立的組件解阅,由于想覆蓋掉頁(yè)面的所有信息落竹,所以使用了 Vue3.0 最新提供的teleport 組件:

<!-- 這個(gè)canvas會(huì)被渲染為 app 的子級(jí) -->
  <teleport to="#app">
    <canvas class="mask-canvas" ref="canvas" :class="{ 'mask-canvas-posi': isShow }"></canvas>
  </teleport>

然后需要把組件通過(guò) Vue 插件的方式注冊(cè)到全局屬性,由于我想使用 Composition API 货抄,所以最終決定使用 provide+ inject 的方式注冊(cè)和使用全局 property。一般的情況下使用app.config.globalProperties就可以了蟹地,但是這種配合Composition API寫(xiě)起來(lái)會(huì)比較麻煩,不推薦呀酸。

(Mask as any).install = (app: App): void => {
  // Vue3 的 Composition API 建議使用 provide + inject 的方式注冊(cè)和使用全局 property
  app.provide('mask', Mask);
};

// 使用的時(shí)候
const Mask = inject('mask');

最后,由于翻頁(yè)動(dòng)效和路由都在一起使用琼梆,就繼續(xù)封裝了個(gè)useNext函數(shù)茎杂,這樣在一般的view組件使用的話错览,就非常簡(jiǎn)單了,同時(shí)做了翻頁(yè)動(dòng)效和翻頁(yè)的操作:

nextPage();

到這里我可以夸夸Composition API了煌往,非常的簡(jiǎn)單和方便倾哺,通過(guò)這個(gè)全局通用組件的封裝,我徹底喜歡上了這種方式刽脖。

云層動(dòng)效

這部分是我覺(jué)得最有趣的羞海,以前用three.js實(shí)現(xiàn)過(guò)一個(gè) 3D 照片墻,但是這個(gè)云層動(dòng)效真的牛曲管,也是最難破解的却邓,還好被我搞定了,下篇詳細(xì)說(shuō)明破解的過(guò)程院水。

image

源碼

最后放上源碼腊徙,感興趣的同學(xué)可以看一下,歡迎 Star 和提出建議檬某。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末撬腾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恢恼,更是在濱河造成了極大的恐慌民傻,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異饰潜,居然都是意外死亡初坠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)彭雾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)碟刺,“玉大人,你說(shuō)我怎么就攤上這事薯酝“牍粒” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵吴菠,是天一觀的道長(zhǎng)者填。 經(jīng)常有香客問(wèn)我,道長(zhǎng)做葵,這世上最難降的妖魔是什么占哟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮酿矢,結(jié)果婚禮上榨乎,老公的妹妹穿的比我還像新娘。我一直安慰自己瘫筐,他們只是感情好蜜暑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著策肝,像睡著了一般肛捍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上之众,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天拙毫,我揣著相機(jī)與錄音,去河邊找鬼棺禾。 笑死恬偷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帘睦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼坦康,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼竣付!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起滞欠,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤古胆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逸绎,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惹恃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棺牧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巫糙。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖颊乘,靈堂內(nèi)的尸體忽然破棺而出参淹,到底是詐尸還是另有隱情,我是刑警寧澤乏悄,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布浙值,位于F島的核電站,受9級(jí)特大地震影響檩小,放射性物質(zhì)發(fā)生泄漏开呐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一规求、第九天 我趴在偏房一處隱蔽的房頂上張望筐付。 院中可真熱鬧,春花似錦颓哮、人聲如沸家妆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伤极。三九已至,卻和暖如春姨伤,著一層夾襖步出監(jiān)牢的瞬間哨坪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工乍楚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留当编,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓徒溪,卻偏偏與公主長(zhǎng)得像忿偷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子臊泌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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