react之每日一更(实现canvas拖拽,增、删、改拖拽模块大小功能)
效果图:
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拖拽,增、删、改拖拽模块大小功能)的更多相关文章
- HTML5之Canvas时钟(网页效果--每日一更)
今天,带来的是使用HTML5中Canvas标签实现的动态时钟效果. 话不多说,先看效果:亲,请点击这里 众所周知,Canvas标签是HTML5中的灵魂,HTML5 Canvas是屏幕上的一个由Java ...
- CSS3之图片3D翻转效果(网页效果--每日一更)
今天,带来的是纯CSS3的效果--图片3D翻转. 请看效果:亲,请点击这里 这个效果主要还是运用了CSS3的transform变形属性,与上个效果不同的是,这次并不是动画,所以没有采用animatio ...
- CSS3之绽放的花朵(网页效果--每日一更)
今天,带来的是纯CSS3打造的效果--绽放的花朵. 先来看效果吧:亲,请点击这里 这是纯CSS3样式打造的效果,关键是采用了animation属性和transform属性.详细请看下面代码. HTML ...
- JQuery图片轮播滚动效果(网页效果--每日一更)
今天,带来的是一个图片的轮播滚动效果! 先来看一下效果展示:亲,请点击这里 原理很简单,设置一个定时器,使图片列表在每隔一段时间后滚动一次.而循环效果,就是在每一滚动的时候,将第一张图片放到最后一张的 ...
- react拖拽(表格拖拽排序、普通拖拽排序以及树形拖拽排序)
表格拖拽排序:组件地址:https://reactabular.js.org/#/drag-and-drop 拖动的排序是用React-DnD:React-DnD:http://react-dnd.g ...
- js 斐波那契数列的获取和曲线的实现(每日一更)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...
- js 每日一更(数组转换成前端更容易解析的树状结构)
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...
- react.js语法为了更好的兼容可以选以下这种方法配置即可
首先下一个sublime text3 的编辑器: 支持大家使用正版,不过我是为了方便才所以这版本而已... 地址: http://sublimetext.iaixue.com/forum.php?mo ...
- Linux shell 常用命令大全 每日一更
大一上学期学习了Linux的基本操作,已经很久没使用了,虚拟机也近半年没开(作为一个计算机类专业的少年真的不应该).为了补回这些知识和为将来的学习打下基础,现在每天更新一条shell命令及其子命令,欢 ...
随机推荐
- 慢 SQL 优化
# 导致SQL执行慢的原因 1. 硬件问题.如网络速度慢,内存不足,I/O吞吐量小,磁盘空间满了等. 2. 没有索引或者索引失效.(一般在互联网公司,DBA会在半夜把表锁了,重新建立一遍索引,因为当你 ...
- 「算法笔记」旋转 Treap
一.引入 随机数据中,BST 一次操作的期望复杂度为 \(\mathcal{O}(\log n)\). 然而,BST 很容易退化,例如在 BST 中一次插入一个有序序列,将会得到一条链,平均每次操作的 ...
- linux中网络存储与考试系统搭建(实现多用户可以共享文件)
上期内容回顾 1.数据备份的方式有哪些 全量和增量 2.数据备份的命令有哪些 都有哪些优点缺点 cp : 本地复制,全量复制 scp : 远程复制,全量复制 rsync : 远程复制,增量复制 3.r ...
- [Git]解决Permission denied, please try again问题
在gitlab上传项目的时候出现Permission denied, please try again问题, 网上有很多解释,但是都没能解决我的问题,后来经过自己尝试成功了,这里把经验分享给大家. 在 ...
- 美和易思 · 「云农职互联网技术学院」HTML+CSS 做西普尼金表官网
假期作业,好久没碰了,代码写得很烂......写博客纯属记录! 源代码下载地址:https://download.csdn.net/download/weixin_44893902/12805555 ...
- MATLAB/SIMULINK生成代码错误之change the default character encoding setting
SIMULINK点击生成C代码报错 错误提示: Error encountered while executing PostCodeGenCommand for model 'RTW_sc3': Cl ...
- MongoDB学习 - 安装部署
1. docker 启动 拉取镜像 docker pull mongo:latest 指定目录启动 docker run -d -p 27017:27017 --name mongo \-v /ho ...
- Go语言系列之单元测试
go test工具 Go语言中的测试依赖go test命令.编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法.规则或工具. go test命令是一个按照一定约定和组织的测试代码的驱动 ...
- HBase环境搭建(hbase1.2.5+zookeeper3.4.6)
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6627857018461880836/ 系统版本,Hadoop已安装完成 Mysql安装完成 Hive版本 Sqoo ...
- vue2.0点击其他任何地方隐藏dom
methods: { handleBodyClick(){ if (绿色区域出来了,要判断点击其他地方就要关闭,这样可以避免绿色区域已经关闭还在操作) { let _con = $(目标区域) if ...