Maison  >  Article  >  interface Web  >  Comment recadrer des images en réaction

Comment recadrer des images en réaction

php中世界最好的语言
php中世界最好的语言original
2018-06-07 13:53:211966parcourir

Cette fois, je vais vous montrer comment recadrer des images dans React, et quelles sont les précautions à prendre pour recadrer des images dans React. Ce qui suit est un cas pratique, jetons un coup d'oeil.

Démarrer

J'écris Vue depuis plus d'un an et je sens que j'ai rencontré un goulot d'étranglement. Je vais apprendre React pour découvrir ce sentiment. Il m'est récemment arrivé d'utiliser Vue pour écrire un composant de recadrage d'image basé sur cropperJS, j'ai donc passé quelques nuits à l'écrire à nouveau en utilisant React. Le projet d'adresse de code

est développé à l'aide de create-react-app, ce qui permet d'économiser beaucoup d'efforts dans la configuration du webpack. Il prend en charge eslint, l'actualisation automatique et d'autres fonctions. Il suffit d'installer npm et de démarrer npm avant utilisation. Il est recommandé aux personnes qui débutent de réagir de l'essayer également.

Le projet est relativement simple à écrire et la configuration personnalisée est relativement pauvre. Cependant, il complète également la fonction de base de recadrage des images. J'espère qu'il pourra aider les amis nouveaux à réagir et à comprendre. composant d'image de recadrage.

La structure du composant est comme ceci.

<!--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

La principale tâche d'ImageUploader est de télécharger des images, d'écouter l'événement de changement d'entrée et d'appeler la méthode handleImgChange du composant parent Cropper , Cette méthode définit la valeur imageValue liée à l'élément img, ce qui amènera l'élément img à déclencher l'événement de chargement.

 handleImgChange = e => {
  let fileReader = new FileReader()
  fileReader.readAsDataURL(e.target.files[0])
  fileReader.onload = e => {
   this.setState({...this.state, imageValue: e.target.result})
  }
 }

L'événement de chargement déclenche la méthode setSize de Cropper, qui peut définir la position et la taille initiales de l'image et de la zone de sélection de recadrage. Actuellement, le paramètre par défaut de la zone de sélection de recadrage est que la taille correspond à 80 % de l'image et s'affiche au milieu.

 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'
  }
 }

Il existe également une méthode getCropData sur Cropper, qui imprimera et renverra les paramètres de recadrage

 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

Play. encore une fois La structure de selectArea. Il convient de noter que l'attribut curseur de .top-resize est n-resize, et les gauche, droite et bas correspondants sont w-resize, e-resize et 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>

le valeur d'état de selectArea respectivement. Définissez comme ceci, selectArea enregistre les paramètres lorsque vous faites glisser la zone de sélection, resizeArea enregistre les paramètres lors du recadrage de la zone de sélection, le conteneur est l'élément .image-principal et el est l'événement.target lorsque l'événement est. déclenché.

  this.state = {
   selectArea: null,
   el: null,
   container: null,
   resizeArea: null
  }

Faites glisser la zone de sélection

Appuyez sur la souris dans la zone .select, déclenchez l'événement mouseDown et appelez la méthode dragStart.

L'utilisation de method = e => {} peut éviter d'utiliser this.method.bind(this) dans jsx

Dans cette méthode, enregistrez d'abord la souris lorsque la souris est enfoncée. Position, le distance relative entre le cadre de recadrage et l'image et la distance de déplacement maximale du cadre de recadrage, puis ajoutez des écouteurs d'événements

 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)
 }

moveBind et stopBind proviennent de la

 this.moveBind = this.move.bind(this)
 this.stopBind = this.stop.bind(this)

méthode move, lorsque la souris bouge Calculez les nouvelles positions relatives newPosLeft et newPosTop en fonction de l'enregistrement de la nouvelle position de la souris et contrôlez les valeurs dans une plage raisonnable

 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'
 }

méthode d'arrêt, supprimez l'écoute des événements, effacez l'état et éviter les appels de méthode incorrects

 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})
 }

Boîte de sélection de recadrage

Identique au glisser-déposer, appelez d'abord la méthode resizeStart, enregistrez la position de la souris où le recadrage commence, la taille et la position de la zone de recadrage, et ajoutez à propos de la surveillance des événements resizeBind et stopBind. Notez qu'en raison des caractéristiques du mécanisme d'événement de React, stopPropagation doit être utilisé pour empêcher la propagation d'événements. L'utilisation de false comme troisième paramètre de surveillance des événements n'est pas valide.

 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)
 }

La méthode de coupe divise la coupe en deux situations : l'une est l'étirement du côté droit, du côté inférieur et du côté inférieur droit. L'autre est l'étirement sur le côté gauche, le côté supérieur et le côté supérieur gauche.

Dans le premier cas, la position de la case de sélection ne changera pas, seule la taille changera, et le traitement est relativement simple. La nouvelle taille correspond à la taille d'origine plus la position actuelle de la souris moins la position de la souris au début du déplacement. Si la largeur ou la hauteur dépasse la norme, la taille est définie sur une taille qui atteint juste la limite. Tous dépassent la norme et sont définis dans de nouvelles tailles.

Dans le deuxième cas, la position et la taille de la case de sélection changeront en même temps. Il est nécessaire de contrôler la taille et la position en même temps pour ne pas dépasser la limite.

 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'
   }
  }
 }

Je pense que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web chinois de php !

Lecture recommandée :

Comment utiliser vue pour masquer les divs

Un résumé de la façon dont les objets d'état de vuex sont utilisés

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn