阅读 44

js拖拽技能

今天来研究下跟拖拽有关的问题,以备以后可以实现更多友好的交互形式。

1. 相关原生方法:

  • ondragstart:拖拽开始
  • ondragend:拖拽结束
  • ondragenter:拖拽元素进入目标元素头上的时候
  • ondrop:拖拽元素进入目标元素头上,同时鼠标松开的时候
  • ondragover:拖拽元素在目标元素头上移动的时候

有点晕?按下面的例子分别都操作下就明白啦。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type= text/css>
        .container {
            width: 400px;
            height: 400px;
            background: #ff6632;
        }
    </style>
</head>
<body>
    <div id='drag1' draggable="true">拖拽我,come on!</div>
    <div id='drag2'>拽不动我,come on!</div>
    <div class='container' id='container'>

    </div>
</body>
<script>
    document.getElementById('drag1').ondragstart = function(){
        console.log('开始拖拽')
    }
    document.getElementById('drag1').ondragend = function(){
        console.log('结束拖拽')
    }
    document.getElementById('container').ondrop = function(){
        console.log('已掉入规定范围')
    } 
    document.getElementById('container').ondragenter = function(){
        console.log('拖拽元素进入目标元素头上的时候')
    } 
    document.getElementById('container').ondragover = function(ev) {
        //防止ondrop不生效,ev.preventDefault()
        ev.preventDefault();
        console.log('拖拽元素在目标元素头上移动的时候')
    };
    
</script>
</html>
复制代码

特别注意:

  1. 想对一个元素进行一系列的拖拽操作,要注明属性 draggable="true" 来激活它的可拖拽性。
  2. 在使用ondrop时,发现没有生效,在ondragover中需要阻止默认事件的触发。

到这里我们已经可以拖拽元素并且监听一系列的拖拽事件了,然而本文远远没有结束,我们接着往下看:

人家网页上的拖拽都是拖着元素可以随便动,我这写的什么玩意!元素根本都不能动,不好使啊!!!!

别着急,现在我们来让元素跟着拖拽动起来!!!

  • 首先我们要搞清楚一些概念:
  1. screenX,screenY/offsetX,offsetY/clientX,clientY. 1. 1. --学习链接
  2. getComputedStyle -- 学习链接
  • 清楚了概念,我们看下具体实现。

html部分:

<style type='text/css'>
  #box {
        position: absolute;
        left: 100px;
        top: 100px;
        padding: 5px;
        background: #f0f3f9;
        width: 100px;
        height: 100px;
    }
</style>
...
<div id='box'></div>
复制代码

js部分:

    window.onload = function () {
        var oBox = document.getElementById("box");
        startDrag(oBox, oBox);
    };

    var params = {
        left: 0,
        top: 0,
        currentX: 0,
        currentY: 0,
        flag: false
    };
    //获取相关CSS属性
    var getCss = function (o, key) {
        return o.currentStyle ? o.currentStyle[key] : document.defaultView.getComputedStyle(o, null)[key];
    };

    //拖拽的实现
    var startDrag = function (bar, target, callback) {
        if (getCss(target, "left") !== "auto") {
            params.left = getCss(target, "left");
        }
        if (getCss(target, "top") !== "auto") {
            params.top = getCss(target, "top");
        }
        //o是移动对象
        bar.onmousedown = function (event) {
            params.flag = true;
            var e = event;
            params.currentX = e.clientX;
            params.currentY = e.clientY;
        };
        document.onmouseup = function () {
            console.log('test')
            params.flag = false;
            if (getCss(target, "left") !== "auto") {
                params.left = getCss(target, "left");
            }
            if (getCss(target, "top") !== "auto") {
                params.top = getCss(target, "top");
            }
        };
        document.onmousemove = function (event) {
            var e = event ? event : window.event;
            if (params.flag) {
                var nowX = e.clientX, nowY = e.clientY;
                var disX = nowX - params.currentX, disY = nowY - params.currentY;
                target.style.left = parseInt(params.left) + disX + "px";
                target.style.top = parseInt(params.top) + disY + "px";
            }
        }
    };
复制代码

元素跟着动起来啦!

可以尝试把这里的 DEMO 和 drag 的方法合并使用,这里就不赘述啦。

上述为PC的拖动,我们把chrome的模拟器切到手机模式,oh myGod!!炫酷的效果全都失效了!!!!桑心~~~~

下面我们来看下移动端的拖拽操作:

在这之前先对touch事件有一个了解:学习链接

有一些不得不说的概念:

  1. screenX: 触摸点相对于屏幕左边缘的 x 坐标。

  2. screenY: 触摸点相对于屏幕上边缘的 y 坐标。

  3. clientX: 触摸点相对于浏览器的 viewport左边缘的 x 坐标。不会包括左边的滚动距离。

  4. clientY: 触摸点相对于浏览器的 viewport上边缘的 y 坐标。不会包括上边的滚动距离。

  5. pageX: 触摸点相对于 document的左边缘的 x 坐标。 与 clientX不同的是,他包括左边滚动的距离,如果有的话。

  6. pageY: 触摸点相对于 document的左边缘的 y 坐标。 与 clientY不同的是,他包括上边滚动的距离,如果有的话。

  7. target: 总是表示 手指最开始放在触摸设备上的触发点所在位置的 element。即使已经移出了元素甚至移出了document, 他表示的element仍然不变

掌握了上述概念打开控制台,让我来观察元素的运动吧!!!

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type="text/css">
        #content {
            width: 200px;
            height: 200px;
            background: #ff6632;
            position: absolute;
            left: 0;
            top: 0;
        }
    </style>
</head>

<body>
    <div id="content">test</div>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
    (function (window) {  //传入window,提高变量的查找效率
        function myQuery(selector) {  //这个函数就是对外提供的接口。
            //调用这个函数的原型对象上的_init方法,并返回
            return myQuery.prototype._init(selector);
        }
        myQuery.prototype = {
            /*初始化方法,获取当前query对象的方法*/
            _init: function (selector) {
                if (typeof selector == "string") {
                    //把查找到的元素存入到这个原型对象上。
                    this.ele = window.document.querySelector(selector);
                    //返回值其实就是原型对象。
                    return this;
                }
            },
            /*单击事件:
             * 为了规避click的300ms的延迟,自定义一个单击事件
             * 触发时间:
             *   当抬起手指的时候触发
             *   需要判断手指落下和手指抬起的事件间隔,如果小于500ms表示单击时间。
             *
             *   如果是大于等于500ms,算是长按时间
             * */
            tap: function (handler) {
                this.ele.addEventListener("touchstart", touchFn);
                this.ele.addEventListener("touchend", touchFn);

                var startTime,
                    endTime;

                function touchFn(e) {
                    e.preventDefault()
                    switch (e.type) {
                        case "touchstart":
                            startTime = new Date().getTime();
                            break;
                        case "touchend":
                            endTime = new Date().getTime();
                            if (endTime - startTime < 500) {
                                handler.call(this, e);
                            }
                            break;
                    }
                }
            },
            /**
             * 长按
             * @param handler
             */
            longTag: function (handler) {
                this.ele.addEventListener("touchstart", touchFn);
                this.ele.addEventListener("touchmove", touchFn);
                this.ele.addEventListener("touchend", touchFn);
                var timerId;

                function touchFn(e) {
                    switch (e.type) {
                        case "touchstart":  //500ms之后执行
                            timerId = setTimeout(function () {
                                handler.call(this, e);
                            }, 500)
                            break;
                        case "touchmove":
                            //如果中间有移动也清除定时器
                            clearTimeout(timerId)
                            break;
                        case "touchend":
                            //如果在500ms之内抬起了手指,则需要定时器
                            clearTimeout(timerId);
                            break;
                    }
                }
            },
            /**
             * 左侧滑动。
                记录手指按下的左边,在离开的时候计算 deltaX是否满足左滑的条件
             *
             */
            slide: function (handler) {
                this.ele.addEventListener("touchstart", touchFn);
                this.ele.addEventListener("touchend", touchFn);
                var startX, startY, endX, endY;

                function touchFn(e) {
                    e.preventDefault();
                    var firstTouch = e.changedTouches[0];
                    switch (e.type) {
                        case "touchstart":
                            startX = firstTouch.pageX;
                            startY = firstTouch.pageY;
                            break;
                        case "touchend":
                            endX = firstTouch.pageX;
                            endY = firstTouch.pageY;
                            //x方向移动大于y方向的移动,并且x方向的移动大于25个像素,表示在向左侧滑动
                            if (Math.abs(endX - startX) >= Math.abs(endY - startY) && startX - endX >= 25) {
                                handler.call(this, e, 'left');
                            }else if(Math.abs(endX - startX) >= Math.abs(endY - startY) && startX - endX < -25){
                                handler.call(this, e, 'right');
                            }else if(Math.abs(endX - startX) < Math.abs(endY - startY) && startY - endY >= 25){
                                handler.call(this, e, 'up');
                            }else if(Math.abs(endX - startX) < Math.abs(endY - startY) && startY - endY < -25){
                                handler.call(this, e, 'down');
                            }
                            break;
                    }
                }
            },
            /**
             * 右侧滑动。
             *
             */
            slideRight: function (e) {
                
            }
        }
        window.$$ = window.myQuery = myQuery;
    })(window);

    $$("div").tap(function (e) {
        console.log("单击事件")
    })
    $$("div").slide(function (e, type) {
        let content = ''
        console.log(this);
        type === 'left' && (content = "左侧滑动了.....");
        type === 'right' && (content = "右侧滑动了...."); 
        type === 'up' && (content = "向上滑动了...."); 
        type === 'down' && (content = "向下滑动了....");
        this.innerHTML = content;
    })
    $$("div").longTag(function () {
        console.log("长按事件");
    })


    var _x_start, _y_start, _x_move, _y_move, _x_end, _y_end, left_start, top_start;
    document.getElementById("content").addEventListener("touchstart", function (e) {

        _x_start = e.touches[0].pageX;
        _y_start = e.touches[0].pageY;
        left_start = $("#content").css("left");
        top_start = $("#content").css("top");

    })
    document.getElementById("content").addEventListener("touchmove", function (e) {
        _x_move = e.touches[0].pageX;
        _y_move = e.touches[0].pageY;
        $("#content").css("left", parseFloat(_x_move) - parseFloat(_x_start) + parseFloat(left_start) + "px");
        $("#content").css("top", parseFloat(_y_move) - parseFloat(_y_start) + parseFloat(top_start) + "px");
    })
    document.getElementById("content").addEventListener("touchend", function (e) {
        // var _x_end=e.changedTouches[0].pageX;
        // var _y_end=e.changedTouches[0].pageY;
        // console.log("end",_x_end)
    })
    //阻止浏览器下拉事件
    $('body').on('touchmove', function (event) { event.preventDefault(); });
</script>

</html>
复制代码

是不是觉得可以很自由的操控一个元素了?哈哈哈。。。

接下来我们在看一个进度条的例子:

这里有一点对range的扩展,有兴趣的可以了解一下,不看也不影响理解。--学习链接

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type="text/css">
        .scroll {
            top: 100px;
            left: 100px;
            position: relative;
            width: 500px;
            height: 5px;
            background: rgb(180, 180, 180);
        }

        .bar {
            position: absolute;
            top: -3px;
            height: 10px;
            width: 8px;
            background: rgb(255, 0, 0);
        }

        .mask {
            position: absolute;
            height: 5px;
            background: rgb(255, 0, 0);
        }
    </style>
</head>
<body>
    <div class="scroll">
        <div class="bar"></div>
        <div class="mask"></div>
    </div>
    <p></p>
</body>

<script>
    // debugger
    var bar = document.getElementsByClassName('bar')[0];
    var scroll = document.getElementsByClassName('scroll')[0];
    var bar = document.getElementsByClassName('bar')[0];
    var mask = document.getElementsByClassName('mask')[0];
    bar.onmousedown = function(e){
        var leftVal = e.clientX - this.offsetLeft;
        var that = this;
        document.onmousemove = function(e){
            barLeft = e.clientX - leftVal;
            if(barLeft < 0){
                barLeft = 0
            }else if(barLeft > scroll.offsetWidth - bar.offsetWidth){
                barLeft = scroll.offsetWidth - bar.offsetWidth;
            }
            bar.style.left = barLeft + 'px';
            mask.style.width = barLeft + 'px';
            document.getElementsByTagName('p')[0].innerHTML = '已经完成了' + Math.floor((barLeft / (scroll.offsetWidth - bar.offsetWidth))*100) + '%' 
            //防止拖动过快时鼠标弹起后mousemove还在生效
            window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        }
    }
    document.onmouseup = function () {
        document.onmousemove = null; //弹起鼠标不做任何操作
    }
</script>
</html>
复制代码

是不是感觉还挺实用的,哈哈哈!

好啦,今天先扯到这里。

end。。。。。

关注下面的标签,发现更多相似文章
评论