效果图:

import React, { Component } from 'react';
import scaleImage from './images/scale.png';
import closeImage from './images/close.png';
import maskImage from './images/mask.png';
import { Button, message } from 'antd'; class EidtImage extends Component {
constructor(props) {
super(props);
this.state = {
bounder: 7,
image1: {
img: undefined, // 保存图片对象
src: maskImage, // 图片路径
x: 50, // 图片左上角x坐标
y: 100, // 图片左上角y坐标
width: 100, // 用来绘制的宽度(注意不是图片自身的宽度,图片会被压缩显示)
height: 20, // 用来绘制图片的高度
drag: false, // 是否处于拖拽状态
scale: false, // 是否处于缩放状态
scaleDirection: '', // 缩放方向
scaleIcon: scaleImage,
closeIcon: closeImage,
selected: true, //拖拽模块是否处于选中转态,true为是
closeMoudle: false, //true:关闭遮层,false展示遮层
imageUrl: '' //画布背景图
},
imgUrl: '',
cansText: {}, //画布对象
canva: {},
}
} componentDidMount = () => {
this.canvasInit();
}
// 画布初始化
canvasInit = () => {
let canvasId = this.refs.canvas.id;
let canva = document.getElementById(canvasId);
const cansText = canva.getContext("2d");
const { imageUrl } = this.props;
this.setState({
cansText, canva, imageUrl
}, () => {
// 加载图片
this.loadimage();
}) } //加载
loadimage = () => {
const obj = this.state.image1;
const { cansText, canva, imageUrl } = this.state;
let bgImage = new Image();
bgImage.crossOrigin = "anonymous";//解决图片跨域
bgImage.src = imageUrl;
bgImage.onload = function () {
let bgImageW = bgImage.width;
let bgImageH = bgImage.height;
canva.width = 180;
canva.height = 180 * bgImageH / bgImageW;
cansText.drawImage(bgImage, 0, 0, 180, 180 * bgImageH / bgImageW);
if (obj.closeMoudle) return;
let image = new Image();
image.crossOrigin = "anonymous";//解决图片跨域
image.src = obj.src;
image.onload = function () {
cansText.drawImage(image, obj.x, obj.y, obj.width, obj.height);
obj.image = image;
if (obj.selected) {
// 虚线
cansText.setLineDash([5, 5]);//定义虚线的长度和间隔
cansText.strokeStyle = "#fff";
cansText.strokeRect(obj.x, obj.y, obj.width, obj.height);
//渲染伸缩图标
let scaleIcon = new Image();
scaleIcon.crossOrigin = "anonymous";
scaleIcon.src = obj.scaleIcon;
scaleIcon.onload = function () {
cansText.drawImage(scaleIcon, obj.x - 8, obj.y + obj.height - 12, 20, 20);
}
// 关闭遮层图标
let closeIcon = new Image();
closeIcon.crossOrigin = "anonymous";
closeIcon.src = obj.closeIcon;
closeIcon.onload = function () {
cansText.drawImage(closeIcon, obj.x + obj.width - 10, obj.y - 10, 20, 20)
}
} }
}
} // 监听鼠标按下事件
onmousedown = (e) => {
if (e) e.persist();
let that = this;
let { bounder, image1 } = that.state;
let mousex = e ? e.nativeEvent.offsetX : 1000;
let mousey = e ? e.nativeEvent.offsetY : 1000;
let bottom = image1.y + image1.height;
let top = image1.y;
let left = image1.x;
let right = image1.x + image1.width; //判断,是否关闭遮层
if (right - 10 < mousex && mousex < right + 10 && top - 10 < mousey && mousey < top + 10) {
image1.closeMoudle = true;
} // 判断,当前拖拽模块是否选中状态
if (right + 10 < mousex || mousex < left - 10 || bottom + 10 < mousey || mousey < top - 10) {
image1.selected = false;
} else {
image1.selected = true;
} // 判断是缩放还是拖拽,若点击位置和边线的差大于bounder则认为是拖拽,否则是缩放
if ((left + bounder <= mousex && mousex <= right - bounder) && (top + bounder <= mousey && mousey <= bottom - bounder)) {
image1.drag = true;
image1.scale = false;
image1.scaleDirection = '';
} else if (0 <= mousex - left && mousex - left <= bounder) {
image1.scaleDirection = 'left';
image1.scale = true;
image1.drag = false;
} else if (0 <= right - mousex && right - mousex <= bounder) {
image1.scaleDirection = 'right';
image1.scale = false;
image1.drag = true;
} if (0 <= mousey - top && mousey - top <= bounder) {
image1.scaleDirection += 'top';
image1.scale = false;
image1.drag = true;
} else if (0 <= bottom - mousey && bottom - mousey <= bounder) {
image1.scaleDirection += 'bottom';
image1.scale = true;
image1.drag = false;
}
this.loadimage();
}
// 鼠标弹起,重置所有事件参数
onmouseup = (e) => {
e.persist();
const { image1 } = this.state;
// body...
image1.drag = false;
image1.scale = false;
image1.scaleDirection = '';
this.setState({ image1 });
}
// 鼠标移动事件
onmousemove = (e) => {
e.persist();
const { image1, cansText, canva, imageUrl } = this.state;
// body...
let mousex = e.nativeEvent.offsetX;
let mousey = e.nativeEvent.offsetY;
if (image1.drag) {
// 画背景图
let bgImage = new Image();
bgImage.crossOrigin = "anonymous" //解决图片跨域
bgImage.src = imageUrl;
bgImage.onload = function () {
let bgImageW = bgImage.width;
let bgImageH = bgImage.height;
canva.width = 180;
canva.height = 180 * bgImageH / bgImageW; // 鼠标移出canvas区域
if (mousex < 0 || mousex >= 180 || mousey >= canva.height - 5 || mousey <= 0) {
image1.drag = false;
image1.scale = false;
};
cansText.drawImage(bgImage, 0, 0, 180, 180 * bgImageH / bgImageW); if (image1.closeMoudle) return; // 移动图片
if (e.movementX || e.movementY) {
let tem_imgx = image1.x + e.movementX;
let tem_imgy = image1.y + e.movementY;
image1.x = tem_imgx;
image1.y = tem_imgy;
if (image1.x + image1.width >= 180) {
image1.x = 180 - image1.width;
}
if (image1.y + image1.height >= 180 * bgImageH / bgImageW) {
image1.y = 180 * bgImageH / bgImageW - image1.height;
} if (image1.y <= 0) {
image1.y = 0;
}
if (image1.x <= 0) {
image1.x = 0;
}
if (image1.selected) {
//渲染伸缩图标
let scaleIcon = new Image();
scaleIcon.crossOrigin = "anonymous";
scaleIcon.src = image1.scaleIcon;
scaleIcon.onload = function () {
cansText.drawImage(scaleIcon, image1.x - 8, image1.y + image1.height - 12, 20, 20);
}
// 关闭遮层图标
let closeIcon = new Image();
closeIcon.crossOrigin = "anonymous";
closeIcon.src = image1.closeIcon;
closeIcon.onload = function () {
cansText.drawImage(closeIcon, image1.x + image1.width - 10, image1.y - 10, 20, 20)
}
// 虚线
cansText.setLineDash([5, 5]);//定义虚线的长度和间隔
cansText.strokeStyle = "#fff";
cansText.strokeRect(image1.x, image1.y, image1.width, image1.height);
}
// 清空画布
cansText.clearRect(image1.x, image1.y, image1.width, image1.height); // 被拖拽的图片
cansText.drawImage(image1.image, image1.x, image1.y, image1.width, image1.height);
};
} } //缩放
if (image1.scale) {
// 画背景图
let bgImage = new Image();
bgImage.crossOrigin = "anonymous"//解决图片跨域
bgImage.src = imageUrl;
bgImage.onload = function () {
let bgImageW = bgImage.width;
let bgImageH = bgImage.height;
canva.width = 180;
canva.height = 180 * bgImageH / bgImageW;
cansText.drawImage(bgImage, 0, 0, 180, 180 * bgImageH / bgImageW);
// 缩放图片
if (e.movementX || e.movementY) {
let movex = e.movementX;
let movey = e.movementY;
if (movex !== 0 || movey !== 0) {
//根据x缩放方向判断固定点
if (image1.scaleDirection.search('right') !== -1) {
image1.width += movex;
} else if (image1.scaleDirection.search('left') !== -1) {
image1.x += movex;
image1.width -= movex;
}
if (image1.scaleDirection.search('bottom') !== -1) {
image1.height += movey;
} else if (image1.scaleDirection.search('top') !== -1) {
image1.height -= movey;
image1.y += movey;
}
// 清除画布
cansText.clearRect(image1.x, image1.y, image1.width, image1.height);
// 伸缩图标
//渲染伸缩图标
let scaleIcon = new Image();
scaleIcon.crossOrigin = "anonymous";
scaleIcon.src = image1.scaleIcon;
scaleIcon.onload = function () {
cansText.drawImage(scaleIcon, image1.x - 8, image1.y + image1.height - 12, 20, 20);
} // 关闭遮层图标
let closeIcon = new Image();
closeIcon.crossOrigin = "anonymous";
closeIcon.src = image1.closeIcon;
closeIcon.onload = function () {
cansText.drawImage(closeIcon, image1.x + image1.width - 10, image1.y - 10, 20, 20)
}
// 虚线
cansText.setLineDash([5, 5]);//定义虚线的长度和间隔
cansText.strokeStyle = "#fff";
cansText.strokeRect(image1.x, image1.y, image1.width, image1.height);
// 被拖拽的图片
cansText.drawImage(image1.image, image1.x, image1.y, image1.width, image1.height);
};
};
}
}
} // 保存图片
saveImage = () => {
let that = this;
let { canva, imgUrl } = that.state;
// 在导出画布之前,把一些图标、虚线去掉;
this.onmousedown();
setTimeout(function () {
imgUrl = canva.toDataURL('image/jpeg'); //转换图片为dataURL
that.setState({
imgUrl
}, () => {
let obj={};
if(that.props.id==='imageUrlFront'){
obj={imageUrlFront:that.state.imgUrl}
}else if(that.props.id==='imageUrlLeft'){
obj={imageUrlLeft:that.state.imgUrl}
}else if(that.props.id==='imageUrlRight'){
obj={imageUrlRight:that.state.imgUrl}
}
that.props.parent.getEidtImageUrl(that, obj)
message.success('保存成功')
})
}, 100);
}
// 重新编辑
reMake = () => {
let {image1}=this.state;
let newImage=Object.assign({},image1,{closeMoudle:false,selected:true})
this.setState({
image1:newImage
},()=>{
this.canvasInit();
})
} render() {
return (
<React.Fragment>
<div className="canvas-container">
<canvas onMouseUp={this.onmouseup} onMouseDown={this.onmousedown} onMouseMove={this.onmousemove} id={this.props.id} ref="canvas" style={{ backgroundColor: '#fff' }}>您的浏览器不支持画布标签</canvas>
<Button type="primary" size="small" onClick={this.saveImage}>保存图片</Button>
<Button type="default" size="small" style={{ marginLeft: '35px' }} onClick={this.reMake}>重新编辑</Button>
</div>
</React.Fragment>
);
} } export default EidtImage;

react之每日一更(实现canvas拖拽,增、删、改拖拽模块大小功能)的更多相关文章

  1. HTML5之Canvas时钟(网页效果--每日一更)

    今天,带来的是使用HTML5中Canvas标签实现的动态时钟效果. 话不多说,先看效果:亲,请点击这里 众所周知,Canvas标签是HTML5中的灵魂,HTML5 Canvas是屏幕上的一个由Java ...

  2. CSS3之图片3D翻转效果(网页效果--每日一更)

    今天,带来的是纯CSS3的效果--图片3D翻转. 请看效果:亲,请点击这里 这个效果主要还是运用了CSS3的transform变形属性,与上个效果不同的是,这次并不是动画,所以没有采用animatio ...

  3. CSS3之绽放的花朵(网页效果--每日一更)

    今天,带来的是纯CSS3打造的效果--绽放的花朵. 先来看效果吧:亲,请点击这里 这是纯CSS3样式打造的效果,关键是采用了animation属性和transform属性.详细请看下面代码. HTML ...

  4. JQuery图片轮播滚动效果(网页效果--每日一更)

    今天,带来的是一个图片的轮播滚动效果! 先来看一下效果展示:亲,请点击这里 原理很简单,设置一个定时器,使图片列表在每隔一段时间后滚动一次.而循环效果,就是在每一滚动的时候,将第一张图片放到最后一张的 ...

  5. react拖拽(表格拖拽排序、普通拖拽排序以及树形拖拽排序)

    表格拖拽排序:组件地址:https://reactabular.js.org/#/drag-and-drop 拖动的排序是用React-DnD:React-DnD:http://react-dnd.g ...

  6. js 斐波那契数列的获取和曲线的实现(每日一更)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  7. js 每日一更(数组转换成前端更容易解析的树状结构)

    <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...

  8. react.js语法为了更好的兼容可以选以下这种方法配置即可

    首先下一个sublime text3 的编辑器: 支持大家使用正版,不过我是为了方便才所以这版本而已... 地址: http://sublimetext.iaixue.com/forum.php?mo ...

  9. Linux shell 常用命令大全 每日一更

    大一上学期学习了Linux的基本操作,已经很久没使用了,虚拟机也近半年没开(作为一个计算机类专业的少年真的不应该).为了补回这些知识和为将来的学习打下基础,现在每天更新一条shell命令及其子命令,欢 ...

随机推荐

  1. 「Codeforces 724F」Uniformly Branched Trees

    题目大意 如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的. 将中间节点定义为度数大于 \(1\) 的节点.计算由 \(n\) 个节点,其中所有的中间节点度数都为 \(d\) 的互不同构的树 ...

  2. CS学习资料百度云链接

    CS学习资料百度云链接 [0]Springboot微服务开发天气预报系统视频教程https://pan.baidu.com/s/1joz7flyztCq8oklBlsz8dQ提取密码:cpz7 [1] ...

  3. <数据结构>关键路径

    目录 AOV网和AOE网 AOV网 AOE网 定义 与AOV网的转化 AOE网中着重解决的两个问题 1.最长路径问题 2.关键活动问题 总结 最长路径 无正环的图 有向无环图的最短路径 其他情况 关键 ...

  4. Java_Swing中让窗口居中显示的方法(三种方法)

    方法一: int windowWidth = frame.getWidth(); // 获得窗口宽    int windowHeight = frame.getHeight(); // 获得窗口高 ...

  5. 基于MCRA-OMLSA的语音降噪(三):实现(续)

    上篇文章(基于MCRA-OMLSA的语音降噪(二):实现)讲了基于MCRA-OMLSA的语音降噪的软件实现.本篇继续讲,主要讲C语言下怎么对数学库里的求平方根(sqrt()).求自然指数(exp()) ...

  6. 【java多线程】synchronized和volatile

    文章目录 一.synchronized 1.synchronized使用的方法 2.注意 3.不要以字符串作为锁的对象 4.`synchronized`锁的是什么? 二.volatile 1.引出问题 ...

  7. k8s env、configmap、secret外部数据加载配置

    K8s提供了多种外部数据注入容器的方式,今天我们主要学习环境变量.ConfigMap以及Secret的使用和配置. 环境变量 在docker项目中,对一个容器添加环境变量可以在容器创建时通过-e EN ...

  8. win10 安装vue 详解-包括node.js、npm、webpack

    1.下载 去官网下载 node.js https://nodejs.org/en/download/ 一般不会选择最新的,我安装的是 12.18.4 进入历史记录页面网址 https://nodejs ...

  9. c# - 数据类型转换和控制台输入

    1.使用c#自带的 Convert类转换数据类型 2.源码 using System; namespace ConsoleApp1.toValue { class excutejiecheng { s ...

  10. 第10组 Beta冲刺 (5/5)(组长)

    1.1基本情况 ·队名:今晚不睡觉 ·组长博客:https://www.cnblogs.com/cpandbb/p/14018671.html ·作业博客:https://edu.cnblogs.co ...