又雙叒叕是一個動態簡歷

先看效果

請戳這裏看預覽css

這裏是代碼html

見過了?別走,這是不同凡響的地方

  • 針對移動端優化了體驗
  • 支持動畫跳過
  • 支持多段動畫
  • 標點字符特殊處理,停留時間略長於字符時間
  • typescript 編寫
  • 對功能進行了封裝處理,能夠直接引入使用

基本準備

字符逐個彈出效果的實現

原理很簡單,一個閉包,逐一截取字符串,setTimeout 渲染在頁面上便可前端

/** * @param {HTMLElement} container - 渲染字符的容器 * @param {string} text - 須要渲染的字符串 */
function loadItem(container, text) {
  let num = 0
  let sum = text.length
  let interval = 16

  const startLoad = () => {
    setTimeout(() => {
      num += 1
      if (num <= sum) {

        let str = text.substr(0, num)

        container.scrollTop = 100000

        container.innerHTML = str

        setTimeout(() => {
          startLoad()
        }, interval)

      }
    }, interval)
  }

  startLoad()
}
複製代碼

html 上的 CSS 字符自動生效

只要在字符串開始渲染時,在 html 中添加一個 style 標籤,將渲染的 CSS 代碼寫入到標籤中便可git

建立一個 style 標籤github

function getStyleEl() {
  let newStyle = document.createElement('style')
  let head = document.querySelector('head')
  head.appendChild(newStyle)
  let allStyle = document.querySelectorAll('style')

  return allStyle[allStyle.length - 1]
}
複製代碼

CSS 代碼寫入面試

/** * * @param {string} style - CSS 代碼 * @param {HTMLElement} el - 建立的 style 標籤 */
function handleStyle(style, el) {
  el.innerHTML = style
}
複製代碼

CSS 代碼高亮,markdown 自動轉換

這裏須要藉助 prismjsmarked 兩個代碼處理庫(固然也能夠用其餘的)typescript

須要在上述的 loadTtem 函數中添加判斷數組

let code
switch (type) {
  case 'css':
    handleStyle(str, styleEl)
    code = Prism.highlight(str, Prism.languages.css)
    break
  case 'md':
    code = marked(str)
    break
}
複製代碼

進階處理

分析

基本的核心功能已經準備好了markdown

下面咱們開始分析過程,開始編寫代碼閉包

需求以下:

  1. 支持多段動畫加載
  2. 支持動畫跳過(直接加載完成)
  3. 移動端特殊處理

基於上述需求,咱們須要先對接口進行定義

咱們設想函數是這樣使用的

/** * @param {HTMLElement} container - 字符渲染的容器 * @param {Object} options - 動畫參數 * * @param {string} options.content.load - 須要渲染的字符串 * @param {'css' | 'md'} options.content.type - 渲染後高亮的方式,當前僅支持 'css' | 'md' 兩個參數 * @param {string} options.content.id - 渲染容器的 id * @param {boolean} options.content.rewrite - 是否須要重寫 * * @param {Object}? options.mobileAnimate - 移動端須要特殊處理 * @param {string} options.mobileAnimate.styleID - css 加載的容器 ,id 應與 content 中 css 容器的 id 相同 * @param {string} options.mobileAnimate.string - markdown 加載的容器,id 應與 content 中 md 容器的 id 相同 */
let ar = new AnimateResume(container, {
  content:[
    {
      load:'',
      type:'css',
      id:'',
      rewrite:'',
    },
    ...
  ],
  mobileAnimate:{
    styleID:'',
    resumeID:''
  }
})
ar.animate()
ar.skip()
複製代碼

使用前須要實例化一個並傳入參數,經過 animate 方法開始動畫,skip 方法跳過動畫

根據上述參數設想,咱們能夠寫出以下的 typescript 接口,不瞭解 typescript 的同窗能夠直接跳過,只看上面代碼的註釋便可

interface Core {
  container: Element
  options: CoreOptions
  isSkip: boolean

  animate: () => void
  skip: () => void
}

interface CoreOptions {
  content: Array<LoadParams>
  mobileAnimate?: {
    styleID: string
    resumeID: string
  }
}

interface LoadParams {
  load: string
  type: 'css' | 'md'
  id: string
  rewrite?: boolean
}
複製代碼

實現

基本的架構已經分析好了,如今能夠開始實現了

逐一加載

首先,由於動畫是多段完成的,因此咱們經過參數 content 傳入的是一個二維數組,其中每一個 item 存放着咱們想要加載的內容和對應要求,如何讓動畫一段一段的完成呢?很天然的能想到 Promise 方法,經過 Promise.then() 來實現。

因此咱們能夠將這個需求抽象爲:一個未知長度的數組,須要逐一的在未知時間後加載下一項。

實現也很簡單,代碼以下:

function load(contents) {
  if (contents.length) {
    this.loadItem(contents[0])
      .then(() => this.load(contents.slice(1)))
  }
}
複製代碼

能夠想到,上述中的 loadItem 方法應該返回一個 new Promise ,內部當字符串加載完成後返回 resolve(),而後繼續執行下一段 load 方法

支持跳過

如何才能中斷當前的動畫,直接加載完成呢?

最初我嘗試直接暴力的經過在 loadItem 時檢查加載字數和一個全局變量來判斷是否 setTimeout, 但很明顯這麼作及其不優雅,並且有 bug(但我忘了是什麼 bug 了...)。

優雅實現:在類中聲明 this.isSkip = false(至關於全局變量),在 skip() 方法調用時,將其改變爲 true,在 loadItemsetTimeout 前檢查該變量,若是爲 true 則拋出 reject()

因此上述的 load 方法須要添加變爲:

function load(contents) {
  if (contents.length) {
    this.loadItem(contents[0])
      .then(() => this.load(contents.slice(1)))
      .catch(() => this.skipAnimate())
  }
}
複製代碼

skipAnimate 即爲對應的跳過動畫方法

移動端處理

沒有動圖…… 請點擊預覽在手機或者谷歌調試中自行查看

展現樣式咱們能夠直接在渲染的 CSS 代碼動畫中自定義,因此不過多解釋

這裏只說一下兩個頁面上下滑動的效果實現

咱們須要藉助 better-scroll 插件來幫助優化,分別設置上部分頁面上拉刷新事件和下部分頁面的下拉刷新事件,在對應事件觸發時,經過 transform:translateY(x) 來實現頁面的總體滑動,代碼以下

let styleScroll = new BScroll(styleContainer, {
    pullUpLoad: {
      threshold: 20
    }
  })

  let mdScroll = new BScroll(mdContainer, {
    pullDownRefresh: {
      threshold: 20,
    }
  })

  styleScroll.on('pullingUp', function () {
    mdContainer.style.transform = 'translateY(calc(-100% - 4rem))'
    styleContainer.style.transform = 'translateY(calc(-100% - 1rem))'
    styleScroll.finishPullUp()
  })
  mdScroll.on('pullingDown', function () {
    mdContainer.style.transform = 'translateY(0)'
    styleContainer.style.transform = 'translateY(0)'
    mdScroll.finishPullDown()
  })
複製代碼

須要注意的是若是下方簡歷內容長度不夠,不會觸發 better-scroll 的滑動檢測,致使沒法出現預想的滑動效果。

標點處理

根據傳入的字符來判斷下一個字符出現的延遲時間,即 setTimeout 方法的第二個參數。

function getInterval(str: string, interval = 16): number {
  if (/\D[\,]\s$/.test(str)) return interval * 20
  if (/[^\/]\n\n$/.test(str)) return interval * 40
  if (/[\.\?\!]\s$/.test(str)) return interval * 60
  return 0
}
複製代碼

參考自 github.com/STRML/strml… ,算是拾人牙慧了。

結束

基本的實現思路已經說完,具體的代碼貼上來實在是篇幅太長,請查看源碼

不瞭解 typescript 的同窗能夠看這裏,這是我年初時用 js 寫的,不過算是面向過程編寫,沒有作過多的封裝處理。

寫在最後

第一次見到strml.net/ 時,是在初學前端大概三四個月的樣子,當時看到這樣的展示形式着實是被驚豔到了,那時仍是個小白,連 highlight 這樣的插件都不知道,更不知道還能在style裏自定義東西,更更更不知道網站下面就放着View Source這麼個大字,只是一心想的要本身也寫一個,就那麼硬生生本身寫正則,經過不一樣的特殊符號加載對應的標籤處理變色,再經過 `dom.style....=...' 設置樣式,而後竟然還寫的有模有樣,第一次找工做時還竟然敢拿出來給面試官看了(笑)。

年初的時候試着重寫了這個項目,感受已經是沒有什麼難度了,不過也是面向過程,一頓操做罷了。這些天初學 typescript 想着拿個什麼東西練個手,因此又把這個項目用 ts 重構了,而且進一步的進行了封裝。感受能夠出來溜溜了,因此寫下了這篇文章。

相關文章
相關標籤/搜索