React で画像をトリミングする方法

php中世界最好的语言
リリース: 2018-06-07 13:53:21
オリジナル
1945 人が閲覧しました

今回はReactで画像をトリミングする方法と、Reactで画像をトリミングする際の注意点を紹介します。実際のケースを見てみましょう。

を始めて 1 年以上 Vue を書いてきましたが、ボトルネックに達したと感じています。その感覚を知るために React を勉強します。たまたま最近、Vue を使用して CropperJS に基づいた画像トリミング コンポーネントを作成したため、数晩かけて React を使用して再度作成しました。コードアドレス

このプロジェクトは create-react-app を使用して開発されており、使用前に eslint、自動更新、その他の機能をサポートしています。初めて反応する人も試してみることをおすすめします。

このプロジェクトは比較的簡単に作成でき、カスタム構成も比較的貧弱ですが、画像をトリミングするという基本的な機能も完成しているので、初めて反応し、画像のトリミング コンポーネントを理解したいと考えている友人に役立つことを願っています。 。

コンポーネントの構造はこんな感じです。

<!--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 が行う主な処理は、画像をアップロードし、入力の変更イベントをリッスンし、親コンポーネント Cropper の handleImgChange メソッドを呼び出して、img 要素にバインドされた imageValue を設定することです。これにより、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 であり、left、right、bottom に対応するものはそれぞれ 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 は選択ボックスを切り取るときのパラメーター、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 => {
  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() {
  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 メソッドを呼び出します。react のイベント メカニズムの特性により、stopPropagation を使用する必要があることに注意してください。イベントバブリングを禁止するイベントリスナーの第3パラメータとして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)
 }
ログイン後にコピー
カットの方法は、右側、下側、右下側のストレッチの2つの状況に分かれています。もう一つは左側、上側、左上側のストレッチです。

最初のケースでは、選択ボックスの位置は変更されず、サイズのみが変更され、処理は比較的簡単です。新しいサイズは、元のサイズに現在のマウス位置を加えたものから、ドラッグ開始時のマウス位置を引いたものになります。幅または高さのいずれかが標準を超える場合、そのサイズは境界に達するギリギリのサイズに設定されます。すべて基準を超えており、新しいサイズに設定されています。

2番目のケースでは、選択ボックスの位置とサイズが同時に変更され、境界を超えないようにサイズと位置を同時に制御する必要があります。

 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 中国語 Web サイトの他の関連記事に注目してください。

推奨読書:

vueを使ってdivを非表示にする方法


vuexの状態オブジェクトの使い方のまとめ

以上がReact で画像をトリミングする方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!