react中使用截圖組件Cropper組件


--最近項目用react,學習react並使用cropper組件裁剪圖片。

(這里開發組件不夠統一有用tsx(TypeScript + xml/html)寫的組件,有用jsx(javascript+xml/html)寫的組件

前言:cropper組件引入到項目中的手順直接看官方文檔;github:https://github.com/fengyuanchen/cropperjs#methods  在線演示url: https://fengyuanchen.github.io/cropper/

1.cropper組件以及各種操作的簡單封裝。

  react-cropper.js文件

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Cropper from 'cropperjs';

const optionProps = [
  'dragMode',
  'aspectRatio',
  'data',
  'crop',
  // unchangeable props start from here
  'viewMode',
  'preview',
  'responsive',
  'restore',
  'checkCrossOrigin',
  'checkOrientation',
  'modal',
  'guides',
  'center',
  'highlight',
  'background',
  'autoCrop',
  'autoCropArea',
  'movable',
  'rotatable',
  'scalable',
  'zoomable',
  'zoomOnTouch',
  'zoomOnWheel',
  'wheelZoomRatio',
  'cropBoxMovable',
  'cropBoxResizable',
  'toggleDragModeOnDblclick',
  'minContainerWidth',
  'minContainerHeight',
  'minCanvasWidth',
  'minCanvasHeight',
  'minCropBoxWidth',
  'minCropBoxHeight',
  'ready',
  'cropstart',
  'cropmove',
  'cropend',
  'zoom',
];

const unchangeableProps = optionProps;

class ReactCropper extends Component {
  componentDidMount() {
    const options = Object.keys(this.props)
      .filter(propKey => optionProps.indexOf(propKey) !== -1)
      .reduce((prevOptions, propKey) =>
        Object.assign({}, prevOptions, { [propKey]: this.props[propKey] }), {});
    this.cropper = new Cropper(this.img, options);

  }
  
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.src !== this.props.src) {
      this.cropper.reset().clear().replace(nextProps.src);
    }
    if (nextProps.aspectRatio !== this.props.aspectRatio) {
      this.setAspectRatio(nextProps.aspectRatio);
    }
    if (nextProps.data !== this.props.data) {
      this.setData(nextProps.data);
    }
    if (nextProps.dragMode !== this.props.dragMode) {
      this.setDragMode(nextProps.dragMode);
    }
    if (nextProps.cropBoxData !== this.props.cropBoxData) {
      this.setCropBoxData(nextProps.cropBoxData);
    }
    if (nextProps.canvasData !== this.props.canvasData) {
      this.setCanvasData(nextProps.canvasData);
    }
    if (nextProps.moveTo !== this.props.moveTo) {
      if (nextProps.moveTo.length > 1) {
        this.moveTo(nextProps.moveTo[0], nextProps.moveTo[1]);
      } else {
        this.moveTo(nextProps.moveTo[0]);
      }
    }
    if (nextProps.zoomTo !== this.props.zoomTo) {
      this.zoomTo(nextProps.zoomTo);
    }
    if (nextProps.rotateTo !== this.props.rotateTo) {
      this.rotateTo(nextProps.rotateTo);
    }
    if (nextProps.scaleX !== this.props.scaleX) {
      this.scaleX(nextProps.scaleX);
    }
    if (nextProps.scaleY !== this.props.scaleY) {
      this.scaleY(nextProps.scaleY);
    }
    if (nextProps.enable !== this.props.enable) {
      if (nextProps.enable) {
        this.enable();
      } else {
        this.disable();
      }
    }

    Object.keys(nextProps).forEach((propKey) => {
      let isDifferentVal = nextProps[propKey] !== this.props[propKey];
      const isUnchangeableProps = unchangeableProps.indexOf(propKey) !== -1;

      if (typeof nextProps[propKey] === 'function' && typeof this.props[propKey] === 'function') {
        isDifferentVal = nextProps[propKey].toString() !== this.props[propKey].toString();
      }

      if (isDifferentVal && isUnchangeableProps) {
        throw new Error(`prop: ${propKey} can't be change after componentDidMount`);
      }
    });
  }

  componentWillUnmount() {
    if (this.img) {
      // Destroy the cropper, this makes sure events such as resize are cleaned up and do not leak
      this.cropper.destroy();
      delete this.img;
      delete this.cropper;
    }
  }

  setDragMode(mode) {
    return this.cropper.setDragMode(mode);
  }

  setAspectRatio(aspectRatio) {
    return this.cropper.setAspectRatio(aspectRatio);
  }

  getCroppedCanvas(options) {
    return this.cropper.getCroppedCanvas(options);
  }

  setCropBoxData(data) {
    return this.cropper.setCropBoxData(data);
  }

  getCropBoxData() {
    return this.cropper.getCropBoxData();
  }

  setCanvasData(data) {
    return this.cropper.setCanvasData(data);
  }

  getCanvasData() {
    return this.cropper.getCanvasData();
  }

  getImageData() {
    return this.cropper.getImageData();
  }

  getContainerData() {
    return this.cropper.getContainerData();
  }

  setData(data) {
    return this.cropper.setData(data);
  }

  getData(rounded) {
    return this.cropper.getData(rounded);
  }

  crop() {
    return this.cropper.crop();
  }

  move(offsetX, offsetY) {
    return this.cropper.move(offsetX, offsetY);
  }

  moveTo(x, y) {
    return this.cropper.moveTo(x, y);
  }

  zoom(ratio) {
    return this.cropper.zoom(ratio);
  }

  zoomTo(ratio) {
    return this.cropper.zoomTo(ratio);
  }

  rotate(degree) {
    return this.cropper.rotate(degree);
  }

  rotateTo(degree) {
    return this.cropper.rotateTo(degree);
  }

  enable() {
    return this.cropper.enable();
  }

  disable() {
    return this.cropper.disable();
  }

  reset() {
    return this.cropper.reset();
  }

  clear() {
    return this.cropper.clear();
  }

  replace(url, onlyColorChanged) {
    return this.cropper.replace(url, onlyColorChanged);
  }

  scale(scaleX, scaleY) {
    return this.cropper.scale(scaleX, scaleY);
  }

  scaleX(scaleX) {
    return this.cropper.scaleX(scaleX);
  }

  scaleY(scaleY) {
    return this.cropper.scaleY(scaleY);
  }

  render() {
    const {
      src,
      alt,
      crossOrigin,
      style,
      className,
    } = this.props;

    return (
      <div
        style={style}
        className={className}
      >
        <img
          crossOrigin={crossOrigin}
          ref={(img) => { this.img = img; }}
          src={src}
          alt={alt === undefined ? 'picture' : alt}
          style={{ opacity: 0 }}
        />
      </div>
    );
  }
}

ReactCropper.propTypes = {
  style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
  className: PropTypes.string,

  // react cropper options
  crossOrigin: PropTypes.string,
  src: PropTypes.string,
  alt: PropTypes.string,

  // props of option can be changed after componentDidmount
  aspectRatio: PropTypes.number,
  dragMode: PropTypes.oneOf(['crop', 'move', 'none']),
  data: PropTypes.shape({
    x: PropTypes.number,
    y: PropTypes.number,
    width: PropTypes.number,
    height: PropTypes.number,
    rotate: PropTypes.number,
    scaleX: PropTypes.number,
    scaleY: PropTypes.number,
  }),
  scaleX: PropTypes.number,
  scaleY: PropTypes.number,
  enable: PropTypes.bool,
  cropBoxData: PropTypes.shape({
    left: PropTypes.number,
    top: PropTypes.number,
    width: PropTypes.number,
    height: PropTypes.number,
  }),
  canvasData: PropTypes.shape({
    left: PropTypes.number,
    top: PropTypes.number,
    width: PropTypes.number,
    height: PropTypes.number,
  }),
  zoomTo: PropTypes.number,
  moveTo: PropTypes.arrayOf(PropTypes.number),
  rotateTo: PropTypes.number,

  // cropperjs options
  // https://github.com/fengyuanchen/cropperjs#options
  // aspectRatio, dragMode, data
  viewMode: PropTypes.oneOf([0, 1, 2, 3]),
  preview: PropTypes.string,
  responsive: PropTypes.bool,
  restore: PropTypes.bool,
  checkCrossOrigin: PropTypes.bool,
  checkOrientation: PropTypes.bool,
  modal: PropTypes.bool,
  guides: PropTypes.bool,
  center: PropTypes.bool,
  highlight: PropTypes.bool,
  background: PropTypes.bool,
  autoCrop: PropTypes.bool,
  autoCropArea: PropTypes.number,
  movable: PropTypes.bool,
  rotatable: PropTypes.bool,
  scalable: PropTypes.bool,
  zoomable: PropTypes.bool,
  zoomOnTouch: PropTypes.bool,
  zoomOnWheel: PropTypes.bool,
  wheelZoomRatio: PropTypes.number,
  cropBoxMovable: PropTypes.bool,
  cropBoxResizable: PropTypes.bool,
  toggleDragModeOnDblclick: PropTypes.bool,
  minContainerWidth: PropTypes.number,
  minContainerHeight: PropTypes.number,
  minCanvasWidth: PropTypes.number,
  minCanvasHeight: PropTypes.number,
  minCropBoxWidth: PropTypes.number,
  minCropBoxHeight: PropTypes.number,
  ready: PropTypes.func,
  cropstart: PropTypes.func,
  cropmove: PropTypes.func,
  cropend: PropTypes.func,
  crop: PropTypes.func,
  zoom: PropTypes.func,
};

ReactCropper.defaultProps = {
  src: null,
  dragMode: 'crop',
  data: null,
  scaleX: 1,
  scaleY: 1,
  enable: true,
  zoomTo: 1,
  rotateTo: 0,
};

export default ReactCropper;

 

2.cropper組件調用的簡單封裝

CropperView.jsx文件

import React, { Component, useEffect } from 'react';
import $ from "jquery";
import Cropper from './cropper/react-cropper'
import 'cropperjs/dist/cropper.css'

/* global FileReader */
var showCropArea = true;

export default class CropperView extends Component {

  constructor(props) {
    super(props);
    this.state = {
      cropResult: null,
    };
    // this.cropper = this;
    this.onChange = this.onChange.bind(this);
    this.src = props.src;
  }

  // componentDidMount(){
  //   useEffect(() => {
  //     alert("cropZone" + this.cropper);
  //     // if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
  //     //   return;
  //     // }
  //     alert("left:" + this.cropper.getCropBoxData().left
  //         + "top:" + this.cropper.getCropBoxData().top
  //         + "width:" + this.cropper.getCropBoxData().width
  //         + "height:" + this.cropper.getCropBoxData().height);
  //       }, [this.props.save]);
  // }

  onChange(e) {
    e.preventDefault();
    let files;
    if (e.dataTransfer) {
      files = e.dataTransfer.files;
    } else if (e.target) {
      files = e.target.files;
    }
    const reader = new FileReader();
    reader.onload = () => {
      this.setState({ src: reader.result });
    };
    reader.readAsDataURL(files[0]);
  }

  cropZone() {
    if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
      return;
    }
    alert("left:" + this.cropper.getCropBoxData().left
        + "top:" + this.cropper.getCropBoxData().top
        + "width:" + this.cropper.getCropBoxData().width
        + "height:" + this.cropper.getCropBoxData().height);
        $(".cropper-crop-box").hide();
        $(".cropper-drag-box").hide();
        $(".cropper-wrap-box").append(
          "<div style=\"width:" + this.cropper.getCropBoxData().width + ";"
          + "height:" + this.cropper.getCropBoxData().height + ";"
          + "background:#0000FF; opacity:0.3"
          + "position:absolute; left:" + this.cropper.getCropBoxData().left +";"
          + "top:" + this.cropper.getCropBoxData().top + ";\">"); 
  }

  showCropZone() {
    if (showCropArea) {
      $(".cropper-crop-box").css("display", "block");
      $(".cropper-drag-box").css("display", "block");
    } else {
      $(".cropper-crop-box").hide();
      $(".cropper-drag-box").hide();
    }
  }

  creatCrop(){
    this.cropper.crop();
  }

  clearCrop(){
    this.cropper.clear();
  }

  resetCrop(){
    this.cropper.reset();
  }

  moveLeft(){
    this.cropper.move(-5, 0);
  }

  moveRight(){
    this.cropper.move(5, 0);
  }

  moveUp(){
    console.log("====moveUp===");
    try {
      this.cropper.move(0, -5);
    }catch (err){
        console.log(err);
    }
  }

  moveDown(){
    this.cropper.move(0, 5);
  }

  enlarge(){
    try {
      // 放大
      // this.cropper.zoom(0.1);
      var allCanvasDate = this.cropper.getCanvasData();
      var newCanvasDate = {left:allCanvasDate.left, top: allCanvasDate.top,
        width: allCanvasDate.width*2, height: allCanvasDate.height*2}

      this.cropper.setCanvasData(newCanvasDate);
     

    }catch (err){
        console.log(err);
    }
  
  }

  shrink(){
    try {
      // 縮小
      // this.cropper.zoom(-0.1)
      var allCanvasDate = this.cropper.getCanvasData();
      var newCanvasDate = {left:allCanvasDate.left, top: allCanvasDate.top,
        width: allCanvasDate.width*0.5, height: allCanvasDate.height*0.5}

      this.cropper.setCanvasData(newCanvasDate);
    }catch (err){
        console.log(err);
    }
  }

  test(){
    try {
      var allDate = this.cropper.getData(true);
      alert(allDate.toString());
      var par = {x:allDate.x, y:allDate.y, width:allDate.width*2, height:allDate.height*2,
        rotate:allDate.rotate, scaleX:allDate.scaleX, scaleY:allDate.scaleY}
        
      this.cropper.setData(par);
    }catch (err){
        console.log(err);
    }
  }

  moveCrop(){
    //this.cropper.movecrop();
    console.log("===move===crop===");
  }

  reduceCrop(){
    var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top,
      width:this.cropper.getCropBoxData().width*0.8, height:this.cropper.getCropBoxData().height*0.8}
    this.cropper.setCropBoxData(par);
  }

  raiseCrop(){
    var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top,
      width:this.cropper.getCropBoxData().width*1.2, height:this.cropper.getCropBoxData().height*1.2}
    this.cropper.setCropBoxData(par);
  }

  CropLeft(){
    var par = {left:this.cropper.getCropBoxData().left - 10, top:this.cropper.getCropBoxData().top,
      width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
    this.cropper.setCropBoxData(par);
  }

  CropRight(){
    var par = {left:this.cropper.getCropBoxData().left + 10, top:this.cropper.getCropBoxData().top,
      width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
    this.cropper.setCropBoxData(par);
  }

  CropUp(){
    var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top - 10,
      width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
    this.cropper.setCropBoxData(par);
  }

  CropDown(){
    var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top + 10,
      width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
    this.cropper.setCropBoxData(par);
  }

  render() {
    return (
      <div className="r-view" style={{ position:'absolute', left:'185px', top:"83px" }}>
        <Cropper
          style={{ height:'100%', width:'auto' }}
          aspectRatio={16 / 9}
          preview=".img-preview"
          guides={false}
          src={this.props.src}
          viewMode={2}
          minContainerWidth={585}
          minContainerHeight={430}
          ref={cropper => { this.cropper = cropper; }}
          zoomable={true}
          zoomOnTouch={true}
        />
      </div>
    );
  }
}

3.cropper組件與各種按鈕操作的綁定(原因:設備上不會支持手指在選擇區域的操作以及圖片的放大縮小操作),頁面整合組件。

  CropperScreen.tsx文件

import * as React from 'react';
import styled from "styled-components";
import useTranslate from "../../hooks/useTranslate";
import CropView from "./CropperView";
import 'cropperjs/dist/cropper.css'

import FormView from "./FormView"

const AppStyle = styled.div`
  background: #CCC;
`;

var showCropArea = false;

export default function CropperScreen() {
    const t = useTranslate();

    const handleBackClicked = () => {
        $("#viewable").show();
        $("#scan_settings_id").css("display","none");
    };

    const doCropClicked = () => {
        // showCropArea = !showCropArea;
        // if (showCropArea) {
        //     alert("disabled false");
        //     $("#btn_save_crop").removeAttr("disabled");
        // } else {
        //     alert("disabled true");
        //     $("#btn_save_crop").attr("disabled", "true");
        // }
        // cropUser.showCropZone(showCropArea);

        cropUser.clearCrop();
        
    };

    const doSaveCropClicked = () => {
        cropUser.cropZone();
    };

    const handReset = () => {
        cropUser.resetCrop();
        cropUser.creatCrop();
    }

    const handMoveLeft = () => {
        cropUser.moveLeft();
    }

    const handMoveRight = () => {
        cropUser.moveRight();
    }

    const handMoveUp = () => {
        console.log("====handMoveUp===");
        try {
            cropUser.moveUp();
        }catch (err){
            console.log(err);
        }
    }

    const handMoveDown = () => {
        cropUser.moveDown();
    }

    const handMoveCrop = () => {
        alert("unknow");
        cropUser.moveCrop();
    }

    const handReduceCrop = () => {
        cropUser.reduceCrop();
    }

    const handRaiseCrop = () => {
        cropUser.raiseCrop()
    }

    const handEnlarge = () => {
        cropUser.enlarge();
    }

    const handShrink = () => {
        cropUser.shrink();
    }

    const handCropLeft = () => {
        cropUser.CropLeft()
    }

    const handCropRight = () => {
        cropUser.CropRight()
    }

    const handCropUp = () => {
        cropUser.CropUp()
    }

    const handCropDown = () => {
        cropUser.CropDown()
    }

    const handTest = () => {
        cropUser.test()
    }

    const handleSubmit = () => {
        alert("====handleSubmit====");
        console.log(formUser);
        
        console.log(formUser.state);
    }

    let maxItem = 2;
    let cropUser
    let formUser
    var initValue = {name:'tom',job: '12'}

    return (
        <AppStyle id="scan_settings_id" className="r-view" style={{display:"none", height:"470px"}}>
            <header className="r-titlebar">
                <a href="#" className="r-titlebar__back" onClick={handleBackClicked} />
                <h1 className="r-titlebar__title">Preview</h1>
            </header>
            <div style={{position:"absolute", left:35}}>
            </div>
            <CropView ref={cropView => {cropUser = cropView}} src={require('../smartsdk-css/img/test.png')}/>
            <div className="r-floating-island">
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={doCropClicked}>Crop</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={doSaveCropClicked}>Save</button>
                </div>
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handReset}>reset</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveCrop}>mcrop</button>
                </div>
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveLeft}>left</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveRight}>right</button>
                </div>
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveUp}>up</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveDown}>down</button>
                </div>
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handEnlarge}>+</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handShrink}>-</button>
                </div>
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handReduceCrop}>reduce</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handRaiseCrop}>raise</button>
                </div>

                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropLeft}>cropr</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropRight}>cropl</button>
                </div>

                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropUp}>cropu</button>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropDown}>cropd</button>
                </div>
                <div>
                    <button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handTest}>test</button>
                </div>
                
               
                {/* <FormView handleSubmit={handleSubmit} ref={formView => {formUser = formView}} props = {initValue}/> */}
                
                {/* <button className="r-start-button">
                    {t("dapi:cba.common.start")}
                </button> */}
            </div>
        </AppStyle >
    );
}

4.最后調用整體的組件,頁面展示

頁面

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM