canvas 图片编辑(拖拽旋转缩放)交流

youryida 2013-12-16 11:09:33
需要做一个在canvas里对图片进行拖拽、缩放、旋转的一个小应用。

现在可以拖拽,旋转有个问题,不好解决,demo文件在下面,大概80行代码的样子,希望路过的朋友指点下,谢谢!(我觉得问题出在转换坐标系过程了,但是不是很明白,求指点。)

http://youryida.duapp.com/other/drag_rotate/


...全文
3012 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
Super_Melody 2016-08-28
  • 打赏
  • 举报
回复
终于找到自己想要的代码了,感谢楼主的分享精神,赞一个。
婉程序 2016-03-30
  • 打赏
  • 举报
回复
楼主你那有没有在canvas里对图片进行拖拽、缩放、旋转的多点触控事件代码?
海阔天空--L 2014-03-26
  • 打赏
  • 举报
回复
在火狐上不行....
destiny1231500 2013-12-24
  • 打赏
  • 举报
回复
test-2.3 不错 支持一下楼主 谢谢分享
youryida 2013-12-23
  • 打赏
  • 举报
回复
引用 7 楼 KK3K2005 的回复:
不好意思最近忙死天天加班到深夜 星期天有时间我来看下矩阵做法
嗯,老兄你有空了再整就行,注意身体! 借此楼发布一下我的终极成果:图片的拖拽与旋转(坐标转换、状态保存)
KK3K2005 2013-12-21
  • 打赏
  • 举报
回复
不好意思最近忙死天天加班到深夜 星期天有时间我来看下矩阵做法
zhjdg 2013-12-21
  • 打赏
  • 举报
回复
youryida 2013-12-20
  • 打赏
  • 举报
回复
每次在csdn上问问题都是这样,好几天无人光顾,最后还是自己整出来了... 研究过程我写在了我的博客里了,欢迎交流。 canvas 图片拖拽+旋转 [坐标变换] demo

<!doctype html>
<html>
<head>
	<title> </title>
	<meta http-equiv="X-UA-Compatible" content="IE=9"> 
	<meta charset="utf-8" />
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<style>
		#canvas{border:1px solid #ccc;}
	</style>
   	
</head>
<body>
	<canvas id="canvas" width="500" height="300"></canvas>
	<pre>
功能:拖拽+旋转
思路:始终保持图片中心点在canvas坐标系的原点处,图片的每一次重绘都基于canvas坐标系的原点来绘制,即drawImage(img,-imgW/2,-imgH/2)。
	移动、旋转的时候绘制方法不变,变换的是canvas坐标系。
关键:理解屏幕坐标系和canvas坐标系的关系。将鼠标事件的屏幕坐标,转换为canvas坐标系中的坐标。
	计算旋转时每一次mousemove,在旋转前的canvas坐标系中move的角度。
	</pre>	
	<script>
	var cvs =document.getElementById("canvas");   
	var ctx =cvs.getContext("2d");
	var cvsH=cvs.height;
	var cvsW=cvs.width;
	var beginX,beginY;
	var LT={x:30,y:30};//图片左上角的点
	var Selected_Round_R=12;
	var isDown=false;
	var imgH,imgW;
	var moveAble=false,rotateAble=false;
	var img = new Image();	
	var rotate_radian=0;//canvas坐标系x轴与屏幕坐标系X轴夹角弧度
	img.src ="img/niuniu.jpg";
	img.onload=function (){
		imgH=img.height;
		imgW=img.width;
		PO={x:LT.x+imgW/2,y:LT.y+imgH/2};
		ctx.translate(PO.x,PO.y);//载入时将canvas坐标系原点移到图片中心点上
		onDraw();
		
	}
	function onDraw(){
		ctx.clearRect(-cvsW,-cvsH,2*cvsW,2*cvsH);
		ctx.drawImage(img,-imgW/2,-imgH/2);	
		//旋转控制旋钮
		ctx.beginPath();
		ctx.arc(0,-imgH/2-Selected_Round_R,Selected_Round_R,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=2;
        ctx.strokeStyle="#0000ff";
		ctx.stroke();
	}
	cvs.addEventListener("mousedown", startMove, false); 
	cvs.addEventListener("mousemove", moving, false);
	cvs.addEventListener("mouseup", endMove, false);	 	
	cvs.addEventListener("mouseout",endMove, false);

	function imgIsDown(x,y){
		return (-imgW/2<=x && x<=imgW/2 && -imgH/2<y && y<=imgH/2);
	}
	function RTIsDown(x,y){
		var round_center={x:0,y:-imgH/2-Selected_Round_R};
		var bool=getPointDistance({x:x,y:y},round_center)<=Selected_Round_R;
		return bool;
	}
	function startMove(){
		event.preventDefault();
		isDown=true;
		var loc=getEvtLoc();//获取鼠标事件在屏幕坐标系的位置(原点在canvas标签左上角)
		var x=loc.x,y=loc.y;
		beginX=x,beginY=y;
		var cLoc=convertCoor(loc);
		var Xc=cLoc.x,Yc=cLoc.y;
		moveAble=imgIsDown(Xc,Yc);
		rotateAble=RTIsDown(Xc,Yc);		
		if (moveAble) cvs.style.cursor="move";
		if (rotateAble) cvs.style.cursor="crosshair";
	}
	function moving(){
		event.preventDefault();
		if(isDown==false) return;
		var loc=getEvtLoc();
		if(moveAble){
			var x=loc.x,y=loc.y;
			var dx=x-beginX,dy=y-beginY;
			var mPO={x:PO.x+dx,y:PO.y+dy};//因为鼠标移动dx dy,所以PO在屏幕坐标系的坐标也 移动dx dy
			var cPO=convertCoor(mPO);//屏幕坐标系移动后的PO转换成canvas坐标系的坐标
			ctx.translate(cPO.x,cPO.y);//canvas坐标系原点移动到新的图片中心点		
			onDraw();
			
			PO.x=PO.x+dx;//记录下屏幕坐标系上PO的坐标变化
			PO.y=PO.y+dy;
			beginX=x,beginY=y;//记录移动后鼠标在屏幕坐标系的新位置	
		}else if(rotateAble){
			var cLoc=convertCoor(loc);
			var Xc=cLoc.x,Yc=cLoc.y;
			var newR = Math.atan2(Xc,-Yc);//在旋转前的canvas坐标系中 move的角度(因为旋钮在上方,所以跟,应该计算 在旋转前canvas坐标系中,鼠标位置和原点连线 与 y轴反方向的夹角)
			ctx.rotate(newR);
			rotate_radian+=newR;
			onDraw();
		}
	}
	function endMove(){
		event.preventDefault();
		isDown=false;
		moveAble=rotateAble=false;
		cvs.style.cursor="auto";
	}

	function getEvtLoc(){//获取相对canvas标签左上角的鼠标事件坐标
		return {x:event.offsetX,y:event.offsetY}
	}
	
	function convertCoor(P) {//坐标变换 屏幕坐标系的点 转换为canvas坐标系的点
		var x=P.x-PO.x;//在屏幕坐标系中,P点相对canvas坐标系原点PO的偏移
		var y=P.y-PO.y; 

		if(rotate_radian!=0){
			var len = Math.sqrt(x*x + y*y);
			var oldR=Math.atan2(y,x);//屏幕坐标系中 PO与P点连线 与屏幕坐标系X轴的夹角弧度			
			var newR =oldR-rotate_radian;//canvas坐标系中PO与P点连线 与canvas坐标系x轴的夹角弧度
			x = len*Math.cos(newR);
			y = len*Math.sin(newR);      		
		}
		
		return {x:x,y:y};
	}
	//获取两点距离
	function getPointDistance(a,b){
		var x1=a.x,y1=a.y,x2=b.x,y2=b.y;
		var dd= Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
		return dd;
	}
	</script>
</body>
</html>
youryida 2013-12-20
  • 打赏
  • 举报
回复
引用 3 楼 KK3K2005 的回复:
矩阵~~~ .... 你可以先弄弄看 晚上时间多 可以帮你一起看下
大神,我研究出来了,不过我还是很想看看使用矩阵是怎么做的? 这是我的链接 http://youryida.duapp.com/demo_canvas/coor_convert_move_rotate.html
youryida 2013-12-17
  • 打赏
  • 举报
回复
引用 3 楼 KK3K2005 的回复:
矩阵~~~ 最终显示 = 正常 * 矩阵 .... 你可以先弄弄看 晚上时间多 可以帮你一起看下
就喜欢爽快的大神,非常期待!今天搞了一天没搞出名堂来,燥死我了! 先行谢过了啊!!
KK3K2005 2013-12-17
  • 打赏
  • 举报
回复
矩阵~~~ 最终显示 = 正常 * 矩阵 旋转操作就是获取一个 旋转矩阵 那么你的鼠标坐标定位判断要 / 旋转矩阵 鼠标坐标/矩阵 == 图片原始区域内 则判断在图片里 好吧 上面是理论 不过根据一致性原则 应该是 ok的 你可以先弄弄看 晚上时间多 可以帮你一起看下
youryida 2013-12-17
  • 打赏
  • 举报
回复
好吧,小伙伴儿们,我自己有些进展了,我整理了下,目前需要解决的问题,有两个: 一、计算mousemove旋转时产生的角度; 二、判断旋转之后再拖拽时,mousedown的点是否在图片区域内。 我现在有一些解决思路,但是我数学不好,自己研究可能会花很长一段时间,希望路过的大神能出个demo让小弟参考下! QQ:1140215489 百度知道 100分悬赏 http://zhidao.baidu.com/question/2201564571024171668.html
60个Android开发精典案例 Android软件源码: 2-1(Activity生命周期) 3-1(Button与点击监听器) 3-10-1(列表之ArrayAdapter适配) 3-10-2(列表之SimpleAdapter适配) 3-11(Dialog对话框) 3-12-5(Activity跳转与操作) 3-12-6(横竖屏切换处理) 3-3(ImageButton图片按钮) 3-4(EditText文本编辑) 3-5(CheckBox与监听) 3-6(RadioButton与监听) 3-7(ProgressBar进度条) 3-8(SeekBar 拖动条) 3-9(Tab分页式菜单) 4-10(可视区域) 4-11-1(Animation动画) 4-11-2-1(动态位图) 4-11-2-2(帧动画) 4-11-2-3(剪切图动画) 4-13(操作游戏主角) 4-14-1(矩形碰撞) 4-14-2(圆形碰撞) 4-14-4(多矩形碰撞) 4-14-5(Region碰撞检测) 4-15-1(MediaPlayer音乐) 4-15-2(SoundPool音效) 4-16-1(游戏保存之SharedPreference) 4-16-2(游戏保存之Stream) 4-3(View游戏框架) 4-4(SurfaceView游戏框架) 4-7-1(贝塞尔曲线) 4-7-2(Canvas画布) 4-8(Paint画笔) 4-9(Bitmap位图渲染与操作) 5-1(飞行射击游戏实战) 6-1(360°平滑游戏摇杆) 6-10-1(Socket协议) 6-10-2(Http协议) 6-11(本地化与国际化) 6-2(多触点缩放位图) 6-3(触屏手势识别) 6-4(加速度传感器) 6-5(9patch工具)] 6-6(截屏) 6-8(游戏视图与系统组件) 6-9(蓝牙对战游戏) 7-10-1(遍历Body) 7-10-2(Body的m_userData) 7-11(为Body施加力) 7-12(Body碰撞监听) 7-13-1(距离关节) 7-13-2(旋转关节) 7-13-3(齿轮关节) 7-13-4(滑轮关节) 7-13-5-1(通过移动关节移动Body) 7-13-5-2(通过移动关节绑定两个Body动作) 7-13-6(鼠标关节-拖拽Body) 7-14(AABB获取Body) 7-4(Box2d物理世界) 7-5在物理世界中添加矩形) 7-7(添加自定义多边形) 7-9(在物理世界中添加圆形) 8-1(迷宫小球) 8-2(堆房子)
自己在网上整理的一些例子。很详细的。无积分下载。 2-1(Activity生命周期) 3-1(Button与点击监听器) 3-10-1(列表之ArrayAdapter适配) 3-10-2(列表之SimpleAdapter适配) 3-11(Dialog对话框) 3-12-5(Activity跳转与操作) 3-12-6(横竖屏切换处理) 3-13-1(AIDL_Service测试例子) 3-3(ImageButton图片按钮) 3-4(EditText文本编辑) 3-5(CheckBox与监听) 3-6(RadioButton与监听) 3-7(ProgressBar进度条) 3-8(SeekBar 拖动条) 3-9(Tab分页式菜单) 4-10(可视区域) 4-11-1(Animation动画) 4-11-2-1(动态位图) 4-11-2-2(帧动画) 4-11-2-3(剪切图动画) 4-13(操作游戏主角) 4-14-1(矩形碰撞) 4-14-2(圆形碰撞) 4-14-4(多矩形碰撞) 4-14-5(Region碰撞检测) 4-15-1(MediaPlayer音乐) 4-15-2(SoundPool音效) 4-16-1(游戏保存之SharedPreference) 4-16-2(游戏保存之Stream) 4-3(View游戏框架) 4-4(SurfaceView游戏框架) 4-7-1(贝塞尔曲线) 4-7-2(Canvas画布) 4-8(Paint画笔) 4-9(Bitmap位图渲染与操作) 5-1(飞行射击游戏实战) 6-1(360°平滑游戏摇杆) 6-10-1(Socket协议) 6-10-2(Http协议) 6-11(本地化与国际化) 6-2(多触点缩放位图) 6-3(触屏手势识别) 6-4(加速度传感器) 6-5(9patch工具)] 6-6(截屏) 6-8(游戏视图与系统组件) 6-9(蓝牙对战游戏) 7-10-1(遍历Body) 7-10-2(Body的m_userData) 7-11(为Body施加力) 7-12(Body碰撞监听) 7-13-1(距离关节) 7-13-2(旋转关节) 7-13-3(齿轮关节) 7-13-4(滑轮关节) 7-13-5-1(通过移动关节移动Body) 7-13-5-2(通过移动关节绑定两个Body动作) 7-13-6(鼠标关节-拖拽Body) 7-14(AABB获取Body) 7-4(Box2d物理世界) 7-5在物理世界中添加矩形) 7-7(添加自定义多边形) 7-9(在物理世界中添加圆形) 8-1(迷宫小球) 8-2(堆房子) 9-1(OPENGL中画正方体) 9-2(OPENGL中纹理小测试) JBox2D-src.zip jbox2d.jar
前言 第一部分 准备篇 第1章 Android开发简介 1.1 Android基本概念 1.1.1 Android简介 1.1.2 Android的系统构架 1.1.3 Android应用程序框架 1.2 OMS介绍 1.2.1 OPhone介绍 1.2.2 Widget介绍 1.3 小结 第2章 Android开发环境搭建 2.1 Android开发准备工作 2.2 开发包及其工具的安装和 配置 2.2.1 安装JDK和配置Java开发 环境 2.2.2 Eclipse的安装与汉化 2.2.3 SDK和ADT的安装和 配置 2.3 创建第一个Android项目——HeUoAndroid 2.3.1 创建HelloAndroid项目 2.3.2 运行HelloAndroid及模拟器的使用 2.3.3 调试HelloAndroid 2.4 小结 第二部分 基础篇 第3章 Android程序设计基础 3.1 Android程序框架 3.1.1 Android项目目录结构 3.1.2 Android应用解析 3.2 Android的生命周期 3.3 Android程序U设计 3.4 小结 第4章 用户界面开发 4.1 用户界面开发详解 4.1.1 用户界面简介 4.1.2 事件处理 4.2 常用控件应用 4.2.1 文本框(Textiew) 4.2.2 列表(ListView) 4.2.3 提示(T0ast) 4.2.4 编辑框(EditText) 4.2.5 单项选择(RadioGroup、RadioButton 4.2.6 多项选择(CheckBox) 4.2.7 下拉列表(Spinner) 4.2.8 自动提示(AutoComplete.TextⅥew) 4.2.9 日期和时间(DatePicker、TimePicker) 4.2.10 按钮(Button) 4.2.1l 菜单(Menu) 4.2.12 对话框(Dialog) 4.2.13 图片视图(ImageView) 4.2.14 带图标的按钮(ImageButton) 4.2.15 拖动效果(Gallery) 4.2.16 切换图片(hmgeSwilcher) 4.2.17 网格视图(GridView) 4.2.18 卷轴视图(ScrollView) 4.2.19 进度条(ProgressBar) 4.2.20 拖动条(SeekBar) 4.2.21 状态栏提示(Notification、NotificationManager) 4.2.22 对话框中的进度条(ProgressDialog) 4.3 界面布局 4.3.1 垂直线性布局 4.3.2 水平线性布局 4.3.3.相对布局(RelativeLayout) 4.3.4 表单布局(TableLayout) 4.3.5 切换卡(TabWidget) 4,4 小结 第5章 Android游戏开发 5.1 Android游戏开发框架 5.1.1 View类开发框架 5.1.2 SurfaceView类开发框架 5.2 Graphics类开发 5.5.1 Paint和Color类介绍 5.2.2 Canvas类介绍 5.2.3 几何图形绘制 5.2.4 字符串绘制 5.2.5 图像绘制 5.2.6 图像旋转 5.2.7 图像缩放 5.2.8 图像像素操作 5.2.9 Shader类介绍 5.2.10 双缓冲技术 5.2.11 全屏显示 5.2.12 获得屏幕属性 5.3 动画实现 5.3.1 Tween动画 5.3.2 Frame动画 5.3.3 GIF动画播放 5.4 小结 第6章 Android数据存储 6.1 Android数据存储初探 6.2 数据存储之Shared Preferences 6.3 数据存储之Files 6.4 数据存储之Network 6.5 Android数据库编程 6.5.1 SQLite简介 6.5.2 SQLite编程详解 6.5.3 SQLiteOpenHelper应用 6.6 数据共享(ContentProviders) 6.7 小结 第7 章多媒体开发 7.1 多媒体开发详解 7.1.1 Open Core 7.1.2 MediaPlayer 7.1.3 MediaRecorder 7.2 播放音乐 7.3 播放视频 7.4 录制歌曲 7.5 相机设置 7.6 闹钟设置 7.7 铃声设置 7.8 小结 第8章 网络与通信 8.1 网络通信基础 8.1.1 无线网络技术 8.1.2 Android网络基础 8.2 HTTP通信 8.2.1 HttpURLConnection接口 8.2.2 HttpClient接口 8.2.3 实时更新 8.3 Socket通信 8.3.

39,083

社区成员

发帖
与我相关
我的任务
社区描述
HTML5是构建Web内容的一种语言描述方式。HTML5是互联网的下一代标准,是构建以及呈现互联网内容的一种语言方式.被认为是互联网的核心技术之一。
社区管理员
  • HTML5社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧