Fabric.js 实现恢复 撤销 文字 手写 旋转 保存

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>fabric</title>
		<script src="https://picture.52hrttpic.com/cdn/js/fabric.min.js"></script>
		<script src="./index.js"></script>
		<style type="text/css">
			* {
				margin: 0;
				padding: 0;
			}

			ul {
				display: flex;
				list-style: none;
			}

			ul li {
				width: 50px;
				height: 30px;
				line-height: 30px;
				border: 1px solid #000;
				margin-right: 20px;
				text-align: center;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas" width="500" height="500"></canvas>

		<ul class="bottom">
			<li class="back">
				<p>撤销</p>
			</li>
			<li class="restore">
				<p>恢复</p>
			</li>
			<li class="write">
				<p>手写</p>
			</li>
			<li class="characters">
				<p>文字</p>
			</li>
			<li class="rights">
				<p>打钩</p>
			</li>
			<li class="error">
				<p>打叉</p>
			</li>
			<li class="rotate">
				<p>旋转</p>
			</li>
			<li class="save">
				<p>保存</p>
			</li>
			<li class="test">
				<p>测试</p>
			</li>
			<!-- <li @click="zoomIn">
		        <p>放大</p>
		      </li>
		      <li @click="zoomOut"><p>缩小</p></li> -->
		</ul>
		<script>
			 new myFabric({	
				id: 'canvas',
				url: 'http://1251481829.vod2.myqcloud.com/48804df0vodtransgzp1251481829/8eeca8613270835010470728685/sampleSnapshot/sampleSnapshot_10_1.jpg'
			})
            // 恢复
			let restore = document.querySelector('.restore')
			// 撤销
			let back = document.querySelector('.back')
			// 文字
			let characters = document.querySelector('.characters')
			// 手写
			let write = document.querySelector('.write')
			// 打勾
			let right = document.querySelector('.rights')
			// 打叉
			let error = document.querySelector('.error')
			// 旋转
			let rotate = document.querySelector('.rotate')
			// 保存
			let save = document.querySelector('.save')
			// 测试
			let test = document.querySelector('.test')
			// 自由绘画
			write.onclick = function(){
				myCanvas.DrawingMode()
			}
			// 文字
			characters.onclick = function(){
				myCanvas.addText()
			}
			// 打勾
			right.onclick = function(){
				myCanvas.addImg('right')
			}
			// 打叉
			error.onclick = function(){
				myCanvas.addImg('error')
			}
			// 旋转
			rotate.onclick = function(){
				myCanvas.rotate(90)
			}
			// 保存
			save.onclick = function(){
				myCanvas.save(90)
			}
			// 恢复
			restore.onclick = function(){
				myCanvas.restore()
			}
			// 撤销
			back.onclick = function(){
				myCanvas.back()
			}
			// 测试
			test.onclick = function(){
				myCanvas.test()
			}
		</script>
	</body>
</html>
class myFabric {
	delList = []; // 被删除的数据
	canvasId = ''; // classId
	url = ''; // 图片的地址
	isDrawingMode = false; // 是否开启自由绘画
	canvas = null; // 主体
	backgroundImage = null; // 背景图的引用
	angle = 0; // 图片旋转角度
	constructor(config) {
		this.canvasId = config.id
		this.url = config.url
		this.canvas = new fabric.Canvas(config.id, {
			//backgroundVpt: false // 不受视口变换影响(也就是不管拖拽还是缩放画布,背景图都不受影响)
		})
		this.getBase64FromImageUrl(config.url).then(res => {
			fabric.Image.fromURL(res, // 参数1:图片路径
				img => { // 参数2:图片加载完成后的回调函数
					const imgWidth = img.width;
					const imgHeight = img.height;
					const canvasWidth = this.canvas.width;
					const canvasHeight = this.canvas.height;

					const imgAspectRatio = imgHeight / imgWidth;
					const canvasAspectRatio = canvasHeight / canvasWidth;
					let imgScaleX = 1;
					let imgScaleY = 1;

					if (imgAspectRatio > canvasAspectRatio) { // 图片以高度为约束
						imgScaleY = Number((canvasHeight / imgHeight).toFixed(2));
						imgScaleX = imgScaleY
					} else { // 图片以宽度为约束
						imgScaleX = Number((canvasWidth / imgWidth).toFixed(2));
						imgScaleY = imgScaleX
					}
					img.set({
						left: this.canvas.width / 2, // 图片左边的位置
						top: this.canvas.height / 2, // 图片顶部的位置
						scaleX: imgScaleX, // 计算出图片要拉伸的宽度
						scaleY: imgScaleY, // 计算出图片要拉伸的高度
						originX: 'center', // 设置旋转中心为图片的中心点
						originY: 'center',
						centeredRotation: true, // 设置物体围绕旋转中心进行旋转
					})
					this.canvas.setBackgroundImage(img)
					// 保存背景图片对象的引用
					this.backgroundImage = img;
					// 重新渲染页面
					this.canvas.renderAll()

				}
			)
		})



	}
	// 是否开启自由绘画
	DrawingMode() {
		this.isDrawingMode = !this.isDrawingMode
		this.canvas.isDrawingMode = this.isDrawingMode
	}
	// 添加文字
	addText() {
		// 创建Text元素
		const itext = new fabric.IText('双击可编辑', {
			top: 60, // 文本到画布顶部的距离
			left: 100, // 文本到画布左侧的距离
			splitByGrapheme: true, // 自动换行
		})
		this.canvas.add(itext)
	}
	// 添加钩叉图片
	addImg(imgName) {
		let rightImgUrl = './img/right-icon.png'
		let errorImgUrl = './img/wrong-icon.png'
		let url = ''
		if (imgName == 'right') {
			url = rightImgUrl
		} else if (imgName == 'error') {
			url = errorImgUrl
		}
		fabric.Image.fromURL(
			url, // 图片路径
			img => {
				img.top = 100,
					img.left = 300,
					this.canvas.add(img) // 将图片插入到画布中
			}
		)
	}
	// 旋转
	rotate(angle) {
		this.angle += 90
		if (this.angle == 360) {
			this.angle = 0
		}
		this.backgroundImage.set({
			angle: this.angle // 累积旋转角度
		});
		// 重新渲染页面
		this.canvas.renderAll();
		console.log(this.backgroundImage.angle);
	}
	// 保存
	save() {
		const backgroundImage = this.backgroundImage;
		const bgLeft = backgroundImage.left;
		const bgTop = backgroundImage.top;
		const bgWidth = backgroundImage.width * backgroundImage.scaleX;
		const bgHeight = backgroundImage.height * backgroundImage.scaleY;

		const cropLeft = bgLeft - bgWidth / 2; // 裁剪区域左边的位置
		const cropTop = bgTop - bgHeight / 2; // 裁剪区域顶部的位置
		const cropWidth = bgWidth; // 裁剪区域的宽度
		const cropHeight = bgHeight; // 裁剪区域的高度

		console.log(cropLeft, cropTop, cropWidth, cropHeight);
		console.log(backgroundImage);
		let dataURL;
		let bool = backgroundImage.angle === 180 || backgroundImage.angle === 0;

		console.log(bool);

		if (bool) {
			dataURL = this.canvas.toDataURL({
				left: cropLeft,
				top: cropTop,
				width: cropWidth,
				height: cropHeight
			});
		} else {
			dataURL = this.canvas.toDataURL({
				left: cropTop,
				top: cropLeft,
				width: cropHeight,
				height: cropWidth
			});
		}
         console.log(dataURL);
	}
	// 撤销
	back() {
		if (this.canvas._objects.length > 0) {
			this.delList.push(this.canvas._objects.pop());
			this.canvas.renderAll();
		}
	}
	// 恢复
	restore() {
		if (this.delList.length > 0) {
			this.isRedoing = true;
			this.canvas.add(this.delList.pop());
			this.canvas.renderAll();
		}
	}

	test() {
		console.log(this.canvas);
	}
	// 创建一个用于读取图片的 Promise 函数
	getBase64FromImageUrl(url) {
		return new Promise((resolve, reject) => {
			const img = new Image();
			img.crossOrigin = 'anonymous';
			img.onload = () => {
				const canvas = document.createElement('canvas');
				canvas.width = img.width;
				canvas.height = img.height;
				const ctx = canvas.getContext('2d');
				ctx.drawImage(img, 0, 0);
				let dataURL
				if (url.indexOf('jpg') > -1 || url.indexOf('jpeg') > -1) {
					dataURL = canvas.toDataURL('image/jpeg');
				} else if (url.indexOf('png') > -1) {
					dataURL = canvas.toDataURL('image/png');
				}
				resolve(dataURL);
			};
			img.onerror = reject;
			img.src = url;
		});
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值