搜尋
首頁web前端js教程行動端效果中Picke的實作方法

行動端效果中Picke的實作方法

Oct 12, 2017 am 09:41 AM
實現方法

寫在前面

接著前面的行動端效果的研究,這次來看看行動端效果中Picke的實作方法選擇器的實作原理

行動端效果之Swiper

程式碼看這裡:github

行動端效果中Picke的實作方法

1. 核心解析

1.1 基本HTML結構


<!--     说明:    
1. 类 行動端效果中Picke的實作方法-3d 是为了提供3d视角,如果不需要可以去掉    
2. 类 行動端效果中Picke的實作方法-slot-absolute 在3d视角中需要加上,因为下面相对定位的 行動端效果中Picke的實作方法-items 是要相对父容器进行    transform的,如果不加,就会造成位移不正确    3. DOM中所有的style样式都是在初始化的时候加上的--><p class="行動端效果中Picke的實作方法 行動端效果中Picke的實作方法-3d">
    <p class="行動端效果中Picke的實作方法-items">
        <p class="行動端效果中Picke的實作方法-slot 行動端效果中Picke的實作方法-slot-absolute" style="flex:1;">
            <p class="行動端效果中Picke的實作方法-slot-wrapper" id="wrapper" style="height: 108px;">
                <p class="行動端效果中Picke的實作方法-item 行動端效果中Picke的實作方法-selected" style="height:36px;line-height: 36px">1981</p>
                <!-- ... -->
                <p class="行動端效果中Picke的實作方法-item" style="height:36px;line-height: 36px">1999</p>
            </p>
        </p>
    </p>
    <p class="行動端效果中Picke的實作方法-center-highlight" style="height:36px;margin-top:-18px;"></p></p>

1.2 初始化DOM

由於餓了麼源碼中的行動端效果中Picke的實作方法是採用v-for指令產生的DOM,因此我這裡只是簡單的用javascript來模擬DOM的生成。


var el = document.querySelector(&#39;#wrapper&#39;);
var animationFrameId = null;
var currentValue;
var itemHeight = 36;
var visibleItemCount = 3;
var valueIndex = 0;
var rotateEffect = true;
var datas = [&#39;1981&#39;, &#39;1982&#39;, &#39;1983&#39;, &#39;...&#39;, &#39;1999&#39;];// 如果支持3d视角,则给<p class="行動端效果中Picke的實作方法"></p>加上类"行動端效果中Picke的實作方法-3d"// <p class="行動端效果中Picke的實作方法-slot" style="flex:1;">加上类"行動端效果中Picke的實作方法-slot-absolute"if (rotateEffect) {
    var 行動端效果中Picke的實作方法 = document.querySelector(&#39;.行動端效果中Picke的實作方法&#39;);
    var 行動端效果中Picke的實作方法Slot = document.querySelector(&#39;.行動端效果中Picke的實作方法-slot&#39;);
    行動端效果中Picke的實作方法.classList.add(&#39;行動端效果中Picke的實作方法-3d&#39;);
    行動端效果中Picke的實作方法Slot.classList.add(&#39;行動端效果中Picke的實作方法-slot-absolute&#39;);}// 限定容器高度el.style.height = `${visibleItemCount * itemHeight}px`;// 生成DOMvar html = &#39;&#39;;datas.forEach(function(data, index) {
    html += `<p class="行動端效果中Picke的實作方法-item" style="height:36px;line-height:36px;">${data}</p>`;});el.innerHTML = html;// 激活当前itemvar 行動端效果中Picke的實作方法Items = document.querySelectorAll(&#39;.行動端效果中Picke的實作方法-item&#39;);行動端效果中Picke的實作方法Items[valueIndex].classList.add(&#39;行動端效果中Picke的實作方法-selected&#39;);

1.3 初始化事件

總體上來說,行動端效果中Picke的實作方法的事件也包括滑動開始、滑動中、滑動結束。因為畢竟是行動端,滑動不可避免。這次,原始碼中的對滑動事件進行了封裝,相容了PC#端以及排除了拖曳和選擇造成的影響,具體看一下分析。 `


/**  * draggable.js  * 只是起到一定的兼容性 * 实质和直接调用 el.addEventListener(&#39;touchstart&#39;, startFn); 并没有多大差别 */// 滑动开始// touchstart 和 mousedown 可见对PC端的兼容// onselectstart/ondragstart 直接return 可见排除了拖动和选择element.addEventListener(supportTouch ? &#39;touchstart&#39; : &#39;mousedown&#39;, function(event) {
    if (isDragging) return;
    document.onselectstart = function() { return false; };
    document.ondragstart = function() { return false; };

    // ...});// 滑动结束var endFn = function(event) {
    // 注销事件
    if (!supportTouch) {
        document.removeEventListener(&#39;mousemove&#39;, moveFn);
        document.removeEventListener(&#39;mouseup&#39;, endFn);
    }
    document.onselectstart = null;
    document.ondragstart = null;

    isDragging = false;

    if (options.end) {
        options.end(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
    }}

要是DOM跟隨自己在手機螢幕上的滑動而滑動,方法大同小異,無非就是在開始滑動記錄開始位置,滑動中即時計算位移,滑動結束之後將DOM滑動應該滑動的位置。這點可以參考前面一篇文章移動端效果之Swiper,這篇文章中有著相同的方法。這裡重點講一下其中的差異


// 滑动开始的执行事件方法start: function(event) {
    dragState = {
        range: getDragRange(),
        // ...
        startTranslateTop: translateUtil.getElementTranslate(el).top
    };}

這其中有兩個方法,第一個getDragRange和第二個getElementTranslate(el ).

  • 第一個函數的作用是取得行動端效果中Picke的實作方法能夠滑動的最小和最大的位移,這將會在滑動結束事件中使用到。關於如何計算,這裡簡單提一下,向下滑動,最大不能超過最中間的item的最上方,這也就是為什麼itemHeight * Math.floor(visibleItemCount / 2) ,而向上滑動,最大不能超過中間item的最下方,-itemHeight * (valuesLength - Math.ceil(visibleItemCount / 2)),細細想一下就好了。

  • 第二個函數的作用是取得目前行動端效果中Picke的實作方法transform值,作為下一次滑動計算的依據。其實感覺這樣挺費事,因為在touchend中最後肯定會計算translate值,我們只需要每次保存最後滑動的移動值就好了,而不要每次都要在DOM中取。


/** * translateUtil * 对浏览器对前缀支持的一些判断 * 检测浏览器对3d属性的支持情况 * 获取当前的translate值/清空行動端效果中Picke的實作方法的translate值/移动行動端效果中Picke的實作方法 * 对于浏览器的检测方面,这也算是一个比较好的工具类 */var docStyle = document.documentElement.style;var engine;var translate3d = false;// 浏览器判断if (window.opera && Object.prototype.toString.call(opera) === &#39;[object Opera]&#39;) {
    engine = &#39;presto&#39;;} else if (&#39;MozAppearance&#39; in docStyle) {
    engine = &#39;gecko&#39;;} else if (&#39;WebkitAppearance&#39; in docStyle) {
    engine = &#39;webkit&#39;;} else if (typeof navigator.cpuClass === &#39;string&#39;) {
    engine = &#39;trident&#39;;}// css前缀var cssPrefix = {
    trident: &#39;-ms-&#39;,        // IE
    gecko: &#39;-moz-&#39;,         // FireFox
    webkit: &#39;-webkit-&#39;,     // Chrome/Safari/etc...
    presto: &#39;-o-&#39;           // Opera}[engine];// style前缀var vendorPrefix = {
    trident: &#39;ms&#39;,
    gecko: &#39;Moz&#39;,
    webkit: &#39;Webkit&#39;,
    presto: &#39;O&#39;}[engine];var helpElem = document.createElement(&#39;p&#39;);var perspectiveProperty = vendorPrefix + &#39;Perspective&#39;;var transformProperty = vendorPrefix + &#39;Transform&#39;;var transformStyleName = cssPrefix + &#39;transform&#39;;var transitionProperty = vendorPrefix + &#39;Transition&#39;;var transitionStyleName = cssPrefix + &#39;transition&#39;;var transitionEndProperty = vendorPrefix.toLowerCase() + &#39;TransitionEnd&#39;;if (helpElem.style[perspectiveProperty] !== undefined) {
    translate3d = true;}// 讲一下这个正则// \s*(-?\d+(\.\d+?)?)px 这是一个单元,匹配这样的 -23.15px, 剩下的应该就好理解了var regexp = /translate\(\s*(-?\d+(\.\d+?)?)px,\s*(-?\d+(\.\d+?)?)px\)\s*translateZ\(0px\)/ig;

接下來看看滑動中


drag: function(event) {
    // 加上 dragging 类是为了清除过渡效果,在swiper中也有同样的应用
    el.classList.add(&#39;dragging&#39;);

    dragState.left = event.pageX;
    dragState.top = event.pageY;

    var deltaY = dragState.top - dragState.startTop;
  
    // 计算当前的滑动位移
    var translate = dragState.startTranslateTop + deltaY;

    // 滑动元素
    translateUtil.translateElement(el, null, translate);
    velocityTranslate = translate - prevTranslate || translate;

    prevTranslate = translate;

    if (rotateEffect) {
        updateRotate(prevTranslate, 行動端效果中Picke的實作方法Items);
    }}

看到以上的程式碼中有一個velocityTranslate,這個值有神馬作用,最開始我也不清楚,後面發現在滑動結束之後用到了,才明白了它代表了一個速率的位移值。什麼是速率?就好比你快速滑動的時候,總希望它能夠慣性滑動一下,這個值乘以一個慣性值就可以得到一個慣性位移。看end中的程式碼。


end: function() {
    // 添加过渡
    el.classList.remove(&#39;dragging&#39;);
    // 惯性值
    var momentumRatio = 7;
    var currentTranslate = translateUtil.getElementTranslate(el).top;
    var duration = new Date() - dragState.start;

    var momentumTranslate;
    if (duration < 300) {
        momentumTranslate = currentTranslate + velocityTranslate * momentumRatio;
    }

    // 加上惯性速率之后的位移值
    console.log(&#39;momentumTranslate&#39;, momentumTranslate);

    dragRange = dragState.range;

    setTimeout(function() {
        var translate;
        if (momentumTranslate) {
            translate = Math.round(momentumTranslate / itemHeight) * itemHeight;
        } else {
            translate = Math.round(currentTranslate / itemHeight) * itemHeight;
        }

        // 取得最终的位移值,
        // 必须为itemHeight的倍数
        // 在范围的最大值和最小值中取
        translate = Math.max(Math.min(translate, dragRange[1]), dragRange[0]);
        translateUtil.translateElement(el, null, translate);

        // 计算得出当前位移下应该对应的实际值
        currentValue = translate2Value(translate);

        // 3d效果
        if (rotateEffect) {
            planUpdateRotate();
        }
    }, 10);

    dragState = {};}

這就是整個行動端效果中Picke的實作方法的實作流程,撇開3d效果就可以使用了。下面來看看如何實現的3D效果。在doOnValuesChange中有一個最開始的初始化。


[].forEach.call(items, function(item, index) {
    translateUtil.translateElement(item, null, itemHeight * index);});

給每一個item設定了根據索引來的位移值,此時的每一個item的定位都必須是absolute的,這樣位移下來才是緊挨著的,不然可能中間都會有一個itemHeight的空格。

3D效果中最關鍵的一點就是如何進行翻轉角度的計算。在原始碼中定義了一個常數物件:


var VISIBEL_ITEMS_ANGLE_MAP = {
    3: -45,
    5: -20,
    7: -15};

可以看到,當只有3個可見元素的時候,高亮部分相對於X軸平行,而上一個item就必須繞X軸順時針旋轉45度,反之下一個itemX軸逆時針旋轉45度。另外在其中有一段程式碼特別繞,根據我的理解是這樣的:


// 当前item相对于顶部原本应该有的位移值var itemOffsetTop = index * itemHeight; // 滑动过程中,相对于最开始的位置滑动的位移值var translateOffset = dragRange[1] - currentTranslate;// 当应该有的位移值和滑动的位移值相等的时候,也就说明了当前的`item`被选中// 也就是说此时当前的角度为0var itemOffset = itemOffsetTop - translateOffset;var percentage = itemOffset / itemHeight;var angle = angleUnit * percentage;if (angle > 180) angle = 180;if (angle < -180) angle = -180;rotateElement(item, angle);

如果覺得太繞,其實也沒有必要按照他的這種做法來,我們只要想辦法確定每一個item相對於目前選取的item是處於上一個還是下一個,就可以根據此來計算角度。

2. 總結

關於餓了麼中的行動端效果中Picke的實作方法元件就看了這麼多,整體來說跟swiper中的滑動十分相似,其中的關鍵點在於最後的計算位移值來根據位移值滑動到正確的位置,至於怎麼計算值,其實每個人的實現方式可能都是大同小異的,也沒要必要一定要按照源碼來,可以適當地加入自己的理解,這樣或許寫起程式碼來更加得心應手。這裡只是個人的一點理解,希望能夠給自己也能提供給大家一點幫助。

以上是行動端效果中Picke的實作方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境