이번에는 React에서 이미지를 자르는 방법과 React에서 이미지를 자를 때 주의사항이 무엇인지 알려드리겠습니다. 실제 사례를 살펴보겠습니다.
저는
을 시작하고 1년 넘게 Vue를 작성하면서 병목 현상을 겪은 것 같습니다. 그 느낌을 알아보기 위해 React를 배우겠습니다. 최근에 Vue를 사용하여 CropperJS 기반의 이미지 자르기 구성 요소를 작성했기 때문에 React를 사용하여 다시 작성하는 데 며칠 밤을 보냈습니다. 코드 주소
이 프로젝트는 webpack 구성에 많은 노력을 절약하는 create-react-app을 사용하여 개발되었습니다. eslint, 자동 새로 고침 및 기타 기능을 사용하기 전에 npm 설치 및 시작만 하면 됩니다. 반응이 처음이신 분들도 한번 시도해 보시는 것을 추천합니다.
이 프로젝트는 작성이 비교적 간단하고 사용자 정의 구성이 상대적으로 열악하지만 이미지 자르기의 기본 기능도 완료했습니다. 처음 반응하고 이미지 자르기 구성 요소를 이해하고 싶은 친구들에게 도움이 되기를 바랍니다. .
컴포넌트의 구조는 이렇습니다.
<!--Cropper--> <p> <ImageUploader handleImgChange={this.handleImgChange} getCropData={this.getCropData}/> <p className="image-principal"> <img src={this.state.imageValue} alt="" className="img" ref="img" onLoad={this.setSize}/> <SelectArea ref="selectArea"></SelectArea> </p> </p> <!--ImageUploader --> <form className="image-upload-form" method="post" encType="multipart/form-data" > <input type="file" name="inputOfFile" ref="imgInput" id="imgInput" onChange={this.props.handleImgChange}/> <button onClick={this.props.getCropData}>获取裁剪参数</button> </form> <!--SelectArea --> <p className="select-area" onMouseDown={ this.dragStart} ref="selectArea" > <p className="top-resize" onMouseDown={ event => this.resizeStart(event, 'top')}></p> <p className="right-resize" onMouseDown={ event => this.resizeStart(event, 'right')}></p> <p className="bottom-resize" onMouseDown={ event => this.resizeStart(event, 'bottom')}></p> <p className="left-resize" onMouseDown={ event => this.resizeStart(event, 'left')}></p> <p className="right-bottom-resize" onMouseDown={ event => this.resizeStart(event, 'right')}></p> <p className="left-top-resize" onMouseDown={ event => this.resizeStart(event, 'left')}></p> </p>
ImageUploader & Cropper
ImageUploader가 하는 주요 작업은 이미지를 업로드하고, 입력의 변경 이벤트를 수신하고, img 요소에 바인딩된 imageValue를 설정하는 상위 구성 요소 Cropper의 handlerImgChange 메서드를 호출하는 것입니다. 그러면 img 요소가 로드 이벤트를 트리거하게 됩니다.
handleImgChange = e => { let fileReader = new FileReader() fileReader.readAsDataURL(e.target.files[0]) fileReader.onload = e => { this.setState({...this.state, imageValue: e.target.result}) } }
load 이벤트는 이미지와 자르기 선택 상자의 초기 위치와 크기를 설정할 수 있는 Cropper의 setSize 메서드를 트리거합니다. 현재 자르기 선택 상자의 기본 설정은 크기가 이미지의 80%로 중앙에 표시되는 것입니다.
setSize = () => { let img = this.refs.img let widthNum = parseInt(this.props.width, 10) let heightNum = parseInt(this.props.height, 10) this.setState({ ...this.state, naturalSize: { width: img.naturalWidth, height: img.naturalHeight } }) let imgStyle = img.style imgStyle.height = 'auto' imgStyle.width = 'auto' let principalStyle = ReactDOM.findDOMNode(this.refs.selectArea).parentElement.style const ratio = 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 } // 设置选择框样式 let selectAreaStyle = ReactDOM.findDOMNode(this.refs.selectArea).style let principalHeight = parseInt(principalStyle.height, 10) let principalWidth = 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() let SelectArea = ReactDOM.findDOMNode(this.refs.selectArea).style let a = { 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) return a }
SelectArea
selectArea의 구조를 재생합니다. .top-resize의 커서 속성은 n-resize이고 왼쪽, 오른쪽, 아래쪽에 해당하는 속성은 각각 w-resize, e-resize, s-resize라는 점에 유의하세요.
<p className="select-area" onMouseDown={ this.dragStart} ref="selectArea" > <p className="top-resize" onMouseDown={ event => this.resizeStart(event, 'top')}></p> <p className="right-resize" onMouseDown={ event => this.resizeStart(event, 'right')}></p> <p className="bottom-resize" onMouseDown={ event => this.resizeStart(event, 'bottom')}></p> <p className="left-resize" onMouseDown={ event => this.resizeStart(event, 'left')}></p> <p className="right-bottom-resize" onMouseDown={ event => this.resizeStart(event, 'right')}></p> <p className="left-top-resize" onMouseDown={ event => this.resizeStart(event, 'left')}></p> </p>
상태 값 selectArea가 이것으로 설정되고, selectArea는 선택 상자를 드래그할 때 매개변수를 저장하고, resizeArea는 선택 상자를 자를 때 매개변수를 저장하며, 컨테이너는 .image-principal 요소이고, el은 이벤트가 트리거될 때 event.target입니다.
this.state = { selectArea: null, el: null, container: null, resizeArea: null }
선택 상자를 드래그하세요
.select-area에서 마우스를 누르고 mouseDown 이벤트를 트리거한 다음 dragStart 메소드를 호출하세요.
메서드 = e => {} 형식을 사용하면 jsx에서 this.method.bind(this) 사용을 피할 수 있습니다
이 메서드에서는 먼저 마우스를 눌렀을 때의 마우스 위치와 두 키 사이의 상대적인 관계를 저장합니다. 자르기 상자와 이미지 거리 및 자르기 상자의 최대 변위 거리를 입력한 다음
dragStart = e => { const el = e.target const container = this.state.container let selectArea = { 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) }
move 메서드에서 이벤트 리스너
this.moveBind = this.move.bind(this) this.stopBind = this.stop.bind(this)
moveBind 및 stopBind를 추가합니다. 마우스 이동 중에 새로운 상대 위치 newPosLeft 및 newPosTop이 다음에 따라 계산됩니다. 새로운 마우스 위치를 기록하고 값을 적절한 범위 내에 있도록 제어합니다
move(e) { if (!this.state || !this.state.el || !this.state.selectArea) { return } let selectArea = this.state.selectArea let newPosLeft = e.clientX- selectArea.left let newPosTop = e.clientY - selectArea.top // 控制移动范围 if (newPosLeft <= 0) { newPosLeft = 0 } else if (newPosLeft > selectArea.maxMoveX) { newPosLeft = selectArea.maxMoveX } if (newPosTop <= 0) { newPosTop = 0 } else if (newPosTop > selectArea.maxMoveY) { newPosTop = selectArea.maxMoveY } let elStyle = this.state.el.style elStyle.left = newPosLeft + 'px' elStyle.top = newPosTop + 'px' }
stop 메소드, 이벤트 리스너 제거, 상태 지우기 및 잘못된 메소드 호출 방지
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}) }
자르기 선택 상자
드래그 및 drop에서는 먼저 resizeStart 메서드를 호출하여 자르기가 시작되는 마우스 위치를 저장하고, resizeBind 및 stopBind에 대한 이벤트 리스너를 추가하려면 stopPropagation을 사용해야 합니다. 이벤트 버블링을 금지합니다. 이벤트 리스너의 세 번째 매개변수로 false를 사용하는 것은 유효하지 않습니다.
resizeStart = (e, type) => { e.stopPropagation() const el = e.target.parentElement let resizeArea = { 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 } let container = this.state.container const containerHeight = container.offsetHeight const containerWidth = container.offsetWidth const containerLeft = parseInt(container.style.left || 0, 10) const containerTop = parseInt(container.style.top || 0, 10) let resizeArea = this.state.resizeArea let el = this.state.el let elStyle = el.style if (type === 'right' || type === 'bottom') { let length 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) { const w = containerWidth - parseInt(el.style.left, 10) const h = 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 { let posChange let newPosLeft let newPosTop 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 < containerTop) { // 让选择框到图片最左边 let newPosLeft2 = resizeArea.left -containerLeft // 判断顶部会不会超出边界 if (newPosLeft2 < resizeArea.top) { // 未超出边界 elStyle.top = resizeArea.top - newPosLeft2 + 'px' elStyle.left = containerLeft + 'px' } else { // 让选择框到达图片顶部 elStyle.top = containerTop + 'px' elStyle.left = resizeArea.left + containerTop - resizeArea.top + 'px' } } else { if (newPosLeft < 0) { elStyle.left = 0; elStyle.width = Math.min(resizeArea.width + posChange - newPosLeft, containerWidth) + 'px' elStyle.top = newPosTop - newPosLeft; elStyle.height = Math.min(resizeArea.height + posChange - newPosLeft, containerHeight) + 'px' return; } if (newPosTop < 0) { elStyle.left = newPosLeft - newPosTop; elStyle.width = Math.min(resizeArea.width + posChange - newPosTop, containerWidth) + 'px' elStyle.top = 0; elStyle.height = Math.min(resizeArea.height + posChange - newPosTop, containerHeight) + 'px' return; } elStyle.left = newPosLeft + 'px' elStyle.top = newPosTop + 'px' elStyle.width = resizeArea.width + posChange + 'px' elStyle.height = resizeArea.height + posChange + 'px' } } }
이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!
추천 도서:
위 내용은 반응으로 이미지를 자르는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!