JDR DESIGN 開發小結 — 萌新妹子の項目初體驗

cover.png

通過一個月的時間,在我遇到了不少「這個我不會作啊?」,「這個到底怎麼作「的問題後,它終於成功上線了!下面總結一下整整一個月的時間我是如何開發 JDRD,遇到的各類問題以及解決方案。

JDR DESIGN 是京東零售設計中臺的門戶站點,展現京東零售設計服務平臺的產品以及應用場景,特色是動效豐富、圖片細節多、要求整站文案和外鏈可配置。項目最大的困難就是動效開發複雜和開發排期緊湊的問題。css

這是我入職以來負責的第一個項目,須要花大量時間來熟悉新的開發流程,項目排期很是緊湊,而且在排期完後又新增了窄版、骨架屏、首頁圖標動效、入場動畫、產品頁頭部動效等新的需求,天天高強度的加班,回想起來雖然很難,可是很是有挑戰性,很是有收穫。html

首頁

場景頁

項目架構

技術選型

做爲一個 9012 年的 PC 端項目,咱們天然也須要很是先進的技術選型來幫助咱們提高研發生產力,因此一個優秀的前端框架和一個高效的前端工程化工具,天然是必不可缺的選擇。選擇團隊自研的 Nerv 進行開發,Nerv 是一個基於 Virtual DOM 的類 React 組件框架,比 React 更小的體積更高的性能,還保持了對 IE 瀏覽器的兼容,知足了 JDRD 須要兼容 IE10 的要求。前端

自動化前端構建工具選擇了團隊自研的前端工程化工具 Athena,簡化 Webpack 配置工做,幫助咱們在項目中實現自動化編譯、代碼處理、依賴分析、文件壓縮、文件 MD5 戳等需求。webpack

項目總體架構

在前端架構方面,根據上述的技術選型以及常見的前端體系,基於本項目的需求進行了些調整,總體架構設計以下:css3

圖片

Athena 和 Nerv 上文已經介紹過,下面介紹一下另外幾個:git

  • 通用工具庫:基於以往的項目創建的公共函數庫,包括 Slider、Lazyimg、Lazyload、Nerv-loadable 等。
  • NEOS 管理平臺:將整站的文案和外鏈數據放在 NEOS 平臺進行管理,簡化文案修改等工做。
  • 兜底展現:github

    • 在請求到錯誤連接時重定向到 error.html 頁面
    • 圖片加載失敗時展現兜底圖
  • MTA 數據分析:將網站的全部點擊事件添加埋點進行數據上報,根據實時數據統計分析服務,監控版本質量、渠道情況、用戶畫像屬性及用戶細分行爲。

開發過程

整個開發流程總結以下圖所示:web

圖片

既然是總結,開發過程當中的流程固然沒有這麼完整,漏掉了一部分(已紅色標註),致使開發到後面由於前面漏掉的環節,浪費了不少的時間。npm

  • 整站設計規範:因爲開發和設計是同時進行的,開始開發時只有首頁定稿,沒有其餘頁面的設計稿,以及窄版的規範,咱們應該在開發前和設計師明確整站的設計規範,根據設計規範創建通用樣式表,整站引入。
  • 頁面 & 樓層結構:樓層的結構設計對於後續作樓層懶加載很是重要,根據設計稿的功能結構區分樓層。
  • 兼容 IE:JDRD 是須要兼容 IE 的,一些不兼容 IE 的 API 和樣式屬性應該儘可能避免使用。
  • 骨架屏:骨架屏的高度和樓層間隔和內容是一致的,應該創建公共類,定義骨架屏和內容一致的樣式,在後續調整內容時,不須要再去調整一遍骨架屏的樣式。

數據埋點:交互稿其實有詳細介紹哪些地方須要點擊跳轉的事件,開發時應該在定義點擊跳轉時就給數據埋點傳參。json

除了在開發流程中的問題外,最頭疼的就是動效的開發了,下文會詳細介紹。

項目優化

性能優化

性能優化的初衷就是加快網站的加載速度,讓用戶可以更快的看到內容,上面介紹到前端工程化工具 Athena 已經作到合併、壓縮了靜態資源文件,那還有什麼方法可以縮小請求的靜態資源體積,加快首屏的加載速度呢,咱們嘗試瞭如下性能優化手段。

樓層懶加載

樓層懶加載就是按樓層劃分組件,並進行代碼切割,在頁面滾動時按需加載組件。

Nerv-loadable 是一個專門用於動態 import 的 React 高階組件,你能夠把任何組件改寫爲支持動態 import 的形式,利用 import() 來進行動態加載。

const NewsBannerLoadable = Loadable({
  loader: () =>
    import(/* webpackChunkName: "news_banner" */
    './news_banner'),
  loading: loadingPlaceholder.bind(null, loadingBlock),
  delay: 0
});

上面的代碼在首次加載時,會先展現一個 loadingBlock,而後動態加載 news_banner 的代碼,組件代碼加載完畢以後,便會替換掉 loadingBlock
Lazyload 經過監聽 window 對象或者父級對象的 scroll 事件,觸發 load,實現懶加載,讓組件進入頁面可視區時才加載該組件。須要注意的是 lazyload 須要設置高度,纔會撐起懶加載的區域。

<Lazyload {...this.lazyloadOptions} height={this.floorHeight.newsBanner}>
    <NewsBannerLoadable />
</Lazyload>

以首頁爲例,有四處組件是不須要首次加載的,而是使用動態加載:多端適配、物料、應用場景、設計思考。首次加載實際上只須要加載首屏的頭部、視頻、banner 便可。切分以後,首屏 js 體積縮減了 50KB。
圖片

圖片懶加載

整站圖片很是多,爲了保持清晰度並且所有采用二倍圖引入,消耗資源比較大,爲了加快加載速度,咱們選擇讓滾動條滾動到圖片的可視區後才加載該圖片。

使用 Lazyload 實現,和上述組件懶加載介紹的同樣,<Lazyload></Lazyload> 包裹着須要懶加載的圖片,就能夠實現圖片懶加載。

圖片懶加載以外還有個優化,就是圖片加載中、加載失敗、加載成功的狀態的判斷,根據不一樣狀態展現圖片的內容。

使用 Lazyimg 實現這個功能:

  • 使用 new Image() 建立一個新的 HTMLImageElement 實例
  • img.onload()img.onerror() 捕獲到圖片加載成功或者失敗的狀態

    • 加載中:顯示兜底圖
    • 加載成功:顯示加載到的圖片
    • 加載失敗:顯示兜底圖

圖片

其餘優化手段

除了上面兩點外,還能夠從 Webpack 打包進行性能優化,Webpack 打包後會生成一個或多個包含源代碼最終版本的「打包好的文件」,它們由 chunks 組成,SplitChunks 插件能夠將公共依賴項提取到現有的 entry chunk 或全新的代碼塊中,進行代碼切割,減少 chunks 包的大小。

體驗優化

骨架屏

以往的傳統網站通常會在加載中展現一個 loading 態,也能夠達到佔位的效果,可是 loading 動畫和真實模塊耦合度低,界面效果不夠優美,JDRD 則是選用骨架屏進行佔位,以灰色豆腐塊的形式儘可能縮小真實模塊結構與加載佔位之間的視覺差別。

骨架屏的兩個用途:

  • 組件加載完以前的佔位

使用 Lazyload 懶加載樓層組件,加載中使用 Loadable 提早佔位,佔位符設置爲骨架屏。

  • 數據加載完以前的佔位

設置組件的 state.loaded 初始值爲 false ,數據加載成功時 state.loaded = truerender 函數裏若是 loaded === false ,則顯示骨架屏。

骨架屏的實現方式有兩種,一是下載並引入骨架屏插件(如 antd ),根據不一樣模塊引入對應的骨架屏組件,這種方式和 loading 動畫同樣,耦合度低,可是全局通用,節省代碼量。二是根據視覺稿寫骨架屏的樣式。JDRD 選擇的是第二種,骨架屏和真實模塊實現高度耦合。每一個頁面結構不同,對應的骨架屏也是徹底不一樣,骨架屏暫時不能抽成公共組件全局通用。

圖片

寬窄版

首頁定稿設定的寬度爲 1240px ,對小屏不夠友好,咱們增長了一版窄版樣式兼容小屏。

寬版和窄版開發的重點是定好通用的變量,包括字號粗細、寬窄版寬度、窄版尺寸比。這些通用樣式規範須要和設計師統一規範,兼容窄版的開發就會變得很是簡單。

只須要在兩個地方判斷寬窄版,給最頂層的標籤加上 wide/narraw 類,在 narrow 下添加窄版的自定義樣式。

  1. 在頁面剛加載到時判斷寬窄版,在加載到樣式表以前給 html 標籤添加 wide/narraw
!function(e) {
  window.pageConfig = {};
  pageConfig.isWide = function() {
  var n = e,
  i = document,
  o = i.documentElement,
  t = i.getElementsByTagName("body")[0],
  a = n.innerWidth || o.clientWidth || t.clientWidth;
  return a >= 1300
  } ();
  var n = [];
  pageConfig.isWide ? (n.push("wide")) : n.push("narrow");
  var i = document.getElementsByTagName("html")[0];
  i.className = n.join(" ")
} (window, void 0);
  1. 文檔視圖調整大小時判斷寬窄版,修改 HTML 標籤的 className

先引入 Events.js ,而後在 componentDidMount 裏生命週期函數裏綁定 isWideChange 事件,在文檔視圖寬度達到寬窄版臨界點時調用。

componentDidMount() {
  window._.eventCenter.on('isWideChange', evt => {
    this.setState({//更新state,更新視圖
      isWide: evt.detail.isWide
    });
  });
}

動效開發

首頁圖標動效

爲了突出設計理念,首頁圖標動效包含大量位移、旋轉、縮放、形變、路徑動畫等細節,由始末動畫+循環動畫合成,傳統作法是 CSS3 實現,這須要逐幀寫動畫細節,工做量很是大,咱們嘗試使用 Lottie 直接解析從 AE 導出的 json 格式的動畫(方案由燕婷提出),發現可以徹底還原AE動畫。

Lottie 是 Airbnb 開源的一套跨平臺的完整的動畫效果解決方案,可實時渲染 After Effects 動畫,從而使應用程序能夠像使用靜態圖像同樣輕鬆地使用動畫。這樣實現起來就很是簡單了。分如下兩步:

  1. 在 AE 軟件中用 bodymovin 插件將動畫導出爲 json 文件
  2. 在項目中使用 lottie-web 將 json 格式的動畫解析爲 SVG(使用文檔

lottie-web 文檔中的方法很是全面,JDRD 圖標動效使用加載動畫、播放指定幀區間、反向播放動畫方法,就實現了起始動畫 20 幀 + 循環動畫 60 幀 + 起始動畫反向播放 20 幀的動畫合成操做。

項目示例代碼以下:

npm install lottie-web //安裝lottie-web
import lottie from 'lottie-web' //引入lottie-web到項目中
//lottie-web經常使用方法
this.anim = lottie.loadAnimation({ //加載動畫
  container: element, 
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: 'data.json'
});
this.anim.playSegments([[0,60]], true); //播放指定幀區間
this.anim.setDirection(-1);//動畫反向播放
this.anim.play();//播放動畫
this.anim.pause()//暫停動畫

通過以上兩步,Lottie 已經將一個 AE 格式的動畫渲染在 Web 頁面上。
圖片

圖片

圖片

這裏有 2 個須要注意的點:

  1. json 文件的引入要使用 CDN,引入本地 json 文件會解析失敗。
  2. 若是動畫源文件中有引入圖片文件,bodymovin 導出的動畫爲 json+img。圖片動畫的兼容性有待確認。

以上就是用 Lottie 實現的動畫,看到這裏,是否是以爲 so easy,可是 Lottie 並非萬能的,不能解析全部的動畫特性,開發前須要先看下支持列表。並和設計師確認是否都支持。

入場動畫

JDRD 整站採用了骨架屏佔位,那麼入場動畫最大的問題就是如何讓它不和骨架屏衝突,解決方法就是樓層懶加載裏面,再加一層入場動畫的組件懶加載,兩層懶加載的設置 offset 差,就能夠作到在可視區外加載樓層組件,在可視區內播放入場動畫。具體實現以下:

  1. 樓層懶加載,在頁面滾動時按需加載樓層組件
// 在距離底部200px時,加載樓層組件
getMaterialLoadable(){
  return this.getFloor(
    <Lazyload lazyloadOptions={offset: 200} height={1000}>
      <MaterialLoadable />
    </Lazyload>
  );
}
  1. 樓層中的入場動畫組件懶加載
// 在距離底部-200px時,加載入場動畫組件,這時由於樓層組件已經加載過了,頁面顯示是真實組件而不是骨架屏
<Lazyload lazyloadOptions={offset: -200}>
  <div className="w">
    <IndexTitle showLine={true} title={this.state.title}></IndexTitle>
    {this.renderMaterial()}
  </div>
</Lazyload>

另一個難點是序列動畫的效果,序列動畫就是將列表元素的動畫執行時機錯開,具體實現參考css3 animation 屬性衆妙。實現代碼以下:

@for $i from 1 to 6 {
  .list__item:nth-child(#{$i}) {
    animation-delay: (-1+$i)*0.1s; /*計算每一個元素的 animation-delay */
  }
}

入場動畫

產品頁頭部動效

產品頁的頭部動效分兩部分,氛圍動效 + 波浪動效。

  • 氛圍動效

氛圍動效的實現比較簡單,也是使用 Lottie 實現,這裏遇到了 Lottie 不支持的特性,就是漸變,對於不支持的特性,咱們能夠拿到須要自定義樣式的標籤的 id,自定義樣式,如圖所示:

圖片

#__lottie_element_369 {
  stop[offset="0%"] {
    stop-color:  #FDFDFF;
  }
  stop[offset="100%"] {
    stop-color: #F7F7FB;
  }
}

自定義樣式的時候,這裏有個坑必定要注意,SVG 的 ID 是會變的,例如開發時,這個 ID 是 100,測試時這個 ID 有可能變成 101,這個是偶現的,目前尚未找到 ID 值 + 1 的緣由,可是爲了讓自定義的樣式生效,須要給 ID 爲 100 和 ID 爲 101 的標籤都加上樣式。

圖片

  • 波浪動效

經過引入正弦波浪動效庫 sine_wave 實現。sine_wave 使用 canvas 元素生成多個可配置的正弦波,這樣咱們就能夠經過配置參數獲得想要的正弦波浪,具體實現以下:

new SineWaves({
  el: document.getElementById( `waves`),//dom
  speed: 0.75,//速度
  width: function() {//canvas寬度
    return document.body.clientWidth;
  },
  height: 68,//canvas高度
  ease: 'Linear',//動畫曲線
  waves: [//須要配置的正弦波浪
    {
      "timeModifier":1,//速度
      "lineWidth":1,//線條寬度
      "amplitude":30 * window.devicePixelRatio,//波浪高度
      "wavelength":125 * window.devicePixelRatio,//波長
      "strokeStyle":"rgba(221,221,233,1)",//顏色
      "type": function(x, waves) {//自定義波浪類型
        return waves.sine(x); // Combine two together
      }
    }
  ], 
  rotate: 0,//旋轉角度
  wavesWidth: '400%',//波浪寬度
});

根據文檔介紹的參數來看,有兩個參數是實現 3D 旋轉波浪效果的關鍵:

  1. type,自定義波浪的類型,能夠修改 x 軸的位移,讓 3 根波浪錯位,實現旋轉的效果。
type: function(x, waves) {
  return waves.sine(x+8); // Combine two together
}
  1. wavelength,波浪長度,3 根弧度如出一轍的波浪明顯是不符合要求的,只須要將紅色的波浪的弧長加長,就能夠實現一根波浪環繞另外兩根波浪的效果。

開發過程當中發現 sine_waves 的一個顯示問題,它以默認二倍屏的方式定義的 canvas,這樣在一倍屏下波浪是有問題的,解決方法是在參數波浪高度 amplitude 和波長 wavelength 根據 window.devicePixelRatio 來定義。

{
  "amplitude":30 * window.devicePixelRatio,//波浪高度
   "wavelength":125 * window.devicePixelRatio,//波長
}

全局細節動效

最後介紹的就是全局細節動效的優化,爲了讓整站的動效流暢,平滑的過渡,作了如下工做:

  • 用 SASS 變量 $common_animation 管理通用動畫,讓整站動畫一致化
  • 圖標動效和邊框動效採用 SVG 實現,實現動畫組件化的同時,矢量元素不失真
  • 須要過渡的元素,用 visibility 代替 display 控制元素的顯示隱藏

圖片

圖片

圖片

總體總結

本文從項目架構、開發流程、項目優化 3 個方面闡述 JDRD 官網的開發過程,中間遇到了太多問題,在問題的解決過程當中記錄和總結,是收穫滿滿的喜悅,也發現了一些能夠優化的模塊,讓下次可以作得更好,在開發過程當中的多些思考和探究,最優化的設計項目。

感謝閱讀!

歡迎關注凹凸實驗室博客:aotu.io

或者關注凹凸實驗室公衆號(AOTULabs),不定時推送文章:

image

相關文章
相關標籤/搜索