如何在canvas画布中旋转图像

位置: 首页 > 前端三套件
[发布: 2024.3.30  作者: 马黑  阅读: 121]

canvas画布要绘制旋转的图像,它不是旋转图像对象,它没有这个能力,它要做的是临时旋转绘画坐标系,再绘制图像。我们来看看在 400*400 的画布上绘制于画布中心的小矩形如何旋转:

0

尽管我们说调节矩形旋转角度——这确实也是我们直观观察到的效果——,在画布上旋转 100*40 的小矩形,实现机制却是要旋转画布坐标系。我们可以通过核心代码来理解这个问题:

ctx.clearRect(0, 0, 400, 400); //把黑板擦干净 ctx.save(); //保存画笔状态 ctx.rotate(deg * Math.PI / 180); //旋转 deg 度 ctx.fillRect(x,y,w,h); //绘制矩形 :(x,y)为矩形左上角坐标点,(w,h)为矩形宽高尺寸 ctx.restore(); //还原画笔状态

核心代码是第三行,用 rotate() 方法旋转画笔的绘图坐标系,这个坐标系也可以理解为是画布的坐标系,我们在本文的讨论过程中这两种提法都有。rotate() 需要一个参数,旋转的弧度,因此参数里是一个式子,它将获取到的 deg 角度值换算为弧度值。

演示实例的效果表明,矩形不是在自己的原始位置上旋转,它所旋转的 0 以外的任意角度,都会伴随着位置移动。这是由于画笔旋转坐标系是有一个固定的原点,在画布的左上角,坐标值是(0,0),上例中矩形的每一次旋转都围绕那个点进行,因旋转而发生的移动轨迹是一个弧形路线。也许大家会觉得奇怪,旋转为什么会产生移动?回顾一下旋转的实现机制:是画布在虚拟地旋转,即,画布旋转它的画笔坐标系,这个旋转带动矩形跟着旋转并移位。画布虚拟旋转并在其上绘制图像,可以视为是一个动态的图层,因此旋转的过程我们看不到实质性的画布旋转,而是矩形的旋转与移位。

上面的图像旋转方式应该不是我们所需要的,往往,我们对旋转运动的需求是图像在原地旋转,绕图像自己的中心点进行。画布可以做到这一点,思路与流程严格按照下面的描述进行:

① 用 translate(cx,cy) 方法临时迁移画布坐标系到图像的中心点;
② 旋转画布坐标系 deg 个角度:rotate(deg * Math.PI / 180);
③ 反向将画布坐标系移回初始坐标点 translate(-cx,-cy);
④ 绘制图像。

步骤一需要获知画布坐标系原点移动到哪里,它应当与矩形的中心重合才能达到矩形原地旋转的目的,因此,(cx,cy) 既是新坐标系的原点,也是矩形的中心点。新坐标系原点、矩形中心点共同的坐标 (cx,cy) 和矩形自身绘制时的左上角坐标(x,y)以及矩形的宽高(w,h)存在这样的数学关系:

cx = x + w / 2
cy = y + h / 2

即,矩形中心点坐标和画布新坐标系原点坐标 cx 值等于矩形左上角 x 坐标值加上矩形宽度的一半、cy 值等于矩形左上角坐标 y 坐标值加上矩形高度的一半。有了这个数据,步骤三画布坐标系原路返回就不是个问题 。

而步骤四,即绘制矩形,为什么要放在画布坐标系复原之后而不出之前呢?这是画布的思维异于常人的表现之一,它先把规划制定好,每一个规划细节都不遗漏地记录下来,规划做完了才一鼓作气把东西fill或stroke上去,而不管不顾常人的正常先后次序逻辑。切记:画布坐标系反向复原之后才能绘制矩形(或其它图像)。

这里扩展一下:如果旋转的是用 arc() 或 arcTo() 绘制成的圆,由于 arc 和 arcTo 使用圆心而不是左上角xy坐标值定位,圆的圆心即为画布坐标系移动的坐标值,省却了一些计算。

最后看效果:

0

附:canvas画布绘制原地旋转圆源码

<style> .mama { font: normal 18px/26px sans-serif; } #canv { display: block; margin: auto; border: 1px solid gray; } .wrap { margin: 20px auto; width: 360px; } .tMid { text-align: center; } </style>   <div class="mama">     <h2 class="tMid">在canvas上绘制绕圆心旋转的圆</h2>     <canvas id="canv"></canvas>     <div class="wrap">         <label for="rngCx">调节圆心X坐标位置 :</label>         <input id="rngCx" type="range" min="0" max="400" value="60" />         <output id="cxData">60</output>         <br>         <label for="rngCy">调节圆心Y坐标位置 :</label>         <input id="rngCy" type="range" min="0" max="400" value="60" />         <output id="cyData">60</output>     </div> </div>   <script>   var ctx = canv.getContext('2d'); var w = canv.width = 400, h = canv.height = 400; var deg = 0, cx = 60, cy = 60, r = 50, raf = null;   /* 径向渐变 */ var gradient = ctx.createRadialGradient(190, 200, 15, 200, 200, 280); gradient.addColorStop(0, 'red'); gradient.addColorStop(.15, 'orange'); gradient.addColorStop(.3, 'yellow'); gradient.addColorStop(.45, 'green'); gradient.addColorStop(.51,'cyan'); gradient.addColorStop(.85,'blue'); gradient.addColorStop(1,'purple');   ctx.fillStyle = gradient;   draw_degCircle();   /* 滑杆输入事件 :改变圆心 */ rngCx.oninput = rngCy.oninput = function(e) {     raf = cancelAnimationFrame(raf);     cx = cxData.value = rngCx.value;     cy = cyData.value = rngCy.value;     draw_degCircle(); };   /* 函数 :画绕圆心旋转的圆 */ function draw_degCircle() {     ctx.clearRect(0,0,400,400);     ctx.save();     ctx.beginPath();     ctx.translate(cx, cy);     ctx.rotate(deg * Math.PI / 180);     ctx.translate(-cx, -cy);     ctx.arc(cx, cy, r, 0, 2 * Math.PI);     ctx.fill();     ctx.restore();     deg = (deg + 1) % 360;     raf = requestAnimationFrame(draw_degCircle); };   </script>

代码可以复制到 pencil code 运行以查看效果,也可以将代码存为本地HTML文档后运行。

前一篇: CANVAS画布图形合成模式演示
下一篇: canvas画布判断鼠标指针是否在圆和矩形内

发表评论:

  
 

评论列表 [1条]

#1 | 飞飞 于 2024-3-30 17:02 发布: 矩形0.0位置旋转和以中心点位置旋转可以理解。。下方代里中这个圆的显示,是不是可以想象成一个以中心点向外径向渐变的方形颜色画布(隐藏的),圆的位置到哪里,就显示那一块的渐变。。还是红黄渐变比较自然。。

Copyright © 2023 All Right Reserved 马黑PHP文章管理整站系统v1.8
联系我们: gxblk@163.com