javascript动画系列第一篇——模拟拖拽

×

目录

[1]原理介绍 [2]代码实现 [3]代码优化[4]拖拽冲突[5]IE兼容

前面的话

  从本文开始,介绍javascript动画系列。javascript本身是具有原生拖放功能的,但是由于兼容性问题,以及功能实现的方式,用的不是很广泛。javascript动画广泛使用的还是模拟拖拽。本文将详细介绍该内容

原理介绍

  模拟拖拽最终效果和在桌面上移动文件夹的效果类似

  鼠标按下时,拖拽开始。鼠标移动时,被拖拽元素跟着鼠标一起移动。鼠标抬起时,拖拽结束

  所以,拖拽的重点是确定被拖拽元素是如何移动的

  假设,鼠标按下时,鼠标对象的clientX和clientY分别为x1和x2。元素距离视口左上角x轴和y轴分别为x0和y0

  鼠标移动的某一时刻,clientX和clientY分别为x2和y2

  所以,元素移动的x轴和y轴距离分别为x2-x1和y2-y1

  元素移动后,元素距离视口左上角x轴和y轴的位置分别为

    X = x0 + (x2-x1)
    Y = y0 + (y2-y1)

代码实现

  将上面的原理用代码实现如下

  鼠标按下时,初始态的x0和y0分别用offsetLeft和offsetTop表示

  鼠标移动时,瞬时态的x和y分别赋值为定位后元素的left和top

<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;"></div>
<script>
test.onmousedown = function(e){
    e = e || event;
    //获取元素距离定位父级的x轴及y轴距离
    var x0 = this.offsetLeft;
    var y0 = this.offsetTop;
    //获取此时鼠标距离视口左上角的x轴及y轴距离
    var x1 = e.clientX;
    var y1 = e.clientY;

    test.onmousemove = function(e){
        e = e || event;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        x2 = e.clientX;
        y2 = e.clientY;
        //计算此时元素应该距离视口左上角的x轴及y轴距离
        var X = x0 + (x2 - x1);
        var Y = y0 + (y2 - y1);
        //将X和Y的值赋给left和top,使元素移动到相应位置
        test.style.left = X;
        test.style.top = Y;
    }

    test.onmouseup = function(e){
        //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
        test.onmousemove = null;
    }
}
</script>

代码优化

  使用上面的代码时,会出现一个问题。当鼠标拖动的太快,比onmousemove事件的触发间隔还要快时,鼠标就会从元素上离开。这样就停止了元素的拖拽过程

  此时,如果把mousemove和mouseup事件都加在document上时,即可解决

<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;"></div>
<script>
test.onmousedown = function(e){
    e = e || event;
    //获取元素距离定位父级的x轴及y轴距离
    var x0 = this.offsetLeft;
    var y0 = this.offsetTop;
    //获取此时鼠标距离视口左上角的x轴及y轴距离
    var x1 = e.clientX;
    var y1 = e.clientY;

    document.onmousemove = function(e){
        e = e || event;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        x2 = e.clientX;
        y2 = e.clientY;
        //计算此时元素应该距离视口左上角的x轴及y轴距离
        var X = x0 + (x2 - x1);
        var Y = y0 + (y2 - y1);
        //将X和Y的值赋给left和top,使元素移动到相应位置
        test.style.left = X;
        test.style.top = Y;
    }

    document.onmouseup = function(e){
        //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
        document.onmousemove = null;
    }
}
</script>

拖拽冲突

  由于文字和图片默认支持原生拖放,如果将原生拖放和模拟拖拽掺杂在一起,将造成与预想效果不符的情况

  如果拖放的元素内容存在文字,且文字被选中会触发文字的原生拖放效果

  在文字上面双击鼠标,即可选中文字,再移动鼠标时,会触发文字的原生拖放效果,如下所示

  只要在onmousedown事件阻止浏览器的默认行为即可

<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">测试文字</div>
<script>
test.onmousedown = function(e){
    e = e || event;
    //获取元素距离定位父级的x轴及y轴距离
    var x0 = this.offsetLeft;
    var y0 = this.offsetTop;
    //获取此时鼠标距离视口左上角的x轴及y轴距离
    var x1 = e.clientX;
    var y1 = e.clientY;

    document.onmousemove = function(e){
        e = e || event;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        x2 = e.clientX;
        y2 = e.clientY;
        //计算此时元素应该距离视口左上角的x轴及y轴距离
        var X = x0 + (x2 - x1);
        var Y = y0 + (y2 - y1);
        //将X和Y的值赋给left和top,使元素移动到相应位置
        test.style.left = X;
        test.style.top = Y;
    }

    document.onmouseup = function(e){
        //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
        document.onmousemove = null;
    }
    //阻止默认行为
    return false;
}
</script>

IE兼容

  以上代码在IE8-浏览器中仍然无法阻止默认行为。此时,为了实现IE兼容,需要使用全局捕获setCapture()和释放捕获releaseCapture()

  首先,先看一下全局捕获的效果

  下面代码中,开启全局捕获之后,页面中的所有点击效果,都相当于针对按钮一的点击效果。释放捕获后,效果消失

  [注意]IE浏览器完全支持全局捕获;chrome不支持,使用全局捕获会报错;firefox不报错,但静默失败

<button id="btn1">按钮一</button>
<button id="btn2">开启按钮一的全局捕获</button>
<script>
btn1.onclick = function(){
    alert(1);
}
btn2.onclick = function(){
    if(btn1.setCapture){
        if(btn2.innerHTML.charAt(0) == ‘开‘){
            btn1.setCapture();
            btn2.innerHTML = ‘关闭按钮一的全局捕获‘;
        }else{
            btn1.releaseCapture();
            btn2.innerHTML = ‘开启按钮一的全局捕获‘;
        }
    }
}
</script>

  通过在IE浏览器设置全局捕获来达到取消文字原生拖放的默认行为

<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">测试文字</div>
<script>
test.onmousedown = function(e){
    e = e || event;
    //获取元素距离定位父级的x轴及y轴距离
    var x0 = this.offsetLeft;
    var y0 = this.offsetTop;
    //获取此时鼠标距离视口左上角的x轴及y轴距离
    var x1 = e.clientX;
    var y1 = e.clientY;

    document.onmousemove = function(e){
        e = e || event;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        x2 = e.clientX;
        y2 = e.clientY;
        //计算此时元素应该距离视口左上角的x轴及y轴距离
        var X = x0 + (x2 - x1);
        var Y = y0 + (y2 - y1);
        //将X和Y的值赋给left和top,使元素移动到相应位置
        test.style.left = X;
        test.style.top = Y;
    }

    document.onmouseup = function(e){
        //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
        document.onmousemove = null;
        //释放全局捕获
        if(test.releaseCapture){
            test.releaseCapture();
        }
    }
    //阻止默认行为
    return false;
    //IE8-浏览器阻止默认行为
    if(test.setCapture){
        test.setCapture();
    }
}
</script>

源码查看

时间: 2024-12-11 15:11:33

javascript动画系列第一篇——模拟拖拽的相关文章

深入理解javascript作用域系列第一篇——内部原理

× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原理更为重要.本文是深入理解javascript作用域系列的第一篇——内部原理 内部原理分成编译.执行.查询.嵌套和异常五个部分进行介绍,最后以一个实例过程对原理进行完整说明 编译 以var a = 2;为例,说明javasc

深入理解javascript对象系列第一篇——初识对象

× 目录 [1]定义 [2]创建 [3]组成[4]引用 前面的话 javascript中的难点是函数.对象和继承,前面已经介绍过函数系列.从本系列开始介绍对象部分,本文是该系列的第一篇——初识对象 对象定义 javascript的基本数据类型包括undefined.null.boolean.string.number和object.对象和其他基本类型值不同的是,对象是一种复合值:它将许多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值 于是,对象也可看做是属性的无序集合,每个属性都是一个

javascript运动系列第一篇——运动函数

× 目录 [1]简单运动 [2]定时器管理 [3]分享到效果[4]移入移出[5]运动函数[6]透明度[7]多值[8]多物体 前面的话 除了拖拽以外,运动也是javascript动画的一个基本操作.通过CSS属性transition和animation可以实现运动.但是,要进行更精细地操作,javascript运动是必不可少的.本文将详细介绍javascript运动 简单运动 让一个元素在页面中运动起来很简单,设置定时器,改变定位元素的left或top值即可 <button id="btn&

javascript动画系列第二篇——磁性吸附

× 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性吸附的效果 范围限定 如果我们限定元素只可以在可视范围内移动,那么就需要对其进行范围限定 首先,先要搞清楚是可视区域限定被拖拽元素 左侧范围L0 = 0 右侧范围R0 = document.documentElement.clientWidth 上侧范围T0 = 0 下侧范围B0 = documen

javascript面向对象系列第一篇——构造函数和原型对象

× 目录 [1]构造函数 [2]原型对象 [3]总结 前面的话 一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同.本文将详细介绍如何用构造函数和原型对象来创建对象 构造函数 构造函数是用new创建对象时调用的函数,与普通唯一的区别是构造函数名应该首字母大写 function Person(){ this.age = 30; } var person1 = new Person(); console.log(person1.ag

JavaScript动画-模拟拖拽

模拟拖拽的原理: x1等于div.offsetLeft y1等于div.offsetTop x2等于ev.clientX(ev表示event事件) y2等于ev.clientY 当我们在方块上按下鼠标的时候,x2-x1即可确定.移动鼠标之后,我们用鼠标当前的位置即x4.y4减去x2-x1.y2-y1就可以得到方块现在的位置. 效果图:点击查看 代码: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 &l

javascript动画系列第五篇——模拟滚动条

× 目录 [1]原理介绍 [2]数字加减 [3]元素尺寸[4]内容滚动 前面的话 当元素内容溢出元素尺寸范围时,会出现滚动条.但由于滚动条在各浏览器下表现不同,兼容性不好.所以,模拟滚动条也是很常见的应用.本文将详细介绍滚动条模拟 原理介绍 滚动条模拟实际上和元素模拟拖拽类似.仅仅通过范围限定,使元素只可以在单一方向上拖拽 <div id="box" style="height: 200px;width: 16px;background-color:#F5F5F5;bo

深入理解表单脚本系列第一篇——表单对象

× 目录 [1]表单属性 [2]表单事件 [3]表单方法 前面的话 javascript最初的一个应用就是分担服务器处理表单的责任,打破处处依赖服务器的局面.尽管目前的web和javascript已经有了长足的发展,但web表单的变化并不明显.由于web表单没有为许多常见任务提供现成的解决方法,很多开发人员不仅会在验证表单时使用javascript,而且还增强了一些标准表单控件的默认行为.本文是表单脚本系列第一篇——表单对象 表单属性 在HTML中,表单是由form元素来表示的,而在javasc

深入理解javascript作用域系列第二篇——词法作用域和动态作用域

× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作模型导致的,作用域分为词法作用域和动态作用域,分清这两种作用域模型就能够对变量查找过程有清晰的认识.本文是深入理解javascript作用域系列第二篇——词法作用域和动态作用域 词法作用域 第一篇介绍过,编译器的第一个工作阶段叫作分词,就是把由字符组成的字符串分解成