React.js实现原生js拖拽效果及思考
一、起因&思路
不知不觉,已经好几天没写博客了。。。近来除了研究React,还做了公司官网。。。
一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨。所以就用react来实现这个拖拽效果。
首先,其实拖拽效果的思路是很简单的。主要就是三个步骤:
1.onmousedown的时候,启动可拖拽事件,记录被拖拽元素的原始坐标参数。
2.onmousemove的时候,实时记录鼠标移动的距离,结合被拖拽元素第一阶段的坐标参数,计算并设置新的坐标值。
3.onmouseup的时候,关闭可拖拽事件,记录新的坐标值。
注意:这里主要是通过绝对定位的top和left来确定元素的位置的,因此被拖拽元素的css一定要设置绝对定位。
二、辅助工具
辅助工具主要就是是开发过程变得高效,而且酷炫的。在这个demo中,要给大家推荐一个gulp+browser-sync的开发工具,gulp有很多功能,在这个demo中gulp的作用主要是可以设置实时编译react中的jsx文件,当然如果你写css用的是sass,也可以设置实时编译sass。用browser-sync这个呢,主要就是可以自动实时刷新页面,我们平时做页面,看效果的时候,通常都是通过F5来刷新浏览器,然后看到页面的。但是用了这个插件,你写完代码的时候,只要按下,ctrl+s保存,新的效果就会自动在浏览器中刷新,然后看得到了。
用法详解:
安装:
1.在node的环境下,安装gulp,这里就不详说了,具体过程可参考我的博文《react.js入门必须知道的那些事》
2.安装gulp-livereload,在命令行或者git bash ,输入npm install --save-dev gulp-livereload
3.安装gulp-watch,在命令行或者git bash ,输入npm install --save-dev gulp-watch
4.安装browser-sync,在命令行或者git bash ,输入npm install --save-dev browser-sync
配置及解释如图:
三、定义组件构建页面
备注:这里的代码说明均在react相关模块安装好的情况下,安装过程见我的博文《react.js入门必须知道的那些事》.
效果图:
组件拆分思路:
我当时觉得组件拆分得细一点好,所以我把input、button分别做成了一个组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var React=require('react');
var MyInput=React.createClass({
render:function(){
return (
< div className="form-group">
< label htmlFor={this.props.labelId} className="col-sm-2 control-label{this.props.labelTip</label>
< div className="col-sm-10">
< input name={this.props.name} type={this.props.type} onChange={this.props.onChange} className="form-control" id={this.props.labelId} placeholder={this.props.placeholder}/>
</ div >
</ div >
);
}
});
module.exports=MyInput;
|
1
2
3
4
5
6
7
8
9
10
11
|
var React=require( 'react' );
var Button=React.createClass({
render: function (){
return (
<button type={ this .props.type} className= "loginButton" >{ this .props.ButtonTip}</button>
);
}
})
module.exports=Button;
|
由于input有很多都是需要指定的,这种情况下,如果像我这样定义需要传太多参数,而且其实登陆的input大多都是固定且没必要复用的,所以这样其实不大好。这里的input直接写比较好。
写好之后的父组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
render: function (){
return (
<form className= "form-horizontal" id= "form" ref= "dragBox" onSubmit={ this .submitHandler} onMouseMove={ this .move} onMouseUp={ this .endDrag}>
<DragArea callbackParent={ this .onChildChanged} />
<div id= "form-wrap" >
<MyInput name= "username" labelId={ "userId" } labelTip={ "用户名" } type={ "text" } placeholder={ "请输入用户名" } value={ this .state.username} onChange={ this .handleChange}/>
<MyInput name= "password" labelId={ "pw" } labelTip={ "密码" } type={ "password" } placeholder={ "请输入密码" } value={ this .state.password} onChange={ this .handleChange}/>
<div className= "form-group" >
<div className= "col-sm-offset-2 col-sm-10" >
<div className= "checkbox" >
<label>
<input name= "checked" type= "checkbox" checked={ this .state.checked} onChange={ this .handleChange} /> 记住我
</label>
</div>
</div>
</div>
<MyButton type={ "submit" } ButtonTip={ "登陆" }/>
</div>
</form>
);
|
备注:因为demo中需要获取真实的dom节点,所以定义了ref。
再加上css样式,页面就完成啦!最后,重点来啦!!!
四、父子组件间通信实现拖拽
说明:由于我要实现的效果是,鼠标按住子组件DragArea的时候,拖动的是整个form,所以启动拖拽的是DragArea,而响应的是form。所以,一开始必须把父组件的一些状态属性传给子组件,然后鼠标在DragArea按下的的时候,必须通过子组件DragArea找到父组件的原始坐标参数,然后更新父组件里面的状态属性,并且告诉父组件可以进行拖拽了。父组件给子组件传参就是直接传递的。而子组件给父组件传参需要通过事件。所以在父组件中定义这么一个函数:
1
2
3
|
onChildChanged: function (newState){ //因为参数过多,所以把参数放到对象里面,通过对象来传
this .setState(newState);
},
|
而子组件需要绑定这个函数,如上面的代码:callbackParent={this.onChildChanged}
在子组件中,响应的函数为:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
startDrag: function (e){
var dragBox=document.getElementById( 'form' );
var newState={};
var event=e||window.event;
event.preventDefault();
var computedStyle=document.defaultView.getComputedStyle(dragBox, null );
newState.left=computedStyle.left;
newState.top=computedStyle.top;
newState.currentX=event.clientX;
newState.currentY=event.clientY;
newState.flag= true ;
this .props.callbackParent(newState);
}
|
这样,在子组件中就启动了拖拽开关,并且已经更新了from的相关参数,from的两外两个事件,move和endDrag分别为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
move: function (event){
var e = event ? event : window.event; //兼容IE的写法
if ( this .state.flag) {
var nowX = e.clientX, nowY = e.clientY;
var disX = nowX - this .state.currentX, disY = nowY - this .state.currentY;
ReactDOM.findDOMNode( this .refs.dragBox).style.left = parseInt( this .state.left) + disX + "px" ;
ReactDOM.findDOMNode( this .refs.dragBox).style.top = parseInt( this .state.top) + disY + "px" ;
}
},
endDrag: function (){
var computedStyle=document.defaultView.getComputedStyle(ReactDOM.findDOMNode( this .refs.dragBox), null );
this .setState({
left:computedStyle.left,
top:computedStyle.top,
flag: false
});
}
|
至此,拖拽实现!
五、反思回顾
1.理论上来说,拖拽效果可以在任意元素中实现,拖拽的思路都是一致的,所以理论上来说,拖拽各个过程的函数可以抽离出来,做成一个Mixin,然后可以反复调用。我一开始的思路就是这样,但是在传参、响应、绑定元素上面总是出错。查找了一下资料,没找到react与拖拽的简单写法资料,只有一些react的专用插件,而且是用ES6的写法,由于现在的水平还没能看懂。所以暂时放弃了这种写法。希望有相关想法的大神们和我交流一下。
2.文中子组件获取from的参数时,用了var dragBox=document.getElementById('form');去找dom,这样好像违反了react的一些理念。但是我还不是很熟悉该怎么从子组件获取父组件的dom。我试过在父组件定义refs=this.refs.dragBox。然后传给子组件,但是不知道为什么浏览器一直报错说这个不是dom节点。求大神指教。
3.拖拽事件的一般写法,是在document上面定义mousemove和mouseup事件,但是这两个事件都关联到from的参数,这样的话,如果我在react中定义在document,就跟踪不了相关参数。所以我就定义在了from上面。是不是有更好的方法呢?求分享!
4.革命尚未成功,同志仍需努力!
React.js实现原生js拖拽效果及思考相关推荐
- 【加强版】js原生实现拖拽效果,这次没有用document的mousedown、mousemove、mouseup事件我们来点实际的(但是有个弊端:拖拽过程中鼠标会变成一个禁用符号,不太友好)
<div class='dragged'></div> //初始化需要拖拽的列initDrags() {var arr = document.querySelectorAll( ...
- 【墙裂推荐】【原生基础版】js原生实现拖拽效果,注意不要忘了div的cursor用grab和grabbing 还是古法炮制、传统工艺的原生代码兼容性最好,推荐
以下方式的劣势就是在放弃拖拽那一刻会触发click事件,通常如果被拖拽元素还有其他点击事件,会重复触发,往往并非业务需求.优势就是-额-貌似这段代码没什么屌优势! <div class='dra ...
- html设置图片不可拖拽,js css3实现图片拖拽效果
本文实例为大家分享了css3实现图片拖拽效果的具体代码,供大家参考,具体内容如下 body{ text-align: center; } .container{ display: flex; just ...
- 有趣且重要的css知识合集(1)纯css实现div左右拖拽效果
目前我看主流的都是用js来实现div拖拽效果,不过我个人觉得一直监听拖拽,会耗费很大的性能,毕竟监听盒子的宽高这些属性,都会触发浏览器的回流,并且我看了很多博客,他们是能用css实现div左右拖拽效果 ...
- html5 原生拖拽,原生JS实现拖拽效果
这篇文章主要为大家详细介绍了原生JS实现拖拽效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了JS实现拖拽效果的具体代码,供大家参考,具体内容如下 ...
- 盒子拖拽效果,原生js实现
原生js实现拖拽效果 <!DOCTYPE html> <html lang="en"> <head><meta charset=" ...
- 原生拖拽太拉跨了,纯JS自己手写一个拖拽效果,纵享丝滑
前言 提到元素拖拽,通常都会先想到用 HTML5 的拖拽放置 (Drag 和 Drop) 来实现,它提供了一套完整的事件机制,看起来似乎是首选的解决方案,但实际却不是那么美好,主要是它的样式太过简陋, ...
- 原生JS实现鼠标按下拖拽效果
原生JS实现鼠标按下拖拽效果 <!DOCTYPE html> <html lang="en"><head><meta charset=&q ...
- html实现鼠标拖拽按钮,JS实现鼠标按下拖拽效果
原生js实现鼠标按下拖拽效果,供大家参考,具体内容如下 鼠标拖动 body { margin: 0; } div { width: 200px; height: 200px; position: ab ...
最新文章
- 没有插件的 Chrome 是没有灵魂的
- Leetcode 剑指 Offer 03. 数组中重复的数字 (每日一题 20210614)
- (转)怎么实时查看mysql当前连接数
- boost::histogram::accumulators用法的测试程序
- oracle 数据库启动停止小结
- MIT深度学习课堂开课啦,自动驾驶圈大牛排队来讲课丨资源
- “请求未在nginx中配置的域名时,给浏览器返回508错误码”配置示例
- 雷林鹏分享:Ruby 发送邮件 - SMATP
- html 时钟怎样居中,怎么用css3做时钟刻度
- 数据挖掘概念与技术(第三版)课后答案——第四章
- 基于Node.js打造Web在线聊天室
- c# 定义四边形_C# 绘制多边形
- 【汇编语言】纯汇编语言编写打飞机小游戏
- 一个创业失败案例的复盘
- 堆叠沙漏网络(stacked hourglass network)
- 13 个非常有用的 Python 代码片段,建议收藏!
- php中subtr()函数的使用方法
- 乐视:基于Docker的RDS,我们是这样做的
- C#循环体内定义对象/变量
- 思维导图工具,如何改默认字体?
热门文章
- AJAX框架眼镜穿搭夏天,20套夏天穿搭!我帮你整理出来了
- 中考计算机考试作文,准备中考的作文4篇
- 齿轮与减速器输出端连接方式
- 简单家乡风景静态HTML网页设计作品 DIV布局家乡介绍网页模板代码 DW个人网站制作成品 web网页制作与实现
- idea中设置文件头自动设置@author@date
- 碱性干电池的内阻测试方法_实测南孚一号干电池内阻
- python网页查询然后返回结果_python(30) 获取网页返回的状态码,状态码对应问题查询...
- office出现应用程序错误无法正常启动(0xc0000142)
- 记一次小程序接口本地可访问,真机不能访问的问题
- 40款非常酷的国外创意名片设计欣赏(下)