this.resizeStart(event,'left')}>
ImageUploader&Cropper
ImageUploader主要做的就是上传图片,监听了input的change事件,并调用了父组件Cropper的的handleImgChange方法,该方法设置了绑定到img元素的imageValue,会使得img元素出发load事件。
handleImgChange=e=>{
letfileReader=newFileReader()
fileReader.readAsDataURL(e.target.files[0])
fileReader.onload=e=>{
this.setState({...this.state,imageValue:e.target.result})
}
}
load事件触发了Cropper的setSize方法,该方法可以设置了图片和裁剪选择框的初始位置和大小。目前裁剪选择框是默认设置是大小为图片的80%,中间显示。
setSize=()=>{
letimg=this.refs.img
letwidthNum=parseInt(this.props.width,10)
letheightNum=parseInt(this.props.height,10)
this.setState({
...this.state,
naturalSize:{
width:img.naturalWidth,
height:img.naturalHeight
}
})
letimgStyle=img.style
imgStyle.height='auto'
imgStyle.width='auto'
letprincipalStyle=ReactDOM.findDOMNode(this.refs.selectArea).parentElement.style
constratio=img.width/img.height
//设置图片大小、位置
if(img.width>img.height){
imgStyle.width=principalStyle.width=this.props.width
imgStyle.height=principalStyle.height=widthNum/ratio+'px'
principalStyle.marginTop=(widthNum-parseInt(principalStyle.height,10))/2+'px'
principalStyle.marginLeft=0
}else{
imgStyle.height=principalStyle.height=this.props.height
imgStyle.width=principalStyle.width=heightNum*ratio+'px'
principalStyle.marginLeft=(heightNum-parseInt(principalStyle.width,10))/2+'px'
principalStyle.marginTop=0
}
//设置选择框样式
letselectAreaStyle=ReactDOM.findDOMNode(this.refs.selectArea).style
letprincipalHeight=parseInt(principalStyle.height,10)
letprincipalWidth=parseInt(principalStyle.width,10)
if(principalWidth>principalHeight){
selectAreaStyle.top=principalHeight*0.1+'px'
selectAreaStyle.width=selectAreaStyle.height=principalHeight*0.8+'px'
selectAreaStyle.left=(principalWidth-parseInt(selectAreaStyle.width,10))/2+'px'
}else{
selectAreaStyle.left=principalWidth*0.1+'px'
selectAreaStyle.width=selectAreaStyle.height=principalWidth*0.8+'px'
selectAreaStyle.top=(principalHeight-parseInt(selectAreaStyle.height,10))/2+'px'
}
}
Cropper上还有一个getCropData方法,方法会打印并返回裁剪参数,
getCropData=e=>{
e.preventDefault()
letSelectArea=ReactDOM.findDOMNode(this.refs.selectArea).style
leta={
width:parseInt(SelectArea.width,10),
height:parseInt(SelectArea.height,10),
left:parseInt(SelectArea.left,10),
top:parseInt(SelectArea.top,10)
}
a.radio=this.state.naturalSize.width/a.width
console.log(a)
returna
}
SelectArea
重新放一遍selectArea的结构。要注意,.top-resize的cursor属性是n-resize,而和left,right,bottom对应的分别是w-resize,e-resize,s-resize
this.resizeStart(event,'top')}>
this.resizeStart(event,'right')}>
this.resizeStart(event,'bottom')}>
this.resizeStart(event,'left')}>
this.resizeStart(event,'right')}>
this.resizeStart(event,'left')}>
selectArea的state值设为这样,selectArea保存拖拽选择框时的参数,resizeArea保存裁剪选择框时的参数,container为.image-principal元素,el为触发事件时的event.target。
this.state={
selectArea:null,
el:null,
container:null,
resizeArea:null
}
拖拽选择框
在.select-area按下鼠标,触发mouseDown事件,调用dragStart方法。
使用method=e=>{}的形式可以避免在jsx中使用this.method.bind(this)
在这个方法中,首先保存按下鼠标时的鼠标位置,裁剪框与图片的相对距离和裁剪框的最大位移距离,接着添加事件监听
dragStart=e=>{
constel=e.target
constcontainer=this.state.container
letselectArea={
posLeft:e.clientX,
posTop:e.clientY,
left:e.clientX-el.offsetLeft,
top:e.clientY-el.offsetTop,
maxMoveX:container.offsetWidth-el.offsetWidth,
maxMoveY:container.offsetHeight-el.offsetHeight,
}
this.setState({...this.state,selectArea,el})
document.addEventListener('mousemove',this.moveBind,false)
document.addEventListener('mouseup',this.stopBind,false)
}
moveBind和stopBind来自于
this.moveBind=this.move.bind(this)
this.stopBind=this.stop.bind(this)
move方法,在鼠标移动中根据记录新的鼠标位置来计算新的相对位置newPosLeft和newPosTop,并控制该值在合理范围内
move(e){
if(!this.state||!this.state.el||!this.state.selectArea){
return
}
letselectArea=this.state.selectArea
letnewPosLeft=e.clientX-selectArea.left
letnewPosTop=e.clientY-selectArea.top
//控制移动范围
if(newPosLeft<=0){
newPosLeft=0
}elseif(newPosLeft>selectArea.maxMoveX){
newPosLeft=selectArea.maxMoveX
}
if(newPosTop<=0){
newPosTop=0
}elseif(newPosTop>selectArea.maxMoveY){
newPosTop=selectArea.maxMoveY
}
letelStyle=this.state.el.style
elStyle.left=newPosLeft+'px'
elStyle.top=newPosTop+'px'
}
stop方法,移除事件监听,清除state,避免方法错误调用
stop(){
document.removeEventListener('mousemove',this.moveBind,false)
document.removeEventListener('mousemove',this.resizeBind,false)
document.removeEventListener('mouseup',this.stopBind,false)
this.setState({...this.state,el:null,resizeArea:null,selectArea:null})
}
裁剪选择框
跟拖拽一样,首先调用resizeStart方法,保存开始裁剪的鼠标位置,裁剪框的尺寸和位置,添加关于resizeBind和stopBind的事件监听,注意,由于react的事件机制特点,需要使用stopPropagation来禁止事件冒泡,事件监听的第三个参数使用false是无效的。
resizeStart=(e,type)=>{
e.stopPropagation()
constel=e.target.parentElement
letresizeArea={
posLeft:e.clientX,
posTop:e.clientY,
width:el.offsetWidth,
height:el.offsetHeight,
left:parseInt(el.style.left,10),
top:parseInt(el.style.top,10)
}
this.setState({...this.state,resizeArea,el})
this.resizeBind=this.resize.bind(this,type)
document.addEventListener('mousemove',this.resizeBind,false)
document.addEventListener('mouseup',this.stopBind,false)
}
裁剪的方法,将裁剪分为两种情况,一种是右侧,下侧和右下侧的拉伸。另一种是左侧,上侧和左上侧的拉伸。
第一种情况下,选择框的位置是不会变的,只有尺寸会变,处理起来相对简单。新的尺寸大小为原大小加上当前的鼠标的位置再减去开始拖拽处的鼠标的位置,如果宽度或者高度有一个超标了,则将尺寸设置为刚好到边界的大小。均为超标,设置为新的尺寸。
第二种情况下,选择框的位置和大小同时会变,要同时控制尺寸和位置不超出边界。
resize(type,e){
if(!this.state||!this.state.el||!this.state.resizeArea){
return
}
letcontainer=this.state.container
constcontainerHeight=container.offsetHeight
constcontainerWidth=container.offsetWidth
constcontainerLeft=parseInt(container.style.left||0,10)
constcontainerTop=parseInt(container.style.top||0,10)
letresizeArea=this.state.resizeArea
letel=this.state.el
letelStyle=el.style
if(type==='right'||type==='bottom'){
letlength
if(type==='right'){
length=resizeArea.width+e.clientX-resizeArea.posLeft
}else{
length=resizeArea.height+e.clientY-resizeArea.posTop
}
if(parseInt(el.style.left,10)+length>containerWidth||parseInt(el.style.top,10)+length>containerHeight){
constw=containerWidth-parseInt(el.style.left,10)
consth=containerHeight-parseInt(el.style.top,10)
elStyle.width=elStyle.height=Math.min(w,h)+'px'
}else{
elStyle.width=length+'px'
elStyle.height=length+'px'
}
}else{
letposChange
letnewPosLeft
letnewPosTop
if(type==='left'){
posChange=resizeArea.posLeft-e.clientX
}else{
posChange=resizeArea.posTop-e.clientY
}
newPosLeft=resizeArea.left-posChange
//防止过度缩小
if(newPosLeft>resizeArea.left+resizeArea.width){
elStyle.left=resizeArea.left+resizeArea.width+'px'
elStyle.top=resizeArea.top+resizeArea.height+'px'
elStyle.width=elStyle.height='2px'
return
}
newPosTop=resizeArea.top-posChange
//到达边界
if(newPosLeft<=containerLeft||newPosTop
结束
通过这些组件的编写,感觉想要学好react,需要加深对this和事件模型的了解,这几天在这上面踩了不少的坑。如果觉得这篇文章有帮助的话,欢迎star我的项目
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。