• 移动端按钮长按弹出,长按移动,松开选定


    目录

    • 演示
    • 原理
      • touch对象
      • touch事件
      • targetTouches和touches的区别
    • 实现(代码部分)

    演示

    image
    第1个交互是点击弹出隐藏按钮
    第2个交互是长按“百度”按钮,然后弹出隐藏按钮,然后保持长按向上移动,触点经过的按钮会有特效,松开就会选定并跳转。

    原理

    通过对touchstart、touchmove、touchend事件监听,实现长按响应,然后通过计算位移来确定用户的手指在哪个按钮上面,当手指松开时,则触发跳转,因为要根据位移来确定触碰点在哪个按钮上面,所以需要确定位移多少后属于哪个按钮的范围,也就要知道每个按钮的高度。

    touch对象

    Touch构造函数接受一个配置对象作为参数,它有以下属性。
    identifier:必需,类型为整数,表示触摸点的唯一 ID。
    target:必需,类型为元素节点,表示触摸点开始时所在的网页元素。
    clientX:可选,类型为数值,表示触摸点相对于浏览器窗口左上角的水平距离,默认为0。
    clientY:可选,类型为数值,表示触摸点相对于浏览器窗口左上角的垂直距离,默认为0。
    screenX:可选,类型为数值,表示触摸点相对于屏幕左上角的水平距离,默认为0。
    screenY:可选,类型为数值,表示触摸点相对于屏幕左上角的垂直距离,默认为0。
    pageX:可选,类型为数值,表示触摸点相对于网页左上角的水平位置(即包括页面的滚动距离),默认为0。
    pageY:可选,类型为数值,表示触摸点相对于网页左上角的垂直位置(即包括页面的滚动距离),默认为0。
    radiusX:可选,类型为数值,表示触摸点周围受到影响的椭圆范围的 X 轴半径,默认为0。
    radiusY:可选:类型为数值,表示触摸点周围受到影响的椭圆范围的 Y 轴半径,默认为0。
    rotationAngle:可选,类型为数值,表示触摸区域的椭圆的旋转角度,单位为度数,在0到90度之间,默认值为0。
    force:可选,类型为数值,范围在0到1之间,表示触摸压力。0代表没有压力,1代表硬件所能识别的最大压力,默认为0。

    关于touch事件

    触摸事件与鼠标事件类似,不同的是触摸事件还提供同一表面不同位置的同步触摸。TouchEvent 接口将当前所有活动的触摸点封装起来。Touch 接口表示单独一个触摸点,其中包含参考浏览器视角的相对坐标。 ——《MDN Web Docs》

    TouchEvent属性列表(部分)
    TouchEvent.changedTouches 只读
    一个 TouchList 对象,包含了代表所有从上一次触摸事件到此次事件过程中,状态发生了改变的触点的 Touch 对象,这里的状态改变常见的有其各种坐标(clientX、pageX、screenX等)或者触摸压力发生了改变,即触点移动了,返回的是原来的一开始的触点touch对象,但是它的一些属性发生了变化。
    例子如下:

    <!DOCTYPE html>
    <html>
        <head>
            <title>targetTouches和touches的区别</title>
            <style>
                .layer1{
                    margin:20px;
                    600px;
                    height:600px;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    border: solid 1px;
                }
                .layer2{
                    margin:20px;
                    400px;
                    height:400px;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    border: solid 1px;
                }
                .layer3{
                    margin:20px;
                    200px;
                    height:200px;
                    border: solid 1px;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                }
            </style>
        </head>
        <body>
            <div id="container">
                <div class="layer1">
                    layer1
                    <div class="layer2">
                        layer2
                        <div class="layer3">layer3</div>
                    </div>
                </div>
            </div>
        </body>
        <script>
            let goStart = function(ev){
                console.log("start======");
                console.log("touches",ev.touches);
                console.log("targetTouches",ev.targetTouches);
                console.log("changedTouches",ev.changedTouches);
                console.log(ev.touches.length === ev.targetTouches.length);
            };
            let goEnd = function(ev){
                console.log("end======");
                console.log("touches",ev.touches);
                console.log("targetTouches",ev.targetTouches);
                console.log("changedTouches",ev.changedTouches);
                console.log(ev.touches.length === ev.targetTouches.length);
            };
            let layers = [].slice.call(document.querySelectorAll("#container div"));
            layers[1].addEventListener("touchstart",goStart);
            layers[1].addEventListener("touchend",goEnd);
        </script>
    </html>
    

    我的代码给div.layer2的touchstart、touchmove和touchend事件都添加了监听器,打印一下touches、targetTouches和changedTouches三个属性,现在我只是点击一下layer3区域,打印结果如下:
    image
    点开changedTouches,可以看到,force属性值改变了,导致点击的触点touch对象被记录下来了。
    相应的,当触点位置移动时,changedTouches也会记录。当从layer2移动到layer1范围,记录的还是一开始的那个触点,只不过其属性会随触点移动发生变化:
    image
    这里是从“layer2”字符串位置平移到“layer1”的位置,可以看见changedTouches记录的touch对象的target指向了div.layer2
    总结:
    changedTouches返回一个TouchList,其成员是在本次触摸事件中属性值发生变化的touch对象,该触摸事件包括了touchstart(force变为1)、touchmove(坐标改变)、touchend(force变为0,坐标也可能改变)。可以理解为只要发生触摸事件,changedTouches返回的TouchList一定不为空。

    TouchEvent.targetTouches 只读
    一个 TouchList 对象,是包含了如下触点的 Touch 对象:触摸起始于当前事件的目标 element 上,并且仍然没有离开触摸平面的触点。
    该属性返回一个TouchList实例,成员是触摸事件的目标元素节点内部、所有仍然处于活动状态(即触摸中)的触摸点。
    function touches_in_target(ev) {
    return (ev.touches.length === ev.targetTouches.length ? true : false);
    }
    //上面代码用来判断,是否所有触摸点都在目标元素内

    TouchEvent.touches 只读
    一 个 TouchList 对象,包含了所有当前接触触摸平面的触点的 Touch 对象,无论它们的起始于哪个 element 上,也无论它们状态是否发生了变化。代表所有的当前处于活跃状态的触摸点,该属性返回一个TouchList实例,成员是所有仍然处于活动状态(即触摸中)的触摸点。一般来说,一个手指就是一个触摸点。

    targetTouches和touches的对比

    image
    touches:当前屏幕上所有触摸点的集合列表
    targetTouches: 绑定事件的那个结点上的触摸点的集合列表
    changedTouches: 触发事件时改变的触摸点的集合
    举例来说,比如div1, div2只有div2绑定了touchstart事件,第一次放下一个手指在div2上,触发了touchstart事件,这个时候,三个集合的内容是一样的,都包含这个手指的touch,然后,再放下两个手指一个在div1上,一个在div2上,这个时候又会触发事件,但changedTouches里面只包含第二个第三个手指的信息,因为第一个没有发生变化,而targetTouches包含的是在第一个手指和第三个在div2上的手指集合,touches包含屏幕上所有手指的信息,也就是三个手指。
    参考:https://segmentfault.com/q/1010000002870710
    补充:这三个属性记录的touch对象的target都是触发touchstart的那个元素,不管最后是不是移动到了别的元素上面。

    实现
    自适应
    这里采用的是rem作为单位的自适应方案,限制最大宽度。

    // 设置基准大小
    const baseSize =16;
    function setRem () {
      // 当前页面宽度相对于 414 宽(设计图)的缩放比例
      const scale = document.documentElement.clientWidth / 414
      document.documentElement.style.fontSize = Math.min(29.6,(baseSize * Math.min(scale, 2))) + 'px'
    }
    // 初始化
    setRem()
    window.onresize = function () {
      setRem()
    }
    

    实现长按监测
    html部分

        <div id="guide-button">
            <!--隐藏的按钮-->
            <template v-if="showButtonList">
            <div id="old-exhibition">
                <img class="exbutton" 
                    :class="[btnIndex == 2?'hit':'']"           
                    src="@/assets/images/exbutton.png"/>
                <div class="button-text black-text">
                    <a :href="jumpUrl[2]"  
                        :class="[btnIndex == 2?'hit':'']">
                        {{titles[2]}}</a></div></div>
    
            <div id="cloud-exhibition">
                <img class="exbutton" 
                    :class="[btnIndex == 1?'hit':'']"
                    src="@/assets/images/exbutton.png"/>
                <div class="button-text black-text">
                    <a :href="jumpUrl[1]"
                         :class="[btnIndex == 1?'hit':'']">
                        {{titles[1]}}</a></div></div>
            </template>
            <template v-else>
                <div id='front'><img src='@/assets/images/front.png'/></div>
                <div id="back"><img src='@/assets/images/back.png'/></div>
            </template>
    
            <div id="mainbutton">
                <img class="exbutton" 
                    @touchstart='goStart' 
                    @touchmove='goMove'
                    @touchend='goEnd'
                    src="@/assets/images/mainbutton.png"/>
                <div class="button-text">
                    <a :href="jumpUrl[0]">{{titles[0]}}</a></div>
                <img :class="[showButtonList?'':'arrowDown','arrow']"
                    src="@/assets/images/arrow.png"
                    @click.self="buttonList"></div>
        </div>
    

    JS部分

        data(){
            return{
                showButtonList:false,     //是否显示隐藏按钮
                timeOutEvent: 0,         // 长按事件定时器
                startPageY:0,            //长按第一个按钮时的起始y坐标
                currentPageY:0,           //长按移动时的当前y坐标
                btnHeight:40,            //按钮的长度
                titles:['百度','掘金','36氪'],
                jumpUrl:['https://www.baidu.com',
                  'https://juejin.cn/',
                  'https://www.36kr.com/'],
            }
        },
        computed:{
            btnIndex:function(){
                //根据位移和按钮长度计算出的触点在哪个按钮上面
                let index;
                let relativeY = this.startPageY - this.currentPageY;
                index = Math.floor(relativeY / this.btnHeight);
                console.log(index);
                return index;
            },
        },
        methods:{
            pageJump(url){
                //跳转函数 
                window.location.href = url;
                console.log('jump');
            },
            buttonList(){
                //控制按钮隐藏或显示
                this.showButtonList = !this.showButtonList;
                console.log('buttonlist',this.showButtonList);
            },
            goStart(event) {
                //监听touchstart事件
                let _this = this;
                event.preventDefault();          //移动端长按一般会触发“复制”操作,应避免
                clearTimeout(_this.timeOutEvent);
                let touch = event.targetTouches[0];
                console.log('起点',touch.pageY);
                this.startPageY = touch.pageY;
                // 开始触摸
                _this.timeOutEvent = setTimeout(() => {
                    _this.timeOutEvent = 0
                    console.log('处理长按事件');
                    this.showButtonList = true;
                },1500);                        //如果触碰时间超过1.5秒则触发长按事件的响应
            },
            goMove(event) {
                event.preventDefault();
                let touch = event.targetTouches[0];
                console.log('移动中',touch.pageY);
                this.currentPageY = touch.pageY;
            },
            goEnd(){
                let _this = this;
                clearTimeout(this.timeOutEvent)
                if(_this.timeOutEvent !== 0){
                    console.log('处理单击事件');
                    // this.pageJump(this.jumpUrl[0]);
                }else{
                    console.log('处理长按结束事件');
                    if(this.btnIndex < this.titles.length
                        && this.btnIndex >= 0){
                        this.pageJump(this.jumpUrl[this.btnIndex]);
                    }
                    this.showButtonList = false;
                    this.startPageY = this.currentPageY = 0; 
    				//因为computed会缓存btnIndex,而需要在切换页面之后恢复原状,所以要改变
                }
            }
        },
    

    最后,因为是要实现自适应的,按钮的高度会变化,所以需要实时更新btnHeight。btnHeight未必是准确的按钮高度,还需考虑按钮的间隔等,只要能保证触点经过时能正确触发特效和交互即可,除数1.7是根据实际情况确定的,基本上是按照设计稿比例。

        beforeUpdate(){
            this.btnHeight = document.getElementById('mainbutton').clientHeight / 1.7;
            console.log('btnheight',this.btnHeight);
        },
    
  • 相关阅读:
    nodejs 文件拷贝
    MySQL linux二进制安装
    【Android工具类】验证码倒计时帮助类CountDownButtonHelper的实现
    JAVA一些基础概念
    程序猿生存定律-公司选择上的方法论
    Leetcode 第 2 题(Add Two Numbers)
    SpringMVC学习记录(五)--表单标签
    算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS
    CentOS 7 virt-manager 无法连接本地的hypervisor
    Android自己定义View画图实现拖影动画
  • 原文地址:https://www.cnblogs.com/liulangbxc/p/15234722.html
Copyright © 2020-2023  润新知