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.最后调用整体的组件,页面展示

页面

猜你喜欢

转载自www.cnblogs.com/wang-liang-blogs/p/12576023.html