简体   繁体   English

如何在 html 画布中为多个点设置动画

[英]How to animate several dots moving here and there in html canvas

I have a requirement to move many dots here and there inside a canvas.我需要在画布内到处移动许多点。

Hence I created several arcs with different radius and placed them at random places.因此,我创建了几个具有不同半径的弧并将它们放置在随机位置。

 var context = document.getElementById('stage').getContext('2d'); var radian = Math.PI / 180; var x = 40; var y = 40; var r = 20; var colorPoints = []; var frames = 50; var currentFrame = 0; var toggle = false; var iconsLoaded = false; context.beginPath(); context.arc(x,y, r, 0 * radian, 360 * radian, false) context.fill(); var drawMultipleCurves = function(ctx){ if(!iconsLoaded){ for (let i = 0; i < 600; i++) { ctx.beginPath(); ctx.filter = 'blur(5px)'; ctx.fillStyle = '#B835FF'; colorPoints.push({x: Math.floor((Math.random() * 700) + 0), xMove: Math.floor((Math.random() * 2) + 0) , yMove: Math.floor((Math.random() * 2) + 0) , y: Math.floor((Math.random() * 700) + 0), radius: Math.floor((Math.random() * 20) + 5)}); ctx.arc(colorPoints[colorPoints.length - 1].x, colorPoints[colorPoints.length - 1].y, colorPoints[colorPoints.length - 1].radius, 0 * radian, 360 * radian, false); ctx.fill(); ctx.closePath(); iconsLoaded = true; } } else{ for(let i =0;i< colorPoints.length; i++){ if(frames === currentFrame ){ toggle = !toggle; currentFrame = 0; } if(!toggle){ colorPoints[i].xMove === 1 ? colorPoints[i].x = colorPoints[i].x + 5 : colorPoints[i].x = colorPoints[i].x - 5; colorPoints[i].yMove === 1 ? colorPoints[i].y = colorPoints[i].y + 5 : colorPoints[i].y = colorPoints[i].y - 5; } else{ colorPoints[i].xMove === 1 ? colorPoints[i].x = colorPoints[i].x - 5 : colorPoints[i].x = colorPoints[i].x + 5; colorPoints[i].yMove === 1 ? colorPoints[i].y = colorPoints[i].y - 5 : colorPoints[i].y = colorPoints[i].y + 5; } ctx.beginPath(); ctx.arc(colorPoints[i].x, colorPoints[i].y, colorPoints[i].radius, 0 * radian, 360 * radian, false); context.closePath( ); ctx.fill(); currentFrame = currentFrame + 1; } } } var animate = function(){ setTimeout(()=>{ context.clearRect(0,0,400,400); context.beginPath(); drawMultipleCurves(context); context.fill(); requestAnimationFrame(animate) }, 1000/30) } requestAnimationFrame(animate)
 <canvas id="stage" width="400" height="400"> <p>Your browser doesn't support canvas.</p> </canvas>

Above is the code that I have tried.以上是我尝试过的代码。 I have first created and placed several dots at random places with random radius.我首先在随机半径的随机位置创建并放置了几个点。 When I created them I saved all these random places in an array 'colorPoints'当我创建它们时,我将所有这些随机位置保存在数组“colorPoints”中

Now I'm looping into this array and moving all the dots everytime 'requestAnimation' is called.现在我循环进入这个数组并在每次调用“requestAnimation”时移动所有的点。

I'm able to achieve my animation of moving the dots randomly but as I have used 800 dots and then saving them into an array and then again looping them to move their position, the animation is not looking smooth.我能够实现随机移动点的动画,但是由于我使用了 800 个点,然后将它们保存到数组中,然后再次循环它们以移动它们的位置,因此动画看起来并不流畅。

It looks like it is moving and strucking.它看起来像在移动和撞击。 How can I achieve this animation smoothly?我怎样才能顺利实现这个动画?

Thanks in advance :)提前致谢 :)

Render "fill" once per style每种样式渲染一次“填充”

Your code is slowing down due to where you placed fill (same if you use stroke )由于放置fill的位置,您的代码速度变慢(如果使用stroke则相同)

When you have many objects with the same style call fill only once per frame for each object.当您有许多具有相同样式的对象时,每个对象每帧只调用一次fill

You had something like你有类似的东西

  for (const c of circles) { 
      ctx.beginPath();
      ctx.arc(c.x, c.y, c.r, 0, TAU) 
      ctx.fill();
  }

With a filter active the fill command forces the filter to be reset, which for blur is complex.激活过滤器后,填充命令会强制重置过滤器,这对于模糊来说很复杂。

Rather add all the arcs then fill.而是添加所有弧然后填充。

   ctx.beginPath();
   for (const c of circles) { 
      ctx.moveTo(c.x + c.r, c.y);
      ctx.arc(c.x, c.y, c.r, 0, TAU) 
   }
   ctx.fill();

The move ctx.moveTo(cx + cr, cy);移动ctx.moveTo(cx + cr, cy); is used to close the previous arc.用于关闭前一个弧。

You can also close the arc with ctx.closePath but this can be a lot slower when you have many arcs in the path buffer.您也可以使用ctx.closePath关闭弧,但是当路径缓冲区中有许多弧时,这可能会慢很多。

   // slower than using moveTo
   ctx.beginPath();
   for (const c of circles) { 
      ctx.arc(c.x, c.y, c.r, 0, TAU) 
      ctx.closePath();
   }
   ctx.fill();

Example例子

Example draws 600 arcs using the blur filter as it only calls fill once per frame.示例使用模糊滤镜绘制 600 条弧线,因为它每帧只调用一次fill This should run smooth on all but the most low end devices.除了最低端的设备外,这应该可以在所有设备上顺利运行。

See function drawCircles参见函数drawCircles

 requestAnimationFrame(animate); const ctx = canvas.getContext('2d'); const W = canvas.width; const BLUR = 5; const CIRCLE_COUNT = 600; const MIN_RADIUS = BLUR; const MAX_RADIUS = 30; const MAX_DELTA = 1; const MAX_CIR_R = MAX_RADIUS + BLUR; const MOVE_SIZE = MAX_CIR_R * 2 + W; const TAU = 2 * Math.PI; const setOf = (c, cb, i = 0, a = []) => { while(i < c) { a.push(cb(i++)) } return a }; const rnd = (m, M) => Math.random() * (M - m) + m; const style = { filter: "blur(" + BLUR + "px)", fillStyle: '#B835FF', }; var currentStyle; function setStyle(ctx, style) { if (currentStyle !== style) { Object.assign(ctx, style); currentStyle = style; } } const circle = { get x() { return rnd(-MAX_CIR_R, W + MAX_CIR_R) }, get y() { return rnd(-MAX_CIR_R, W + MAX_CIR_R) }, get dx() { return rnd(-MAX_DELTA, MAX_DELTA) }, get dy() { return rnd(-MAX_DELTA, MAX_DELTA) }, get r() { return rnd(MIN_RADIUS, MAX_RADIUS) }, move() { var x = this.x + this.dx + MOVE_SIZE + MAX_CIR_R; var y = this.y + this.dy + MOVE_SIZE + MAX_CIR_R; this.x = x % MOVE_SIZE - MAX_CIR_R; this.y = y % MOVE_SIZE - MAX_CIR_R; } }; const circles = setOf(CIRCLE_COUNT, () => Object.assign({}, circle)); function drawCircles(circles, ctx, style) { setStyle(ctx, style); ctx.beginPath(); for (const c of circles) { ctx.moveTo(cx + cr, cy); ctx.arc(cx, cy, cr, 0, TAU); } ctx.fill(); } function updateCircles(circles) { for (const c of circles) { c.move(); } } function animate() { ctx.clearRect(0,0,W, W); updateCircles(circles); drawCircles(circles, ctx, style); requestAnimationFrame(animate); }
 <canvas id="canvas" width="600" height="600"> </canvas>

If you have several colors, group all the same colors so you can keep the number of fill calls as low as possible.如果您有多种颜色,请将所有相同的颜色分组,这样您就可以将填充调用的数量保持在尽可能低的水平。

There are many ways to get the same effect with many colors (each circle a different color) but will need more setup code.有很多方法可以通过多种颜色获得相同的效果(每个圆圈都有不同的颜色),但需要更多设置代码。

The CanvasRenderingContext2D blur filter is quite heavy - especially if you use it on a canvas consisting of 600 circles. CanvasRenderingContext2D模糊滤镜非常重 - 特别是如果您在由 600 个圆圈组成的画布上使用它。 That means on every screen update it has to re-draw 600 circles and apply a blur filter afterwards.这意味着在每次屏幕更新时,它必须重新绘制 600 个圆圈并在之后应用模糊滤镜。

The usual approach is a little different.通常的方法有点不同。 Initially you create a master texture with a blurred circle.最初,您创建一个带有模糊圆圈的主纹理。 This texture can then be re-used and drawn onto the canvas using the drawImage() method.然后可以使用drawImage()方法重新使用此纹理并将其绘制到画布上。 To vary the size of the circles there is no radius anymore though.为了改变圆的大小,不再有radius了。 We can get the same effect by using a scale instead.我们可以通过使用scale来获得相同的效果。

Here's an example:这是一个例子:

 var context = document.getElementById('stage').getContext('2d'); var radian = Math.PI / 180; var x = 40; var y = 40; var r = 20; var colorPoints = []; var frames = 50; var currentFrame = 0; var toggle = false; var iconsLoaded = false; var texture = document.createElement("canvas"); var textureContext = texture.getContext("2d"); texture.width = 80; texture.height = 80; textureContext.filter = 'blur(5px)'; textureContext.fillStyle = '#B835FF'; textureContext.arc(texture.width / 2, texture.height / 2, 25, 0 * radian, 360 * radian, false); textureContext.fill(); textureContext.closePath(); var drawMultipleCurves = function(ctx) { if (!iconsLoaded) { for (let i = 0; i < 600; i++) { colorPoints.push({ x: Math.floor((Math.random() * 700) + 0), xMove: Math.floor((Math.random() * 2) + 0), yMove: Math.floor((Math.random() * 2) + 0), y: Math.floor((Math.random() * 700) + 0), scale: 0.2 + Math.random() * 0.8 }); iconsLoaded = true; } } else { for (let i = 0; i < colorPoints.length; i++) { if (frames === currentFrame) { toggle = !toggle; currentFrame = 0; } if (!toggle) { colorPoints[i].xMove === 1 ? colorPoints[i].x = colorPoints[i].x + 5 : colorPoints[i].x = colorPoints[i].x - 5; colorPoints[i].yMove === 1 ? colorPoints[i].y = colorPoints[i].y + 5 : colorPoints[i].y = colorPoints[i].y - 5; } else { colorPoints[i].xMove === 1 ? colorPoints[i].x = colorPoints[i].x - 5 : colorPoints[i].x = colorPoints[i].x + 5; colorPoints[i].yMove === 1 ? colorPoints[i].y = colorPoints[i].y - 5 : colorPoints[i].y = colorPoints[i].y + 5; } ctx.drawImage(texture, colorPoints[i].x, colorPoints[i].y, texture.width * colorPoints[i].scale, texture.height * colorPoints[i].scale); currentFrame = currentFrame + 1; } } } var animate = function() { setTimeout(() => { context.clearRect(0, 0, 400, 400); context.beginPath(); drawMultipleCurves(context); context.fill(); requestAnimationFrame(animate) }) } requestAnimationFrame(animate)
 <canvas id="stage" width="400" height="400"> <p>Your browser doesn't support canvas.</p> </canvas>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM