requestAnimationFrame實現(xiàn)jquery上拉下拉動畫

使用requestAnimationFrame代替setInterval和setTimeout

優(yōu)勢與特點:

1)requestAnimationFrame會把每一幀中的所有DOM操作集中起來淑际,在一次重繪或回流中就完成满败,并且重繪或回流的時間間隔緊緊跟隨瀏覽器的刷新頻率

2)在隱藏或不可見的元素中鳄乏,requestAnimationFrame將不會進行重繪或回流爽篷,這當(dāng)然就意味著更少的CPU魄揉、GPU和內(nèi)存使用量

3)requestAnimationFrame是由瀏覽器專門為動畫提供的API桶癣,在運行時瀏覽器會自動優(yōu)化方法的調(diào)用焚鲜,并且如果頁面不是激活狀態(tài)下的話紊册,動畫會自動暫停,有效節(jié)省了CPU開銷

jquery slideToggle函數(shù)接受兩個參數(shù)男翰,第一個參數(shù)代表動畫時間另患,第二個為回調(diào)函數(shù)
本代碼實現(xiàn)的slideToggle函數(shù)接受三個參數(shù),第一個參數(shù)為操作的dom元素蛾绎,第二個代表動畫時間昆箕,單位ms,第三個為動畫結(jié)束時的回調(diào)函數(shù)

原生實現(xiàn)slideToggle租冠,代碼如下:

/**
 * 獲取元素實際樣式值
 * @param el dom元素
 * @param styleName 樣式名
 */
function getStyle(el, styleName) {
  if (el.currentStyle) return el.currentStyle[styleName];
  if (getComputedStyle) return window.getComputedStyle(el, null)[styleName];
  return el.style[styleName];
}

// 優(yōu)雅降級requestAnimationFrame
const requestAnimationF = (function () {
  return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    // if all else fails, use setTimeout
    function (callback) {
      return window.setTimeout(callback, 1000 / 60); // shoot for 60 fps
    };
})();

// 優(yōu)雅降級cancelAnimationFrame
const cancelAnimationF = (function () {
  return window.cancelAnimationFrame ||
    window.webkitCancelAnimationFrame ||
    window.mozCancelAnimationFrame ||
    window.oCancelAnimationFrame ||
    function (id) {
      window.clearTimeout(id);
    };
})();


/**
 * 獲取元素實際高度
 * @param el  dom元素
 * @returns {number} 元素高度
 */
function getHeight(el) {
  let height;
  // 已隱藏的元素
  if (getStyle(el, "display") === "none") {
    el.style.position = "absolute";
    el.style.visibility = "hidden";
    el.style.display = "block";
    height = getStyle(el, "height");
    el.style.position = "";
    el.style.visibility = "";
    el.style.display = "";
    return parseFloat(height);
  }
  return parseFloat(getStyle(el, "height"));
}

/**
 * 獲取已隱藏元素的css值
 * @param el
 * @param styleName
 * @returns {*}
 */
function getCurrentStyle(el, styleName) {
  let styleValue;
  // 已隱藏的元素
  if (getStyle(el, "display") === "none") {
    el.style.position = "absolute";
    el.style.visibility = "hidden";
    el.style.display = "block";
    styleValue = getStyle(el, styleName);
    el.style.position = "";
    el.style.visibility = "";
    el.style.display = "";
    return (styleValue);
  }
  return (getStyle(el, styleName));
}

/**
 * 優(yōu)化實現(xiàn)sildeToggle效果
 * @param el dom元素
 * @param time 動畫時長鹏倘,單位ms,默認值300
 * @param fn 回調(diào)函數(shù)
 */
function slideToggle(el, time, fn) {
  if (!el) return false;
  time = time || 300;
  if (el.dataUid) return false; // 如該dom元素已有動畫未處理完顽爹,則必須等到動畫結(jié)束才執(zhí)行

  cancelAnimationF(el.dataUid);

  // 已隱藏的元素纤泵,下拉
  if (getStyle(el, "display") === "none" || getHeight(el) === 0) {
    let aniSplitTime = Date.now();

    let height = 0, paddingTop = 0, paddingBottom = 0;
    let totalHeight = parseFloat(getCurrentStyle(el, "height"));
    let totalPaddingTop = parseFloat(getCurrentStyle(el, "paddingTop"));
    let totalPaddingBottom = parseFloat(getCurrentStyle(el, "paddingBottom"));

    let basePaddingBottom = totalPaddingBottom/time;
    let basePaddingTop = totalPaddingBottom/time;
    let baseHeight = totalHeight/time;

    el.style.overflow = "hidden";
    el.style.display = "block";

    el.dataUid = requestAnimationF(function go(){
      let aniTime = Date.now();
      let splitTime = aniTime - aniSplitTime;
      aniSplitTime = aniTime;
      let splitPaddingBottom = basePaddingBottom*splitTime;
      let splitPaddingTop = basePaddingTop*splitTime;
      let splitHeight = baseHeight*splitTime;

      if (height >= totalHeight){
        el.style.overflow = "";
        el.style.height = "";
        el.style.paddingTop = "";
        el.style.paddingBottom = "";

        if (fn && typeof fn === "function") fn();
        cancelAnimationF(el.dataUid);
        el.dataUid = null;
        delete el.dataUid;
      } else {
        el.style.height = height + "px";
        el.style.paddingTop = paddingTop + "px";
        el.style.paddingBottom = paddingBottom + "px";
        el.dataUid = requestAnimationF(go);
      }
      height = height + splitHeight;
      paddingTop = paddingTop + splitPaddingTop;
      paddingBottom = paddingBottom + splitPaddingBottom;
    });

  } else {
    // 上拉
    let aniSplitTime = Date.now();
    let height = getHeight(el);
    let paddingTop = parseFloat(getStyle(el, "paddingTop"));
    let paddingBottom = parseFloat(getStyle(el, "paddingBottom"));
    el.style.overflow = "hidden";

    let basePaddingBottom = paddingBottom/time;
    let basePaddingTop = paddingTop/time;
    let baseHeight = height/time;

    el.dataUid = requestAnimationF(function go(){
      let aniTime = Date.now();
      let splitTime = aniTime - aniSplitTime;
      aniSplitTime = aniTime;
      let splitPaddingBottom = basePaddingBottom*splitTime;
      let splitPaddingTop = basePaddingTop*splitTime;
      let splitHeight = baseHeight*splitTime;

      if (height <= 0) {
        el.style.height = 0;
        el.style.paddingTop = 0;
        el.style.paddingBottom = 0;
    
        setTimeout(()=>{
          el.style.height = "";
          el.style.overflow = "";
          el.style.paddingTop = "";
          el.style.paddingBottom = "";
          el.style.display = "none";
        },0);

        if (fn && typeof fn === "function") fn();
        cancelAnimationF(el.dataUid);
        el.dataUid = null;
        delete el.dataUid;
      } else {
        el.style.height = height + "px";
        el.style.paddingTop = paddingTop + "px";
        el.style.paddingBottom = paddingBottom + "px";
        el.dataUid = requestAnimationF(go);
      }

      height = height - splitHeight;
      paddingBottom = paddingBottom - splitPaddingBottom;
      paddingTop = paddingTop - splitPaddingTop;
    });
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市镜粤,隨后出現(xiàn)的幾起案子捏题,更是在濱河造成了極大的恐慌玻褪,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涉馅,死亡現(xiàn)場離奇詭異归园,居然都是意外死亡,警方通過查閱死者的電腦和手機稚矿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門庸诱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晤揣,你說我怎么就攤上這事桥爽。” “怎么了昧识?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵钠四,是天一觀的道長。 經(jīng)常有香客問我跪楞,道長缀去,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任甸祭,我火速辦了婚禮缕碎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘池户。我一直安慰自己咏雌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布校焦。 她就那樣靜靜地躺著赊抖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寨典。 梳的紋絲不亂的頭發(fā)上氛雪,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音耸成,去河邊找鬼报亩。 笑死,一個胖子當(dāng)著我的面吹牛墓猎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赚楚,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼毙沾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宠页?” 一聲冷哼從身側(cè)響起左胞,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤寇仓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后烤宙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遍烦,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年躺枕,在試婚紗的時候發(fā)現(xiàn)自己被綠了服猪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡拐云,死狀恐怖罢猪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叉瘩,我是刑警寧澤膳帕,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站薇缅,受9級特大地震影響危彩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泳桦,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一汤徽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓬痒,春花似錦泻骤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亲轨,卻和暖如春趋惨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惦蚊。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工器虾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹦锋。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓兆沙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親莉掂。 傳聞我的和親對象是個殘疾皇子葛圃,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345