效果圖:
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;