本例中 Canvas.Context Save 和 Restore 的目的是什么?

2023-12-23

这一页 http://www.authorcode.com/text-animation-in-html5/在 HTML5 画布中显示一些动画。如果你看一下滚动条的来源 http://www.authorcode.com/scrolling-text-right-to-left-in-html5,有一条语句在清除矩形并在动画后恢复它后保存上下文。如果我用另一个替换恢复语句ctx.clearRect(0, 0, can.width, can.height声明,没有任何作用。我认为恢复正在恢复清除的矩形,但它似乎恢复了更多信息。下一帧需要什么额外信息?

我不是在寻找保存和恢复的 HTML5 教科书定义,但我想了解为什么在这个特定示例中需要它们。

UPDATE

我在问题中已经明确提到过我不想获得 save() 和 Restore() 的定义,但得到答案是令人沮丧的。我已经知道 Save() 保存上下文的状态,而 Restor()e 恢复它。我的问题非常具体。当所有 Save 操作都保存为空画布时,为什么要以示例中的方式使用 Restore() 呢?为什么恢复空画布not和清零一样吗?


画布状态并不是在其上绘制的内容。它是定义当前状态的属性堆栈tools用于绘制next thing.

Canvas 是即时模式位图。 就像 MS Paint 一样。一旦它在那里,它就在那里,所以没有必要“保存”当前的图像数据,因为这就像保存整个 JPEG,每次你进行更改,每一帧......

...不,您保存的状态将决定您用来绘制下一个事物(以及此后的所有事物,直到您手动更改这些值)的坐标方向、尺寸比例、颜色等的状态。

var canvas = document.createElement("canvas"),
    easel  = canvas.getContext("2d");

easel.fillStyle = "rgb(80, 80, 120)";
easel.strokeStyle = "rgb(120, 120, 200)";

easel.fillRect(x, y, width, height);
easel.strokeRect(x, y, width, height);

easel.save();  // stores ALL current status properties in the stack

easel.rotate(degrees * Math.PI / 180); // radians
easel.scale(scale_X, scale_Y); // any new coordinates/dimensions will now be multiplied by these
easel.translate(new_X, new_Y); // new origin coordinates, based on rotated orientation, multiplied by the scale-factor

easel.fillStyle = "gold";
easel.fillRect(x, y, width, height); // completely new rectangle
// origin is different, and the rotation is different, because you're in a new coordinate space

easel.clearRect(0, 0, width, height); // not even guaranteed to clear the actual canvas, anymore
easel.strokeRect(width/2, height/2, width, height); // still in the new coordinate space, still with the new colour


easel.restore(); // reassign all of the previous status properties
easel.clearRect(0, 0, width, height);

假设您只是堆栈深处的一个状态更改,那么既然您的画布之前的状态已恢复,那么最后一行应该已成功清除自身(尽管有亚像素恶作剧)。

正如您所看到的,它与擦除画布关系非常非常小。
事实上,它与删除它完全无关。

它与想要画一些东西有关,并进行基本的轮廓和扫除颜色/样式,然后手动写入顶部较小细节的颜色,然后手动将所有样式写回以前的方式,返回到下一个对象的扫掠笔触,如此反复……

相反,保存将重用的常规状态,为较小的细节创建一个新状态,然后返回常规状态,而不必每次都对其进行硬编码,或者编写 setter 函数来在画布上设置常用值等等(重置缩放/旋转/仿射变换/颜色/字体/线宽/基线对齐/等)。

在你的确切例子中,如果你注意的话,你会发现唯一改变的是step.

他们已经为画布设置了一堆值的状态(颜色/字体/等)。
然后他们保存。那么,他们拯救了什么?
你看的不够深入。他们实际上拯救了默认翻译(即:原始世界空间中的 origin=0,0).
但你没看到他们定义它吗?
那是因为它被定义为默认值。

然后,他们增加第 1 步像素(实际上,他们首先执行此操作,但在第一个循环之后并不重要 - 请留在此处)。
然后他们为 0,0 设置一个新的原点(即:从现在开始,当他们输入0,0新的原点将指向画布上完全不同的位置)。

该原点等于 x 是画布的正中间,y 等于当前步骤(即:像素 1 或像素 2 等...以及为什么从 0 开始和从 1 开始之间的差异实际上并不存在没关系)。

然后他们做什么呢?
他们恢复。

那么,他们恢复了什么?
……好吧,他们改变了什么?

他们正在将原点恢复到 0,0
Why?

好吧,如果他们不这样做,会发生什么?
如果画布是 500px x 200px,并且它在我们当前的屏幕空间中从 0,0 开始......那太好了......
然后他们将其转换为宽度/2, 1
好的,现在当他们要求在 0,0 处绘制文本时,他们实际上会在 250, 1 处绘制

精彩的。但下次会发生什么呢?

现在他们按宽度/2, 2 进行翻译
你认为,好吧,那很好...... 0,0 的绘制调用将在 250, 2 发生,因为他们已将其设置为清除数字:canvas.width/2, 2

没有。因为根据我们的屏幕,当前的 0,0 实际上是 250,1。并且一个翻译是相对于其前一个翻译的......

...所以现在您告诉画布从当前坐标 0,0 开始,向左 250,向下 2。
根据屏幕(就像一个窗口,查看地图,而不是地图本身),我们现在位于右侧 500 像素处,距离开始位置向下 3 像素...并且只过去了一帧。

因此,在设置新坐标之前,他们将地图的坐标恢复为与屏幕坐标相同的原点(并且旋转相同,比例、倾斜等......)。

正如您可能猜到的,现在通过查看它,您可以看到文本实际上应该从上到下移动。不是从右到左,就像页面上说的那样......

为什么要这样做?
当绘图命令给你一个x and y就在函数中吗?

如果你想在画布上画一幅画,并且你知道它的高度和宽度,以及你想要左上角的位置,为什么不能这样做:

easel.drawImage(myImg, x, y, myImg.width, myImg.height);

好吧,你可以。
你完全可以做到这一点。没有什么可以阻止你。

事实上,如果你想让它在屏幕上缩放,你只需更新x and y在计时器上,今天就到此为止。

但如果你画的是游戏角色呢?如果角色有一顶帽子、戴着手套的手和大靴子,并且所有这些东西都与角色分开绘制怎么办?

所以首先你会说“好吧,他站在世界上的 x 和 y 处,所以 x 加上他的手相对于他的身体的位置将是 x + body.x - hand.x...或者是那个加号。 ..”

...现在您对他的所有部件进行了绘制调用,这些部件看起来都像一本装满 5 年级数学作业的笔记本。

相反,你可以说:“他是here。设置我的坐标,使 0,0 位于我的家伙的中间”。现在您的绘制调用就像“我的右手距离身体右侧 6 个像素,我的左手距离左侧 3 个像素”一样简单。

绘制完角色后,您可以将原点设置回 0,0,然后可以绘制下一个角色。或者,如果您想尝试它,您可以根据从一个字符到另一个字符的增量,从那里翻译到下一个字符的起源(这将为您节省每次翻译的函数调用)。然后,如果您一直只保存一次状态(原始状态),那么最后,您可以通过调用返回到 0,0.restore.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

本例中 Canvas.Context Save 和 Restore 的目的是什么? 的相关文章

随机推荐

  • epoll_wait 中的 epoll 事件顺序

    我已将一个程序从 select 移植到 epoll 以增加我们可以处理的套接字数量 我已经将socket添加到epoll FD中并且可以愉快地读写了 但是 即使我使用级别触发事件 我也担心套接字可能会饥饿 我担心的情况是 准备好的套接字数量
  • 如何从函数中获取结果(输出)?以后如何使用结果?

    假设我有一个类似的函数 def foo x hello world 如何让函数返回x 这样我就可以将它用作另一个函数的输入或在程序体内使用该变量 我尝试使用return然后使用x另一个函数中的变量 但我得到了NameError那样 For
  • Rails - url_for 在使用命名空间时表现不同(基于当前使用的控制器)

    假设命名空间是 abc 我们有一个控制器 abcs 另一个使用命名空间 abc 的控制器是 defs 为了便于理解 AbcsController Abc DefsController 当电流处于AbcsController url for
  • 使用现有字符串定义定义“resValue”

    我更感兴趣的是在 strings xml 文件而不是 build gradle 中定义我的应用程序的多种风格 鉴于多种风格 我想要一个简化的发布 调试变体 buildTypes release signingConfig signingCo
  • 依赖类型的 ghc-7.6 类实例

    异构列表是 ghc 7 6 的新依赖类型工具给出的示例之一 data HList gt where HNil HList HCons a gt HList t gt HList a t 示例列表 li 编译良好 li HCons Int H
  • Windows Phone 8.1:设备唯一 ID 的 DeviceExtendedProperties 或 DeviceStatus

    我想获取 Windows Phone 中的设备 ID8 1 The 设备扩展属性 http msdn microsoft com library windows apps microsoft phone info deviceextende
  • 让链接执行 2 个操作

    首先 我不知道我是否采取了正确的方法来解决这个问题 所以请耐心等待 我的问题如下 我尝试使用手风琴 其中每个选项卡都是一个类别 展开时 手风琴会显示该类别中的艺术家 到目前为止 一切都很好 现在 我想要实现的另一部分是 一旦我单击选项卡 其
  • 使用 GORM 和 Postgresql 时如何节省 Go 中数据库的时间?

    我目前正在解析时间字符串并将其保存到数据库 Postgresql event Time time Parse 3 04 PM 9 00 PM value of event Time now is 0000 01 01 21 00 00 00
  • Apache Ignite 使用 Spring-Boot 加载两次?

    我正在使用 Spring Boot 1 2 7 RELEASE 和 Apache Ignite 1 4 0 进行原型设计 并注意到有些奇怪的东西 也许这只是一个日志配置 看起来 Apache Ignite 正在尝试用 Spring 启动两次
  • 在重定向之前设置 cookie

    我试图在标头重定向之前设置 cookie 但它不起作用 我读过在标头重定向行之后设置 cookie 应该可以解决问题 但我没有运气 同样 之前的一篇文章建议 如果您在位置重定向中使用 人类 URL 则应该在 cookie 的路径参数中使用
  • 在 Interface Builder 中将文本标记为不需要翻译

    我在 Interface Builder 中使用虚拟文本 这是一个例子 The MYNAME不是真正的界面文本 它是以编程方式设置的viewDidLoad 我可以用特殊方式标记它 以便默认将其从我的翻译文件中排除吗 如果您正在使用ibtoo
  • Capistrano 使用 sudo 甚至使用“set :use_sudo, false”

    我不希望使用 sudo 通过 Capistrano 执行任何远程命令 具体来说 当我跑步时cap deploy setup 第一次时要求我输入 sudo 密码mkdir命令 我添加了set use sudo false到我的deploy r
  • SqlCipher Mingw/msys 问题

    我有一个问题 我无法编译 SqlCipher 我正在用这个http groups google com group sqlcipher browse thread thread 55c6296b56bf4533 c792bbec6df7d4
  • 如何在jquery中引用JSF组件Id? [复制]

    这个问题在这里已经有答案了 我如何在 jquery 中引用 JSF 组件 因为我不知道要引用的组件 id 前面的 id 你可以付出一切NamingContainer http download oracle com javaee 6 api
  • Prometheus-如何监控其他docker容器

    我想用普罗米修斯监控我的docker容器 我可以跑普罗米修斯 with Grafana但我不知道如何指示它监视其他 docker 容器 如果可能的话我想看一些例子 例如我有Ubuntu在我的主机上运行的容器以及Gentoo容器 我如何告诉
  • 中间件应该总是调用下一个?

    我一直在尝试了解 ASP NET 5 管道中间件的真正工作原理 据我所知 中间件只是一个Func
  • 允许对 C++11 中的循环进行编译器优化

    是否允许符合 C 11 的编译器优化 转换此代码 bool x true not an atomic type but suppose bool can be read written atomically while x spins un
  • 用鼠标移动 QGraphicsRectItem

    将 QGraphicsRectItem 添加到场景后 我试图移动它 它会移动 但与鼠标指针有一定的偏移 我认为这只是将鼠标指针位置添加到其原始位置 我不知道如何解决这个问题 这是我的代码 class ucFilter public QGra
  • 覆盖 Python Tkinter 中的默认选项卡行为

    我正在使用 Tkinter 用 Python 编写一个应用程序来管理我的 GUI 有一个文本输入框 我试图在其中实现自动完成功能 该功能将绑定到 Tab 键 我已将 Tab 键绑定到输入框 但是当我按 Tab 时 程序会尝试在 GUI 元素
  • 本例中 Canvas.Context Save 和 Restore 的目的是什么?

    这一页 http www authorcode com text animation in html5 在 HTML5 画布中显示一些动画 如果你看一下滚动条的来源 http www authorcode com scrolling tex