canvas图片旋转绘制
最近项目有个需求根据后端提供的图片旋转角度在页面上显示正确方向的图片,(手机端拍摄的图片会带个角度,这个角度pc浏览器无法识别,图片就无法显示正确的图片方向,需要自己手动控制旋转),要求宽度是固定的高度自适应并且保证图片不能变形,一开始采用的是img的形式,img旋转之后不但坐标会混乱处理着麻烦,而且90度和270度的图片旋转成正向还会有空白滚动条的问题,最后决定用canvas实现。
这里绘制的图片要刚好铺满整个canvas,所以canvas的宽高就是图片的宽高,图片的高度是要与宽等比例不能变形的,canvas的宽等于父容器的clientWidth,图片可能很高会有滚动条(naturalWidth:图片的原始宽,naturalWidth:图片的原始高),根据宽高比 width/height = canvas.width/canvas.height 计算得出canvas.height ,canvas本身是无法实现滚动条效果,所以需要个它添加一个父容器。
HTML:
1 2 3 | < div class="wrap"> < canvas id="canvas"></ canvas > </ div > |
CSS
1 2 3 4 5 6 7 | < style > .wrap{ width: 500px; height: 300px; overflow-y: scroll; } </ style > |
js部分:
首先获取父容器和canvas,生成canvas的绘图环境,通过new Image()对象生成一个图片对象实例,对象实例通过src赋值图片,之后一切操作都放到onload 函数里面,图片加载完成后进行,不在这里面进行就无法获取到图片的原始尺寸。获取原始尺寸后通过宽高比列计算出需要设置图片的高度,在这里也是canvas的高度。
图片旋转角度为0时:直接计算完成通过ctx.drawImage方法绘制即可。
<script> let wrap = document.querySelector('.wrap') let canvas = document.querySelector('#canvas'); let ctx = canvas.getContext('2d'); canvas.width = wrap.clientWidth; const img = new Image(); img.src = 'img/bg.jpg'; img.onload = function(){ let width = this.naturalWidth; let height = this.naturalHeight; canvas.height = canvas.width * height / width; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); } </script>
效果:
图片旋转角度为90和270度时:因为这里牵涉到旋转所以要先知道canvas默认旋转中心点是哪里。根据下面图片可以直接看到中心点在左上角。因为是旋转90和270所以图片的宽高是互换的。
这里的计算 canvas.height = canvas.width * height / width; 要改成 canvas.height = canvas.width * width / height;
默认中心点在左上角一转就抛出画布范围里,为了方便操作和调试先把旋转中心点拉到中心来。
1 | ctx.translate(canvas.width * 0.5, canvas.height * 0.5); |
这是拉到中心为旋转的效果,然后给它旋转放正,这里可以正270度和负90度都可以把图片放正,我选择正270度。
旋转后效果
可以看到图片角度没问题了,宽高再拉回来一半就可以了
const img = new Image();
img.src = 'img/bg1.jpg';
img.onload = function(){
let width = this.naturalWidth;
let height = this.naturalHeight;
canvas.height = canvas.width * width / height;
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
ctx.rotate(1.5 * Math.PI);
ctx.drawImage(img, - canvas.height / 2, - canvas.width / 2, canvas.height, canvas.width);
}
图片旋转角度为180时:和90度的操作一样,只不过宽高不会互换罢了。
1 2 3 4 5 6 7 8 9 10 | const img = new Image(); img.src = 'img/bg2.jpg' ; img.onload = function (){ let width = this .naturalWidth; let height = this .naturalHeight; canvas.height = canvas.width * height / width; ctx.translate(canvas.width * 0.5, canvas.height * 0.5); ctx.rotate(1 * Math.PI); ctx.drawImage(img, - canvas.width / 2, - canvas.height / 2, canvas.width, canvas.height); } |
全部代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta http-equiv= "X-UA-Compatible" content= "IE=edge" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Document</title> <style> *{ margin: 0; padding: 0; } .wrap{ width: 500px; height: 300px; overflow-y: scroll; margin: 20px; } </style> </head> <div class = "wrap" > <canvas id= "canvas" ></canvas> </div> <body> <script> let wrap = document.querySelector( '.wrap' ) let canvas = document.querySelector( '#canvas' ); let ctx = canvas.getContext( '2d' ); canvas.width = wrap.clientWidth; const img = new Image(); img.src = 'img/bg1.jpg' ; let angle = 3; //旋转角度 1:90度,2:180度,3:270 img.onload = function (){ let width = this .naturalWidth; let height = this .naturalHeight; if (angle === 0){ canvas.height = canvas.width * height / width; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); } else if (angle === 1 || angle === 3){ canvas.height = canvas.width * width / height; ctx.translate(canvas.width * 0.5, canvas.height * 0.5); angle === 1 ? ctx.rotate(0.5 * Math.PI) : ctx.rotate(1.5 * Math.PI); ctx.drawImage(img, - canvas.height / 2, - canvas.width / 2, canvas.height, canvas.width); } else { canvas.height = canvas.width * height / width; ctx.translate(canvas.width * 0.5, canvas.height * 0.5); ctx.rotate(1 * Math.PI); ctx.drawImage(img, - canvas.width / 2, - canvas.height / 2, canvas.width, canvas.height); } } </script> </body> </html> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!