摘要:本文介紹一個簡單的類似的布局組件的實現,基于。介紹的內容已經制作成組件。即當不可以拖出抽屜時,應觸發默認事件,比如垂直方向的滾動等等。這種優化可以將一部分復雜的計算工作提前準備好,使頁面的反應更為快速靈敏。
本文介紹一個簡單的DrawerLayout(類似Android的DrawerLayout)布局組件的實現,基于Vue.js。介紹的內容已經制作成 vue-drawer-layout 組件。前言
大家有興趣先用手機掃一掃這個二維碼,或者點我
然后點擊頁面中左上角的頭像打開drawer或者向右向左拖拽,就可以看到下面gif的效果,打開自己的手機QQ,是不是很像:)
谷歌官方把這種布局叫做DrawerLayout(抽屜式導航欄)。那么我們要如何實現呢,好了正片開始!
HTML結構頁面結構很簡單,一個抽屜,一個主容器,內容可以利用slot支持外部自行定制。
抽屜一開始是隱藏在左側屏幕外的,故設置left:-100%使其整個都藏在外部
使用Touch首先,判斷瀏覽器是否支持touchEvent
let isTouch = "ontouchstart" in window; let mouseEvents = isTouch ? { down: "touchstart", move: "touchmove", up: "touchend", over: "touchstart", out: "touchend" } : { down: "mousedown", move: "mousemove", up: "mouseup", over: "mouseover", out: "mouseout" };
綁定touchdown事件
document.addEventListener(mouseEvents.down, initDrag, false);
先定義一些變量,手指按下的x坐標記為startX,滑動中手指的位置x坐標記為nowX,drawer的x坐標偏移量記為startPos
let startX, nowX, startPos;
觸發touchstart時,記錄起始位置并綁定touchmove,注意:如果是mouseEvent,通過e.clientX來獲取當前的x坐標,如果是touchEvent,要通過e.changedTouches[0].clientX來獲取x坐標
const initDrag = function (e) { startX = e.clientX || e.changedTouches[0].clientX; //記錄手指按下的位置 startPos = this.pos; //記錄drawer的上次位置 document.addEventListener(mouseEvents.move, drag, false); document.addEventListener(mouseEvents.up, removeDrag, false); }.bind(this);
const drag = function (e) { nowX = e.clientX || e.changedTouches[0].clientX; //滑動中手指的位置x坐標 let pos = startPos + nowX - startX; pos = Math.min(width, pos); //不能超過滑動最大值 pos = Math.max(0, pos); //不能小于0 this.pos = pos; //設置滾動距離為拖動的距離 }.bind(this);
那么,手指滑動的距離就是nowX - startX,當前drawer的位置為startPos + nowX - startX,這樣抽屜已經跟隨手指向右移動了,并且不會超過我們設置的拖動最大值。
區分垂直滑動和水平滑動接下來你會發現一個問題,當手指垂直滾動主內容時,向右滑動手指也會拖出抽屜,這時應該做一件事:區分垂直滑動和水平滑動
當然,辦法有很多,這里先介紹一種利用三角函數來判定的方法
假設,上圖中的每個箭頭是手指滑動的方向,綠色箭頭代表可以拖出抽屜,紅色箭頭代表不可以拖出(注意,紅色箭頭也是有x坐標的偏移量的)。即當不可以拖出抽屜時,應觸發默認事件,比如垂直方向的滾動等等。
當手指按下觸發touchstart時,記錄初始位置P0;當滑動手指時,觸發的第一次touchmove時,記錄位置P1,我們將P0到P1的矢量記為S(原諒我這個靈魂畫手)
這時候很容易看出,∠θ大于某個值時,比如30度,就可能是垂直方向的滾動操作而不是拖動抽屜。所以,可以根據y/x>tan30°得到判斷條件:
if (isVerticle === undefined) isVerticle = Math.abs(nowY - startY) / Math.abs(nowX - startX) > (Math.sqrt(3) / 3);
當isVerticle為true時,不執行drawer的拖動
讓Drawer動起來我們使用css3的transition屬性使drawer具有過渡動畫效果,這里寫一個moving類
.moving transition transform .3s ease
別忘了加上class綁定,拖動時是不需要過渡動畫的(要跟隨手指),而松開手指時才需要過渡動畫。
所以綁定touchend事件的方法時要做這些步驟
const removeDrag = function (e) { if (isVerticle !== undefined) { if (!isVerticle) {//當判定為抽屜拖動才進入 let pos = this.pos; this.visible = pos > width * 3 / 5 //當前位置如果大于總寬度的3/5就判定為全部展開抽屜,否則將抽屜彈回隱藏 if (this.pos > 0 && this.pos < width) this.moving = true;//如果位置已經處于最小值或最大值處,不需要有動畫效果了 } this.pos = this.visible ? width : 0; } if (!this.moving) { this.willChange = false; //留個懸念 } isVerticle = undefined; //取消touchmove和touchend事件綁定 document.removeEventListener(mouseEvents.move, drag, false); document.removeEventListener(mouseEvents.up, removeDrag, false); }.bind(this);
上面你可能發現代碼里有個this.willChange = false,它是干啥的捏?下面我們請出css的will-change大法
.will-change will-change transform
CSS 屬性 will-change 為web開發者提供了一種告知瀏覽器該元素會有哪些變化的方法,這樣瀏覽器可以在元素屬性真正發生變化之前提前做好對應的優化準備工作。 這種優化可以將一部分復雜的計算工作提前準備好,使頁面的反應更為快速靈敏。
其實是我們在touchstart可以預先告知瀏覽器抽屜可能要發生位移
const initDrag = function (e) { //... this.willChange = true; }.bind(this);
當然最后別忘了在transitionend事件后把transition和will-change去掉,讓瀏覽器歇一會兒~
還有什么可以優化的?上面說的已經基本上把主要功能實現了,但是這其中還有沒有哪里可以優化的?
咦?passive是什么鬼?
網站使用被動事件偵聽器以提升滾動性能,在您的觸摸和滾輪事件偵聽器上設置 passive 選項可提升滾動性能 具體看這里
原來這是現代瀏覽器的一個新特性,我們需要以新的方式來綁定我們的touch事件,當然首先先檢測一下是否支持passive
const supportsPassive = (() => { let supportsPassive = false; try { const opts = Object.defineProperty({}, "passive", { get: function () { supportsPassive = true; } }); window.addEventListener("test", null, opts); } catch (e) { } return supportsPassive; })();
于是我們的綁定事件代碼變成這樣
document.addEventListener(mouseEvents.move, drag, supportsPassive ? {passive: true} : false);
是否有效果呢?有興趣的朋友可以點這里看國外大神的視頻
寫在最后本文介紹了實現抽屜式導航欄的主要過程,詳細代碼已封裝成vue-drawer-layout組件,支持更豐富的定制和使用方式,具體文檔可以訪問我的github或者npm官網檢索。歡迎各位多多提issue,不吝賜教,感謝!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/112911.html
摘要:本文介紹一個簡單的類似的布局組件的實現,基于。介紹的內容已經制作成組件。即當不可以拖出抽屜時,應觸發默認事件,比如垂直方向的滾動等等。這種優化可以將一部分復雜的計算工作提前準備好,使頁面的反應更為快速靈敏。 本文介紹一個簡單的DrawerLayout(類似Android的DrawerLayout)布局組件的實現,基于Vue.js。介紹的內容已經制作成 vue-drawer-layout...
摘要:里邊涉及到的指令是自定義的指令,為了處理移動端的點擊操作,我還整理了一片陋文移動點擊長按滑動指令然后這個組件的源碼我放在了我出來的項目上謝謝各位品嘗, 以下這段都是廢話,請跳過 公司移動端開發平臺進行了大變革,前端架構由DCloud大生態轉換為VUE,所以移動端的UI組件庫從MUI改為使用MintUI,然后開始大刀闊斧的把MintUI組件改成MUI組件的樣子,然后發現少了幾個較為常用的...
摘要:原文匠心打造簽名組件導讀月又是項目吃緊的時候,一大波需求襲來,猝不及防。可以先戳這里體驗把后面將要提到的簽名組件。剩下的也是綁定事件中關鍵的一步。設置完成了上述功能,一個簽名插件就已經成型了。 本文首發于CSDN網站,下面的版本又經過進一步的修訂。原文:匠心打造canvas簽名組件 導讀 6月又是項目吃緊的時候,一大波需求襲來,猝不及防。 度過了漫長而煎熬的6月,是時候總結一波。最近移...
閱讀 849·2021-11-16 11:56
閱讀 1666·2021-11-16 11:45
閱讀 3116·2021-10-08 10:13
閱讀 4107·2021-09-22 15:27
閱讀 730·2019-08-30 11:03
閱讀 646·2019-08-30 10:56
閱讀 952·2019-08-29 15:18
閱讀 1743·2019-08-29 14:05