请教下canvas画图:为什么用save和restore

从此程序员 2014-08-20 02:02:00
<!DOCTYPE HTML>
<html>
<head>
<script language="javascript">
function drawTop(ctx, fillStyle){
ctx.fillStyle = fillStyle;
ctx.beginPath();
ctx.arc(0, 0, 30, 0,Math.PI,true);
ctx.closePath();
ctx.fill();
}
function draw(){
var ctx = document.getElementById('myCanvas').getContext("2d");
// 注意:所有的移动都是基于这一上下文。
ctx.translate(80,80);
for (var i=1;i<10;i++){
<!--ctx.save();-->
ctx.translate(60*i, 0);
drawTop(ctx,"rgb("+(30*i)+","+(255-30*i)+",255)");
<!--ctx.restore();-->
}
}
window.onload=function(){
draw();
}
</script>
</head>
<body>
<canvas id="myCanvas" width="700" height="300"></canvas>
</body>
</html>


按照我的理解,每次循环都有画图形出来。为什么要save和restore呢?代码中是把save和restore屏蔽了,发现效果是上图。个人觉得这个代码应该是出来的是下图,为什么是上图呢?
...全文
6866 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
程序猿人生 2016-06-27
  • 打赏
  • 举报
回复
看懂啦,但是还有一点不是很理解 起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))这个是一个公式吗
qq_26556925 2016-04-04
  • 打赏
  • 举报
回复
厉害,虽然现在隔了将近2年依然有用...豁然开朗啊
menghuan0625 2015-11-16
  • 打赏
  • 举报
回复
看过回复贴之后,很受用,对save和restore也有了一定的了解
Cheris2014 2014-09-05
  • 打赏
  • 举报
回复
很好的回复贴,学习了!
君鹏不姓贾 2014-09-05
  • 打赏
  • 举报
回复
哈哈,看到自己的回复有用真是太好了
yes_353535 2014-08-29
  • 打赏
  • 举报
回复
看完帖子,我也搞懂了,对于理解save和restore有了一个清晰了解
从此程序员 2014-08-22
  • 打赏
  • 举报
回复
引用 12 楼 jasenin 的回复:
[quote=引用 11 楼 junpengbuxingjia 的回复:] 举个例子吧, 你的循环是从i=1开始的下面分别列出有无save——restore的情况,就写i=1和i=2吧 有save——restore i=1 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(60,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(90,0) 执行restroe起点回到(0,0) i=2 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(120,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(150,0) 执行restroe起点回到(0,0) i=3 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(180,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(210,0) 执行restroe起点回到(0,0) 看到坐标点的变化了吗?你画的半圆直径是60,所以第一个半圆的起止点在(60,0)和(120,0)第二个半圆的起止点在(120,0)和(180,0),第三个半圆的起止点(180,0)和(240,0)所以这些半圆都是相切的,就像下面那个图一样。 无save——restore i=1 执行ctx.translate(),起点变为(60*i,0)即(60,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(90,0) i=2 执行ctx.translate(),起点变为(90+60*i,0)即(210,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(240,0) i=3 执行ctx.translate(),起点变为(240+60*i,0)即(420,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(450,0) 对比坐标,第一个半圆起止点是(60,0)和(120,0),第二个半圆起止点是(210,0)和(270,0),第三个半圆的起止点是(420,0)和(480,0)。看到坐标了吗?第一个半圆的止点与第二个半圆的起点差了90,第二个半圆的止点与第三个半圆的起点差了150,所以你画出的图像就错位了,就像你的上图一样
exactly ,热心的junpengbuxingjia小朋友[/quote] 需要了解的朋友参照这个解释和我附上的图,就能完全理解了
从此程序员 2014-08-22
  • 打赏
  • 举报
回复
引用 11 楼 junpengbuxingjia 的回复:
举个例子吧, 你的循环是从i=1开始的下面分别列出有无save——restore的情况,就写i=1和i=2吧 有save——restore i=1 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(60,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(90,0) 执行restroe起点回到(0,0) i=2 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(120,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(150,0) 执行restroe起点回到(0,0) i=3 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(180,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(210,0) 执行restroe起点回到(0,0) 看到坐标点的变化了吗?你画的半圆直径是60,所以第一个半圆的起止点在(60,0)和(120,0)第二个半圆的起止点在(120,0)和(180,0),第三个半圆的起止点(180,0)和(240,0)所以这些半圆都是相切的,就像下面那个图一样。 无save——restore i=1 执行ctx.translate(),起点变为(60*i,0)即(60,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(90,0) i=2 执行ctx.translate(),起点变为(90+60*i,0)即(210,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(240,0) i=3 执行ctx.translate(),起点变为(240+60*i,0)即(420,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(450,0) 对比坐标,第一个半圆起止点是(60,0)和(120,0),第二个半圆起止点是(210,0)和(270,0),第三个半圆的起止点是(420,0)和(480,0)。看到坐标了吗?第一个半圆的止点与第二个半圆的起点差了90,第二个半圆的止点与第三个半圆的起点差了150,所以你画出的图像就错位了,就像你的上图一样
exactly ,热心的junpengbuxingjia小朋友
君鹏不姓贾 2014-08-22
  • 打赏
  • 举报
回复
举个例子吧, 你的循环是从i=1开始的下面分别列出有无save——restore的情况,就写i=1和i=2吧 有save——restore i=1 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(60,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(90,0) 执行restroe起点回到(0,0) i=2 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(120,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(150,0) 执行restroe起点回到(0,0) i=3 执行save保存起点(0,0) 执行ctx.translate(),起点变为(60*i,0)即(180,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(210,0) 执行restroe起点回到(0,0) 看到坐标点的变化了吗?你画的半圆直径是60,所以第一个半圆的起止点在(60,0)和(120,0)第二个半圆的起止点在(120,0)和(180,0),第三个半圆的起止点(180,0)和(240,0)所以这些半圆都是相切的,就像下面那个图一样。 无save——restore i=1 执行ctx.translate(),起点变为(60*i,0)即(60,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(90,0) i=2 执行ctx.translate(),起点变为(90+60*i,0)即(210,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(240,0) i=3 执行ctx.translate(),起点变为(240+60*i,0)即(420,0) 执行drawTop():起点偏移到(x + radius*cos( startAngle), y + radius*sin( startAngle))即(450,0) 对比坐标,第一个半圆起止点是(60,0)和(120,0),第二个半圆起止点是(210,0)和(270,0),第三个半圆的起止点是(420,0)和(480,0)。看到坐标了吗?第一个半圆的止点与第二个半圆的起点差了90,第二个半圆的止点与第三个半圆的起点差了150,所以你画出的图像就错位了,就像你的上图一样
从此程序员 2014-08-22
  • 打赏
  • 举报
回复
引用 8 楼 junpengbuxingjia 的回复:
OK再说细一点,对比上下图就会发现你的图是错位了。画第一个半圆的时候两者是一样的,但是当你执行完一次ctx.arc(0, 0, 30, 0,Math.PI,true);之后ctx的位置就会根据(x + radius*cos( startAngle), y + radius*sin( startAngle))这个公式变化,具体到你的例子就是每次向右移动30,然后你在循环中因为没有把ctx的状态还原在此基础上又移动了60*i的距离,画第二个半圆时传入起点位置自然就不和第一个园相邻了,图像就错位了。
正解。。。有小伙伴一起讨论就是好啊
从此程序员 2014-08-22
  • 打赏
  • 举报
回复
理解了,哈哈。大家可以参照这个博客:http://blog.csdn.net/oney139/article/details/8143281 总结就是save之后的操作都是对整个canvas的操作,、所以之后的所有操作都会受前面的动作影响。我贴出来的图片(没有用save和restore)为什么只有四个半圆。是因为之后所以translate是基于上一次平移之后的坐标。
君鹏不姓贾 2014-08-22
  • 打赏
  • 举报
回复
OK再说细一点,对比上下图就会发现你的图是错位了。画第一个半圆的时候两者是一样的,但是当你执行完一次ctx.arc(0, 0, 30, 0,Math.PI,true);之后ctx的位置就会根据(x + radius*cos( startAngle), y + radius*sin( startAngle))这个公式变化,具体到你的例子就是每次向右移动30,然后你在循环中因为没有把ctx的状态还原在此基础上又移动了60*i的距离,画第二个半圆时传入起点位置自然就不和第一个园相邻了,图像就错位了。
君鹏不姓贾 2014-08-22
  • 打赏
  • 举报
回复
引用 4 楼 jasenin 的回复:
[quote=引用 2 楼 junpengbuxingjia 的回复:] 因为ctx是一个全局对象,ctx.save()的时候保存了ctx当前的状态值,然后你执行了一些值的修改,比如改变阴影大小,在函数最后调用ctx.restore()方法将ctx之前的状态值还原回去。 当楼主注释掉save和restore后,这时调用draw函数时ctx的画笔状态就不会保存与还原(这么说你懂吗?注释掉了save就不会保存之前的状态,同样画完也不会还原画笔状态)
这个我懂啊,你看下代码,for循环里面每次都调用drawTop(ctx,"rgb("+(30*i)+","+(255-30*i)+",255)"),而这个函数里面是一个完整的动作,我只要一直调用就能画下去不是。[/quote] 是啊,你写的只是画图的全过程啊,画笔状态还有其他很多信息啊,虽然你每次都是完成了一个图形的绘制,但是每次画笔的其他属性也都会变化了。你对比一下你的上下图,就会发现图形的颜色都没错,只是上图的图型错位了,
u018875539 2014-08-22
  • 打赏
  • 举报
回复
请参阅http://www.cnblogs.com/xirihanlin/archive/2009/07/24/1530246.html 百度一下你就知道
从此程序员 2014-08-22
  • 打赏
  • 举报
回复
就像算出1+2+3 用for循环来算,我没必要保存状态不是
从此程序员 2014-08-22
  • 打赏
  • 举报
回复
引用 2 楼 junpengbuxingjia 的回复:
因为ctx是一个全局对象,ctx.save()的时候保存了ctx当前的状态值,然后你执行了一些值的修改,比如改变阴影大小,在函数最后调用ctx.restore()方法将ctx之前的状态值还原回去。 当楼主注释掉save和restore后,这时调用draw函数时ctx的画笔状态就不会保存与还原(这么说你懂吗?注释掉了save就不会保存之前的状态,同样画完也不会还原画笔状态)
这个我懂啊,你看下代码,for循环里面每次都调用drawTop(ctx,"rgb("+(30*i)+","+(255-30*i)+",255)"),而这个函数里面是一个完整的动作,我只要一直调用就能画下去不是。
sun_7_flower 2014-08-21
  • 打赏
  • 举报
回复
楼上大神!
君鹏不姓贾 2014-08-21
  • 打赏
  • 举报
回复
因为ctx是一个全局对象,ctx.save()的时候保存了ctx当前的状态值,然后你执行了一些值的修改,比如改变阴影大小,在函数最后调用ctx.restore()方法将ctx之前的状态值还原回去。 当楼主注释掉save和restore后,这时调用draw函数时ctx的画笔状态就不会保存与还原(这么说你懂吗?注释掉了save就不会保存之前的状态,同样画完也不会还原画笔状态)
从此程序员 2014-08-21
  • 打赏
  • 举报
回复
如何使用save和restore呢,这个应该是个基础问题。求懂的兄弟解惑

220

社区成员

发帖
与我相关
我的任务
社区描述
T客论坛是TCL为移动开发者提供的一个交流沟通的平台。开发者可以在T客论坛聚合有共同兴趣的队员,利用TCL开放平台的开放API,开发出优秀有创意的TV版本的应用,实用工具。
社区管理员
  • T客
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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