zoukankan      html  css  js  c++  java
  • 浅谈 原生javaScript && react 实现全局触摸按钮(附带对addeventlistener的了解)

    1.采用原生javaACript 实现全局触摸按钮

    首先在控制台输出,观察事件有哪些关于触摸的字段可以使用,然后拿这些字段的数据开始来写方法。

    因为要做的是全局触摸按钮,我需要拿到的是按钮时时的坐标位置,通过改变样式来改变按钮任意移动位置。所以就那了changedTouches里面的值。touches里面放的是touchstart的开始位置。用react的时候,touch事件会和click起冲突,导致链接点击有bug,所以需要那touchstart的值在touchend的时候做判断是点击事件还是触摸事件,来控制超链接的跳转。但是在普通h5中并没有这个问题,下面再细说。

    这是html代码

    <div class="active" id="active">
         <a class="vote" id="vote" href="https://www.baidu.com"></a> //是全局可以触摸的对象,点击又可以做跳转
    </div>

    css样式

          *{
                margin: 0;
                padding:0;
            }  //demo里面我就直接去掉所有默认的样式
            .active {
                width: 100%;
                height: 100%;
                background: #9ff5ee;
            }
            .vote {
                position: fixed;
                bottom:40px;  //全局触摸按钮使用定位的样式,可是随时改变定位的坐标来控制任意移动
                right: 15px;
                width: 60px;
                height: 60px;
                border-radius: 50%;
                background: yellow;
            }

    接下来是重点的javascript代码,顺便讲解一下(preventDefault()、stopPropagation()这些方法,ps其实是一边写博客一边学习,复习)

    window.onload=function(){ //当一个Web页面加载完成后就会触发执行window.onload 里的代码
        var vote=document.getElementById("vote");//通过id取按钮这个节点
        function handleTouchEvent(e) {
            //只跟踪一次触摸
            var width=document.documentElement.clientWidth,height=document.documentElement.clientHeight; //这里取屏幕的可视宽度和可视高度来控制按钮的最大最小访问
                switch (e.type) {
                    case "touchstart":   //触摸开始调用的方法
                        console.log('start');
                        break;
                    case "touchend":
                            console.log('end');  //触摸结束调用的方法,这里不用用到这两个方法,所以不做详情介绍
                        break;
                    case "touchmove": 
    //全局触摸主要用到touchmove这个方法,touchmove过程中坐标会不断变化,取这个变化的坐标来控制当前那全局按钮#vote的坐标。使用style对象来修改目前的样式,其间做了一个控制,不让按钮可以移动上左下右任意方向的屏幕外我用了两层三目运算符来限制坐标的最小和最大值。
                        document.getElementById("vote").style.left=parseInt(e.changedTouches[0].clientX) - 60 <= 0 || parseInt(e.changedTouches[0].clientX) >= width - 60 ? (parseInt(e.changedTouches[0].clientX) - 60 <= 0 ? 0 : width - 60) : parseInt(e.changedTouches[0].clientX);
    //左右方向,最小位置不小于按钮的直径,最大不超过屏幕的可视宽度减按钮直径,上下方向也是
                        document.getElementById("vote").style.top=parseInt(e.changedTouches[0].clientY) - 60 <= 0 || parseInt(e.changedTouches[0].clientY) >= height - 60 ? (parseInt(e.changedTouches[0].clientY) - 60 <= 0 ? 0 : height-60) : parseInt(e.changedTouches[0].clientY); 
                        break;
                }
    
        }
        function clickA(e){
             e.preventDefault();//这里是点击事件,阻止了默认事件的触发,所以超链接不会跳转,没阻止的话超链接就会正常跳转了
        }
        vote.addEventListener("touchstart", handleTouchEvent, false);
        vote.addEventListener("touchmove", handleTouchEvent, false);
        vote.addEventListener("touchend", handleTouchEvent, false);
        vote.addEventListener("click", clickA, false); 
    }

    事件调用的方法有    attachEvent方法适用于IE    addEventListener方法适用于FF

    attachEvent只有两个参数,第一个参数为事件名称,第二个参数为接收事件处理的函数,因为是做h5的触摸,不需要做ie的兼容,所以没做全兼容。如果要做全兼容,如下

    //兼容所有浏览器的事件监听方法
        function(element, type, handler){
            if (element.addEventListener) {
                element.addEventListener(type, handler, false);
            }
            else 
                if (element.attachEvent) {
                    element.attachEvent("on" + type, handler);
                }
                else {
                    element["on" + type] = handler;
                }
        }

    上文demo中事件的调用采用了addEventListener这个方法,这个方法有三个参数,第一个参数表示事件名称(不含 on,如 "click");第二个参数表示要接收事件处理的函数;第三个参数是一个bool值,一般为false。第三个参数的作用设置事件为冒泡还是捕获,如果为false那就是冒泡bubbling,为true就是捕获capture,冒泡和捕获是什么呢,看图

    两层div元素,而且都设定有click事件,一般来说,如果我在内层黄色的元素上click不只会触发黄色元素的click事件,还会同时触发红色元素的click事件,由内到外的触发就是冒泡bubbling,由外层div到内层div触发就是捕获。这时候如果想阻止这些冒泡和捕获事件的发生,就需要用到stopPropagation()方法了,IE中则使用e.cancelBubble = true阻止冒泡,该方法阻止目标元素的冒泡事件,但是会不阻止默认行为。浏览器的默认行为就需要用到preventDefault()方法来阻止默认事件,比如上文的阻止超链接的跳转。

    阻止冒泡的效果如下

    function clickA(e){
            alert('aaaa')
            e.stopPropagation();  //点击a#vote按钮,如果没有加入该行,点击该事件会同时出发a#vote和div#active上面的事件,加入该行就可以阻止冒泡
            e.preventDefault();//这里是点击事情,阻止了默认事件的触发,所以超链接不会跳转,没阻止的话超链接就会正常跳转了
        }
    function clickDiv(e){
            alert('444')
        }
    document.getElementById("vote").addEventListener("click", clickA, false); //事件的调用
    document.getElementById("active").addEventListener("click", clickDiv, false); 

    以上为原生javascript的实现方法

    2.react 实现全局触摸按钮(使用react+ES6语法,使用webpack打包工具)

    react大体包含(组件 ,JSX, 虚拟DOM, 单向数据流);

    JSX也就是HTML直接嵌套到JS代码里面,所以以下就没有所谓的html和js分开了。

    我们首先还是来观察react+ES6的事件输出,根据前面原生的js我们知道移动端的触摸主要用了touches和changedTouches里面的值,用touches来取得初始的坐标位置,用changedTouches可以取得触摸后移动位置的坐标。除了这两个还有一个targetTouches保存着当前坐标。它们都属于TouchList对象

    TouchList

    Touch对象构成的数组,通过event.touches取到。一个Touch对象代表一个触点,当有多个手指触摸屏幕时,TouchList就会存储多个Touch对象,前面说到的identifier就用来区分每个手指对应的Touch对象。

    touches / changedTouches / targetTouches

    touches
    一个TouchList对象,包含当前所有接触屏幕的触点的Touch对象,不论 touchstart 事件从哪个elment上触发。

    targetTouches
    也是一个TouchList对象,包含了如下触点的 Touch 对象:touchstart从当前事件的目标element上触发

    changedTouches
    也是一个 TouchList 对象,对于 touchstart 事件, 这个 TouchList 对象列出在此次事件中新增加的触点。对于 touchmove 事件,列出和上一次事件相比较,发生了变化的触点。对于 touchend ,列出离开触摸平面的触点(这些触点对应已经不接触触摸平面的手指)。

    touchend这里要特别注意,touches和targetTouches只存储接触屏幕的触点,要获取触点最后离开的状态要使用changedTouches  ,但是在react单向数据流中,可以实时改变状态机state中的值,所以可以不断取得接触屏幕触点的位置来改变触摸对象的坐标,所以react里面可用touches和targetTouches在touchend和touchmove中

    e.touches、e.targetTouches、e.changedTouches  的输出都如下图,都是Touch对象

    react绑定事件处理函数---->触摸(React 标准化了事件对象,因此在不同的浏览器中都会有相同的属性。)

    • onTouchCancel

    • onTouchEnd

    • onTouchMove

    • onTouchStart

    触摸只会在移动设备上产生。在使用触摸时,尝试着用onclick来处理同个元素上的点击事件,发现点击事件根本没反应。所以我就在onTouchEnd处理元素触摸后的结果。当元素触摸结束后位置没偏移,就可以当成是点击事件,处理点击事件要完成的任务。

    接下来上代码。

    'use strict';
    
    import React from 'react';
    import './index.less';  //这里用的都是es6的引入方式
    
    export default class Back extends React.Component {
        static defaultProps = {
             prefixCls: 'zm'
         };
        constructor() { //初始化state的工作要在constructor中完成。
            super();
            //在react中,无法获取dom节点,把要改变的值放在state状态机中
            this.state = {
                top: '76%',  //触摸按钮初始位置(距离左边顶部的位置)
                left: '83%',
                startX: '',   //触摸按钮初始的坐标
                startY: '',
                x: 0,  //记录触摸按钮触摸后有无变化
                y: 0,
                height: document.documentElement.clientHeight,  //屏幕可视宽高
                 document.documentElement.clientWidth,
            };
    
        }
    
        handleStart(e){
            e.preventDefault();
            
    //前面原生js用的是touches,其实还有一个targetTouches,在这两个的输出结果是一致的。
            this.setState({  //当触摸开始时候,记录当时的坐标值,还有设置触摸变化的xy轴的变化为0,因为当新一轮触摸开始时候,必须重新设置,相当于初始化
                startX : e.targetTouches[0].clientX,
                startY : e.targetTouches[0].clientY,
                x:0,
                y:0,
            });
        }
    
        handleTouchMove(e) {
            const { startX, startY, width, height } = this.state;//取得初始坐标和屏幕可视宽高
            
            this.setState({
                //设置当前的坐标位置,思路和上面原生的一样,不过由于react有实时变化的状态机state,所以在此用touches,targetTouches
                //都可以来设置实时变化的值,不用用到changedTouches;
                left: parseInt(e.touches[0].clientX) - 48 <= 0 || parseInt(e.touches[0].clientX) >= width - 48 ? (parseInt(e.touches[0].clientX) - 48 <= 0 ? 0 : width - 48) : parseInt(e.touches[0].clientX),
                top: parseInt(e.touches[0].clientY) - 48 <= 0 || parseInt(e.touches[0].clientY) >= height - 48 ? (parseInt(e.touches[0].clientY) - 48 <= 0 ? 0 : height - 48) : parseInt(e.touches[0].clientY),
                x: e.touches[0].clientX - startX, //当前触摸点-初始坐标取得实时变化值
                y: e.touches[0].clientY - startY,
            });
        }
    
        handleTouchEnd (e) {
            const { x, y } =  this.state;
    
            if (x == 0 && y == 0) { //触摸结束后,判断实时变化值有没变化,没变化则视为点击事件。
                window.location.href = '#';
            };
        }
    
        render() {
            const { prefixCls } = this.props;
            const { top, left, height, width } = this.state;  //取得实时状态机state的值
    
            return ( 
                <div
                    className="zm" 
                    style={{top:`${top}`,left:`${left}` }}        //取得state的值实时改变触摸点的坐标位置                  
                    onTouchStart={this.handleStart.bind(this)}  //使用bind(this)改变函数作用域,不加上bind则this指向的是全局对象window而报错。
                    onTouchMove={this.handleTouchMove.bind(this)}
                    onTouchEnd={this.handleTouchEnd.bind(this)}
                />
    
            )
        }
    }

    react中要注意的是使用touch时 click没反应,需要在touchend中做判断做处理

     

  • 相关阅读:
    JVM和HotSpot
    java中的四种引用类型
    垃圾回收与算法
    Full GC
    JVM内存结构
    事务不同的隔离级别实现原理
    事务的隔离级别
    jQuery后续和 前端框架Bootstrap
    jQuery
    BOM和DOM操作
  • 原文地址:https://www.cnblogs.com/whkl-m/p/6256665.html
Copyright © 2011-2022 走看看