H5仿百度,腾讯地图拖拽面板

752 阅读1分钟

工作中遇到的,简单总结一下吧。

需求背景

老板一看设计稿,就和我说,要做得和百度地图app里的一样丝滑~

具体实现

代码比较简单,直接贴上就好了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <title>H5仿百度,腾讯地图拖拽面板</title>
    <style>
        * {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
        html, body, #app {
            height: 100%;
            position: relative;
            overflow: hidden;
        }
        .container {
            width: 100%;
            height: 100%;
            background: #ccc;
            position: absolute;
            top: 100%;
        }
        .container.moving {
            transition: all .2s;
            transition-timing-function: ease-out;
        }
        .header {
            height: 100px;
            line-height: 100px;
            text-align: center;
            background: darkgoldenrod;
        }
        .box {
            height: calc(100% - 100px);
            overflow-y: hidden;
        }
        .box.scroll {
            overflow-y: scroll;
        }
        .box .content {
            height: 50px;
            text-align: center;
        }
    </style>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.js"></script>
</head>
<body>
    <div id="app">
        <div class="container" :class="{'moving': !moving}" :style="{height: `${maxHeight}px`, transform: `translateY(${-translate}px)`}" @touchmove.stop="touchmoveHandle" @touchstart.stop="touchstartHandle" @touchend.stop="touchendHandle">
            <div class="header">header</div>
            <div class="box" ref="box" @touchmove="touchmoveBoxHandle" @touchstart="touchstartBoxHandle" @touchend="touchendBoxHandle" :class="{'scroll': isScroll}">
                <div class="content" v-for="item in 20">content{{item}}</div>
            </div>
        </div>
    </div>
    <script>
        /*
        * 需要展示效果:
        * 1. 根据手指移动的距离实时移动
        * 2. 划动结束时位移到某个高度 
        * 3. 仅在最高高度时可以内部滚动
        * 
        * 实现思路:
        * 1. 先设定3个高度,最大高度=屏幕高度,最小高度,中间高度
        * 2. 从结构上将面板设置为最大高度,定位到屏幕的最下方,通过改变translateY来进行展示
        * 3. 划动结束需要位移到哪个高度(上移或者下移)可以有多种方式
        *   1:根据初次划动的方向来确定
        *   2:根据划动结束时的位置靠近哪个高度来确定
        *   3:根据结束划动前的方向来确定
        *   4:(高德和腾讯)从使用体验感觉上来说:好像是区分了短划动和长划动,短划时应该时第一种,长划时应用了第二种
        * 4. ios回弹问题
        */
        const App = new Vue({
            el: '#app',
            data: {
                moving: false, // 判断是否进行了拖拽,防止点击时误触发touchend
                maxHeight: 0, // 最大高度
                middleHeight: 0, // 中间高度
                minHeight: 100, // 最小高度
                translate: 0, // 位移高度
                isScroll: false // 内部滚动
            },
            methods: {
                touchmoveHandle(event) {
                    this.moving = true;
                    let currentY = event.touches[0].pageY;
                    // 记录滑动值
                    const upAndDown = this.prevY - currentY;
                    this.translate = this.translate + upAndDown;
                    // 最大最小边界判断
                    if (this.translate > this.maxHeight) {
                        this.translate = this.maxHeight
                    }
                    if (this.translate < this.minHeight) {
                        this.translate = this.minHeight
                    }
                    this.prevY = currentY;
                    // 在初次滑动时判断结束的方向
                    if (this.direction_flag) {
                        this.direction_flag = false;
                        this.up = !((currentY - this.startY) > 0)
                    }
                },
                touchstartHandle(event) {
                    // 记录初始值
                    this.startY = event.touches[0].pageY;
                    this.prevY = this.startY;
                    this.direction_flag = true;
                },
                touchendHandle(event) {
                    if (!this.moving) return; // 防止点击触发
                    this.moving = false;
                    // 根据方向判断最终展示的位移高度
                    if (this.up) {
                        if (this.translate < this.middleHeight) {
                            this.translate = this.middleHeight
                            this.isScroll = false
                        } else {
                            this.translate = this.maxHeight
                            this.isScroll = true
                        }
                    } else {
                        if (this.translate < this.middleHeight) {
                            this.translate = this.minHeight
                            this.isScroll = false
                        } else {
                            this.translate = this.middleHeight
                            this.isScroll = false
                        }
                    }
                    this.direction_flag = true
                },
                touchstartBoxHandle(e) {
                    // ios回弹处理
                    if (this.isIos && this.isScroll) {
                        // 根据滚动条位置判断是否允许滚动
                        this.allowUp = this.box.scrollTop > 0;
                        this.allowDown = (this.box.scrollTop < this.box.scrollHeight - this.box.clientHeight);
                        this.slideBeginY = e.pageY; // e.targetTouches[0].pageY也可以
                    }
                    if (this.box.scrollTop !== 0) {
                        this.stop = true;
                        e.stopPropagation();
                    } else {
                        this.stop = false;
                    }
                },
                touchmoveBoxHandle(e) {
                    if (this.isIos && this.isScroll) {
                        const up = (e.pageY > this.slideBeginY);
                        const down = (e.pageY < this.slideBeginY);
                        this.slideBeginY = e.pageY;
                        if ((up && this.allowUp) || (down && this.allowDown)) {
                            e.stopPropagation();
                        } else {
                            e.preventDefault();
                        }
                    }
                    if (this.stop) e.stopPropagation()
                },
                touchendBoxHandle(e) {
                    if (this.stop) e.stopPropagation()
                }
            },
            mounted() {
                this.isIos = window.navigator.userAgent.indexOf('Mac OS X') > -1;
                // 记录屏幕高度
                this.maxHeight = window.innerHeight
                this.middleHeight = this.maxHeight / 2
                this.translate = this.middleHeight
                this.box = this.$refs['box'];
            }
        })
    </script>
</body>
</html>

实现效果

手机查看效果

二维码图片_12月20日22时37分27秒.png

参考借鉴

github.com/wangjiandev…