canvas实现图片缩放+涂鸦

这两天有人问了上面这个标题的问题,老实说,我一个后端程序员,感觉还是有一点难度的,主要是好多东西都忘记了。正好星期天了,抽时间弄一个简单的实现出来,正好也能写一篇博客了。

效果演示

在这里插入图片描述

分析

创建一个缓存的canvas对象,保存当前canvas中的内容,在屏幕canvas上绘制图像时,同时也在缓存canvas中进行等比例的绘制。然后,在缩放操作时,首先要清空当前 canvas 的内容,然后把缓存 canvas 的内容,绘制到当前canvas中,即可实现该效果了。这里的难点在于等比例绘制时的对应关系。我在当前canvas上设置的是红色画笔,但是缓存 canvas 中,没有设置(默认是黑色),所以你可以看到我缩放的时候,所显示的画笔颜色里面变黑了。实际上不是变成黑色了,而是当前的 canvas 被缓存的 canvas 内容覆盖了,因为是等比例绘制,所以看起来的感觉是画笔变黑了(哈哈,已绘制的线条是无法变黑的,这是canvas的机制决定的,立即模式)。

代码

通过 http 协议在浏览器访问这个页面即可(同目录下放一张图片,注意名字和代码里面的一致)。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			* {
				margin: 0;
				padding: 0;
			}
			#cas {
				clear: left;
				width: 800px;
				border: #e90d0d 1px solid;
			}
		</style>
	</head>
	<body>
		<div id="cas">
			<canvas id="cs" width="800" height="800"></canvas>
		</div>

		<script type="text/javascript">				
			var canvas = document.getElementById("cs");       //获取画布
			var ctx = canvas.getContext("2d");
			var cacheCanvas = document.createElement("canvas"); // 缓存画布
			var cacheCtx = cacheCanvas.getContext("2d");
			cacheCanvas.width = canvas.width;
			cacheCanvas.height = canvas.height;
			ctx.lineWidth = 10;
			ctx.strokeStyle = "#e90d0d"

            var img = new Image();
            img.src = "./husky.png";
            img.onload = () => {
                ctx.drawImage(img, 0, 0);
				cacheCtx.drawImage(canvas, 0, 0);  // 加载完即将当前画布内容写入缓存画布
            }
			
			/* 用户绘制的动作,可以分解为如下操作:
				1.按下鼠标
				2.移动鼠标
				3.松开鼠标
				
			   它们分别对应于鼠标的onmousedown、onmousemove和onmouseup事件。
			   并且上述操作必然是有想后顺序的,因为人的操作必然是几个操作
			   集合中的一种。所以我们需要来限定以下,过滤用户的无效操作,
			   只对按照上诉顺序的操作进行响应。
			*/
			let isDowned = false;  // 是否按下鼠标,默认是false,如果为false,则不响应任何事件。

			// 开始添加鼠标事件
			canvas.onmousedown = (e) => {
				let x = e.clientX - canvas.offsetLeft;
				let y = e.clientY - canvas.offsetTop;
				isDowned = true;   // 设置isDowned为true,可以响应鼠标移动事件
				console.log("当前鼠标点击的坐标为:(", x + ", " + y + ")");
				ctx.beginPath();    // 开始一个新的路径
				ctx.moveTo(x, y);   // 移动画笔到鼠标的点击位置

				// 在缓存canvas中按照比例进行等比例缩小绘制
				cacheCtx.lineWidth = ctx.lineWidth/ZOOM[zoom_pos];
				cacheCtx.beginPath();
				let pos = getScalePos(ZOOM[zoom_pos], x, y);
				cacheCtx.moveTo(pos.x, pos.y);
			}
			
			canvas.onmousemove = (e) => {
				if (!isDowned) {
					return ;
				}
				let x = e.clientX - canvas.offsetLeft;
				let y = e.clientY - canvas.offsetTop;
				ctx.lineTo(x, y);    // 移动画笔绘制线条
				ctx.stroke();


				// 在缓存canvas中按照比例进行等比例缩小绘制
				let pos = getScalePos(ZOOM[zoom_pos], x, y);
				cacheCtx.lineTo(pos.x, pos.y);
				cacheCtx.stroke();
			}

			function getScalePos(size, x , y) {
				// 计算放大后,等比例对应的位置,这里需要多理解。
				return {
					x: (canvas.width*(size-1)/2+x)/size,
					y: (canvas.height*(size-1)/2+y)/size
				}
			}
			
			canvas.onmouseup = (e) => {
				isDowned = false;
			}
			
			/*
				在按下鼠标移动的过程中,如果移出了画布,则无法触发鼠标松开事件,即onmouseup。
				所以需要在鼠标移出画布时,设置isDowned为false。
			*/
			canvas.onmouseout = (e) => {
				isDowned = false;
			}


			// 定义一个缩放数组,初始在 1.0 位置
			const ZOOM = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0];
			let zoom_pos = 0;

            canvas.onmousewheel  = (e) =>  {  
				let x = e.clientX - canvas.offsetLeft;
				let y = e.clientY - canvas.offsetTop;              
                delta = e.wheelDelta;
                if (delta > 0) {
                    if (zoom_pos + 1 < ZOOM.length) {
						zoom_pos += 1;
					} else {
						zoom_pos = ZOOM.length - 1;
					}
                } else {
                    if (zoom_pos - 1 >= 0) {
						zoom_pos -= 1;
					} else {
						zoom_pos = 0;
					}
                }

				let xx = 0, yy = 0, dd = 0, hh = 0;
				let size = ZOOM[zoom_pos];
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.save();
				// 简单的等比例扩大,以canvas为中心。
				ctx.drawImage(cacheCanvas, (1-size)*canvas.width/2, (1-size)*canvas.height/2, canvas.width*size, canvas.height*size);
                ctx.restore();
				console.log(size);
            }
			
		</script>
	</body>
	
</html>

不足

我自己都有大半年没有接触 Canvas 了,里面的 api 我都快忘记了,不过这个问题蛮有趣的,我想了好久,也看了一些资料。不过,基本上只有缩放,或者只有绘制的,两种结合的没有。可能也是搜索的方式不太对吧。这个博客写得很急,代码也很乱,不过看思路就好了。代码,说不定还有 bug 没有发现呢,哈哈。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
实现图片缩放、移动和旋转,可以利用HTML5中的canvas元素和JavaScript来实现。具体的步骤如下: 1. 在HTML中创建canvas元素,并设置它的宽度和高度,以及一个放置图片的容器元素。 ```html <canvas id="myCanvas" width="500" height="500"></canvas> <div id="image-container"></div> ``` 2. 在JavaScript中,获取canvas元素和容器元素,并创建一个Image对象来加载图片。 ```javascript var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); var img = new Image(); img.onload = function() { // 图片加载完成后执行以下代码 }; img.src = "path/to/image.jpg"; ``` 3. 在Image对象加载完成后,将它绘制到canvas上,并将canvas中的图像转换为一个可缩放、可旋转、可拖动的对象。 ```javascript var container = document.getElementById("image-container"); var imageObject = { x: canvas.width / 2, // 图片中心点的x坐标 y: canvas.height / 2, // 图片中心点的y坐标 width: img.width, // 图片的宽度 height: img.height, // 图片的高度 angle: 0, // 图片的旋转角度,单位为弧度 scale: 1 // 图片缩放比例 }; // 将图片绘制到canvas上 ctx.drawImage(img, 0, 0); // 将canvas中的图像转换为一个可缩放、可旋转、可拖动的对象 var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); container.style.width = canvas.width + "px"; container.style.height = canvas.height + "px"; container.style.backgroundImage = "url(" + canvas.toDataURL() + ")"; container.style.backgroundSize = "100% 100%"; ``` 4. 实现图片缩放、移动和旋转功能。对于缩放和旋转,可以利用CSS3的transform属性来实现;对于移动,可以通过修改图片对象的x和y属性来实现。 ```javascript // 缩放图片 function zoomImage(scale) { imageObject.scale *= scale; container.style.transform = "rotate(" + imageObject.angle + "rad) scale(" + imageObject.scale + ")"; } // 旋转图片 function rotateImage(angle) { imageObject.angle += angle; container.style.transform = "rotate(" + imageObject.angle + "rad) scale(" + imageObject.scale + ")"; } // 移动图片 function moveImage(dx, dy) { imageObject.x += dx; imageObject.y += dy; container.style.left = imageObject.x - imageObject.width / 2 + "px"; container.style.top = imageObject.y - imageObject.height / 2 + "px"; } ``` 以上代码仅为示例,具体实现方式可能会因具体需求而有所不同。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值