react中使用截图组件Cropper组件

react

--最近项目用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 }}>

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

页面

以上是 react中使用截图组件Cropper组件 的全部内容, 来源链接: utcz.com/z/382891.html

回到顶部