【问题标题】:Only clean partial drawings on the canvas只清理画布上的部分绘图
【发布时间】:2019-12-29 01:45:51
【问题描述】:

我看到您可以使用CanvasRenderingContext2D.clearRect 清除部分画布。此功能将根据指定的坐标清除全部/部分画布。是否可以清除一些前景图,但保持背景图完整?

在反应组件内部,我有以下内容

  componentDidMount() {
    this.canvas = this.canvasRef.current;
    this.c = this.canvas.getContext('2d')

    this.canvas.width = window.innerWidth
    this.canvas.height = window.innerHeight

    window.addEventListener('resize', this.resize)

    this.init() 
    this.backgroundGradientAnimate() //draw background gradient
    window.requestAnimationFrame(this.step)

  }

  // generate stars
  init = () => {
    this.stars = []

    for (let i = 0; i < 150; i++) {
      const x = Math.random() * this.canvas.width;
      const y = Math.random() * this.canvas.height
      const r = Math.random() * 3
      this.stars.push(new Star(x, y, r, 'white'))
    }
  }

  draw = (star) => {
    this.c.save()
    this.c.beginPath()
    this.c.arc(star.x, star.y, star.radius, 0, Math.PI * 2, false)
    this.c.fillStyle = this.getRandomColor()
    this.c.fill();
    this.c.closePath();
    this.c.restore()
  }

  starsAnmiate = () => {
    this.stars.forEach(star => this.draw(star))
  }

  // only animate in such time
  step = (elapsedTime) => {
    let delta = elapsedTime - this.lastFrameTime || 0

    window.requestAnimationFrame(this.step)

  if (this.lastFrameTime && delta < 1000) {
    return;
  }

  this.lastFrameTime = elapsedTime

  this.starsAnmiate();
  }

问题是动画重复后所有的星星都不会消失。有没有办法只清理星星的画,但保留backgroundGradient,或者两张画总是必须同时清理,因为它们位于同一个画布上?

总的来说,我正在尝试创建这些星星的闪烁效果。当我每次都需要清理/重绘整个画布来模拟每颗星星的闪烁时,这似乎是很多开销。

[编辑]

  1. 为了优化动画效果,您可以将 2 个单独的画布绘制为 HERE

【问题讨论】:

  • 是的,这很有帮助,我会更新我的问题。是否可以只更新星星的不透明度,而不是重新绘制所有星星?
  • 很遗憾没有。画布上的任何东西一旦被绘制就不可能更新。您所能做的就是擦除一个部分或在其上绘制。
  • 我会继续将这些 cmets 移动到答案中,因为它们似乎已经帮助您解决了您的问题。
  • 是的,我没问题。谢谢!

标签: css animation canvas


【解决方案1】:

有几种方法可以做到这一点,但它们都涉及使用某种解决方法。

从本质上讲,这听起来像是您希望在单个画布上拥有多个,这样您就可以从一个而不是其他的中擦除。不幸的是,没有允许您执行此操作的本机功能。

画布被设置为具有一个(且只有一个)上下文,2D 或 3D。当您在上下文中绘制时,您会更改构成该画布的各个像素的颜色。 JavaScript 不知道某个绘图下面是什么,因为它直接对画布的每个像素进行更改(您也可以争辩说 JS 甚至没有 绘图 的概念,但事实并非如此在这里特别重要)。

如果你想拥有多个独立支持擦除的层,你可以做以下两件事之一,这两者都有缺点:

  • 将多个画布元素堆叠在一起并单独修改每个元素。请注意,使用此方法,您将无法一次轻松导出所有画布的内容。
  • 为每一层维护一个像素的一维Uint8ClampedArray,并直接使用ctx.putImageData以分层顺序绘制每个数组。使用类型化数组比原始数组更快,但它的数据结构在概念上有点不稳定。您可以阅读有关此方法的更多信息 herehere

【讨论】:

  • 你的第二点没有意义。 putImageData 会将其区域中的像素替换 到 ImageData 的像素,无法进行任何合成,您不能将“置于”任何现有内容的“顶部”。第一种方法的最大缺点是内存和绘画时间,通过 CSS 进行分层比说 ctx.drawImage(canvas1,0,0); 要做的工作要多得多。 ctx.drawImage(canvas2,0,0) 等。最简单的方法是全部清除并重绘每一帧。如果您有一个不经常更改的图层,请考虑在 DOM 画布之外将其设为自己的图层。
  • @Kaiido 我在回答中关注的部分是维护每层像素内容的记录。最终的想法是使用 OffscreenCanvas 或使用 get,合并像素值,然后使用 put 将其添加回主画布,但我没有提到这一点,所以我的回答很模糊。感谢您指出了这一点。 :)
猜你喜欢
  • 2017-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-22
  • 1970-01-01
相关资源
最近更新 更多