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命令及其子命令,欢 ...
随机推荐
- SpringBoot集成Actuator健康指示器health
1.说明 本文详细介绍Actuator提供的HealthIndicators, 即健康指示器的配置使用, 利用自动配置的健康指标, 检查正在运行的应用程序的状态, 以及自定义健康指标的方法. 监控软件 ...
- CGO快速入门
1. 通过`improt "C"`语句开启CGO特性2. `/**/`中间是C代码,之后接 import "C" 如果存在空行 就会报错.could not d ...
- python的赋值、浅拷贝和深拷贝
""" 一.赋值在python中就是简单的对象引用 """ list_a = ["aaa", "bbb&quo ...
- Redis_客户端命令和数据操作(3)
更多命令请查找:http://c.biancheng.net/redis_command/ 切换数据库 redis数据库没有名称,默认有16个,通过0-15来标识,连接redis默认选择第一个数据库, ...
- git 生成忽略文件
一.在.git的同级目录下打开git bash 二.在命令行输入 三.在生成的文件中添加忽略提交的文件夹名称 来自为知笔记(Wiz)
- vue - 搭建 webapp 自适应项目-使用 vant 组件库 并 可自动调节大小
1.创建个vue 项目,这里不详细写怎么创建,参考 vue - 指令创建 vue工程 - 岑惜 - 博客园 (cnblogs.com) https://www.cnblogs.com/c2g52013 ...
- reduce/reduceRight
使用 reduce 和 reduceRight 方法可以汇总数组元素的值,具体用法如下: reduce function appendCurrent (previousValue, currentVa ...
- 实验 6 :OpenDaylight 实验——OpenDaylight 及 Postman实现流表下发
实验 6 :OpenDaylight 实验--OpenDaylight 及 Postman实现流表下发 一.实验目的 熟悉 Postman 的使用:熟悉如何使用 OpenDaylight 通过 Pos ...
- java中的spi
JAVA中的SPI机制 1.SPI简介 SPI机制(Service Provider Interface)其实源自服务提供者框架(Service Provider Framework),是一种将服务接 ...
- 微信小程序动画实现(API,css)
微信小程序动画API实现 index.js clicktap:function(){ var Animation=wx.createAnimation({ duration: 2000, }) Ani ...