画布:动画一个简单的星场 [英] Canvas: animating a simple star field

查看:107
本文介绍了画布:动画一个简单的星场的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Canvas比较陌生。找到我的脚我正在创建一个简单的街机游戏。



我的问题是关于CPU性能/效率。



<我正在黑色背景上创建100个随机定位的白点作为星星。在每个requestAnimationFrame上,星星向左移动一个像素,当它们到达最左侧时,该列像素位于屏幕的最右侧。



使用requestAnimationFrame我正在调用以下函数:

  bgAnimateId = requestAnimationFrame(scrollBg); 

函数scrollBg(){
var imgData = ctx.getImageData(0,0,1,canvas.height);
var areaToMoveLeft = ctx.getImageData(1,0,canvas.width-1,canvas.height);
ctx.putImageData(areaToMoveLeft,0,0);
ctx.putImageData(imgData,canvas.width-1,0);
bgAnimateId = requestAnimationFrame(scrollBg);
}

我担心的是 - 创建100个小画布元素会更好(或者100 divs)和动画,或者更好地利用我上面使用过的像素方法。



非常感谢您的帮助/指导:-)

解决方案

事实证明 context.getImageData context.putImageData 执行起来非常昂贵并且有100幅画布太多。



所以这是一个创建高效平移星空的计划:



使用 context.drawImage 非常高效,执行起来并不是很昂贵。



以下是如何在画布上绘制随机星空,然后将该画布保存为图像:

  //在画布上绘制一个随机星空
bkCtx.beginPath();
bkCtx.fillStyle =darkblue;
bkCtx.rect(0,0,background.width,background.height);
bkCtx.fill();
bkCtx.beginPath();
for(var n = 0; n< 100; n ++){
var x = parseInt(Math.random()* canvas.width);
var y = parseInt(Math.random()* canvas.height);
var radius = Math.random()* 3;
bkCtx.arc(x,y,radius,0,Math.PI * 2,false);
bkCtx.closePath();
}
bkCtx.fillStyle =white;
bkCtx.fill();

//使用星空画布创建一个新图像
var img = document.createElement(img);
img.src = background.toDataURL();

您将有2种绘图:


  1. 明星的平移背景

  2. 将绘制游戏对象的前景。

因此,创建2个相互对齐的画布。后面的画布用于游戏对象的星星和前画布。



这是一个平移星域运动图像的背景画布:





这是你的游戏对象去的前景画布 - 看我俗气的火箭





这些是用于创建背景/前景组合的2个画布:





这是用于堆叠2幅画布的Html + CSS:

 < div id =容器> 
< canvas id =backgroundclass =subcanvswidth = 300;高度= 300;>< /画布>
< canvas id =canvasclass =subcanvswidth = 300;高度= 300;>< /画布>
< / div>

#container {
position:relative;
边框:1px纯蓝色;
宽度:300px;
身高:300px;
}
.subcanvs {
position:absolute;
}

以下是如何使用星空图像在背景画布上创建平移星空:

  var fps = 60; 
var offsetLeft = 0;
panStars();

函数panStars(){

//增加左偏移量
offsetLeft + = 1;
if(offsetLeft> backImage.width){offsetLeft = 0; }

//绘制星空图像和
//再次绘制它以填充第一张图像右侧的空白空间
bkCtx.clearRect(0,0,background .WIDTH,background.height);
bkCtx.drawImage(backImage,-offsetLeft,0);
bkCtx.drawImage(backImage,backImage.width-offsetLeft,0);

setTimeout(function(){
requestAnimationFrame(panStars);
},1000 / fps);
}

现在前面的画布用于所有游戏对象。



您的游戏效率高,效果好,有2张专用于自己目的的画布。



这是代码和小提琴: http://jsfiddle.net/m1erickson/5vfVb/

 <!doctype html> 
< html>
< head>
< link rel =stylesheettype =text / cssmedia =allhref =css / reset.css/> < ;! - reset css - >
< script type =text / javascriptsrc =http://code.jquery.com/jquery.min.js>< / script>

< style>
body {padding:20px;}
#container {
position:relative;
边框:1px纯蓝色;
宽度:300px;
身高:300px;
}
.subcanvs {
position:absolute;
}
< / style>

< script>
$(function(){

// Paul Irish's great RAF shim
window.requestAnimFrame =(function(callback){
return window.requestAnimationFrame || window .webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback,1000/60);
};
})();

var canvas = document.getElementById(canvas);
var ctx = canvas.getContext(2d);
var background = document.getElementById(background);
var bkCtx = background.getContext(2d);

//创建随机星星的图像
var backImage = RandomStarsImage ();

//在前画布上绘制
ctx.beginPath();
ctx.fillStyle =red;
ctx.rect(75,100,100, 50);
ctx.arc(175,125,25,0,Math.PI * 2,false);
ctx.closePath();
ctx.fill();

//开始淘选r andom在背景画布上的星星图像
var fps = 60;
var offsetLeft = 0;
panStars();

函数panStars(){

//增加左偏移量
offsetLeft + = 1;
if(offsetLeft> backImage.width){offsetLeft = 0; }

//绘制星空图像并再次绘制
//填充第一张图像右侧的空白区域
bkCtx.clearRect(0,0,background .WIDTH,background.height);
bkCtx.drawImage(backImage,-offsetLeft,0);
bkCtx.drawImage(backImage,backImage.width-offsetLeft,0);

setTimeout(function(){
requestAnimFrame(panStars);
},1000 / fps);
}

函数RandomStarsImage(){

//在画布上绘制一个随机星空
bkCtx.beginPath();
bkCtx.fillStyle =darkblue;
bkCtx.rect(0,0,background.width,background.height);
bkCtx.fill();
bkCtx.beginPath();
for(var n = 0; n< 100; n ++){
var x = parseInt(Math.random()* canvas.width);
var y = parseInt(Math.random()* canvas.height);
var radius = Math.random()* 3;
bkCtx.arc(x,y,radius,0,Math.PI * 2,false);
bkCtx.closePath();
}
bkCtx.fillStyle =white;
bkCtx.fill();

//使用星空画布创建一个新图像
var img = document.createElement(img);
img.src = background.toDataURL();
return(img);
}

}); // end $(function(){});
< / script>

< / head>

< body>
< div id =container>
< canvas id =backgroundclass =subcanvswidth = 300;高度= 300;>< /画布>
< canvas id =canvasclass =subcanvswidth = 300;高度= 300;>< /画布>
< / div>
< / body>
< / html>


I'm relatively new to Canvas. In finding my feet I'm creating a simple arcade game.

My question is regarding CPU performance / efficiency.

I'm creating 100 randomly positioned white dots as stars on a black background. On each requestAnimationFrame, the stars move one pixel to the left and as they get to the extreme left that column of pixels is placed on the extreme right of the screen.

Using requestAnimationFrame I'm calling the following function:

bgAnimateId = requestAnimationFrame( scrollBg );

function scrollBg() {
    var imgData = ctx.getImageData( 0, 0, 1, canvas.height );
    var areaToMoveLeft = ctx.getImageData( 1, 0, canvas.width-1, canvas.height );
    ctx.putImageData( areaToMoveLeft, 0, 0 );
    ctx.putImageData( imgData, canvas.width-1, 0 );
    bgAnimateId = requestAnimationFrame( scrollBg );
}

My concern is - would it be better to create 100 small canvas elements (or 100 divs) and animate those, or is it better to utilise the pixel methods that I've used above.

Many thanks for your help / guidance in advance :-)

解决方案

It turns out that context.getImageData and context.putImageData are very expensive to perform and having 100 canvases is too many.

So here’s a plan for creating an efficient panning starfield:

Using context.drawImage is very efficient and not very expensive to perform.

Here’s how to draw a random starfield on a canvas and then save that canvas as an image:

// draw a random starfield on the canvas
bkCtx.beginPath();
bkCtx.fillStyle="darkblue";
bkCtx.rect(0,0,background.width,background.height);
bkCtx.fill();
bkCtx.beginPath();
for(var n=0;n<100;n++){
    var x=parseInt(Math.random()*canvas.width);
    var y=parseInt(Math.random()*canvas.height);
    var radius=Math.random()*3;
    bkCtx.arc(x,y,radius,0,Math.PI*2,false);
    bkCtx.closePath();
}
bkCtx.fillStyle="white";
bkCtx.fill();

// create an new image using the starfield canvas
var img=document.createElement("img");
img.src=background.toDataURL();

You will have 2 kinds of drawing going on:

  1. A panning background of stars
  2. A foreground where your game objects will be drawn.

So create 2 canvases aligned on top of each other. The back canvas is for the stars and the front canvas for you game objects.

This is the background canvas that pans the moving image of the starfield:

This is the foreground canvas where your game objects go -- see my cheesy "rocket"

These are the 2 canvases stacked to create a background/foreground combination:

Here is the Html+CSS to stack the 2 canvases:

<div id="container">
  <canvas id="background" class="subcanvs" width=300; height=300;></canvas>
  <canvas id="canvas" class="subcanvs" width=300; height=300;></canvas>
</div>

#container{
  position:relative;
  border:1px solid blue;
  width:300px;
  height:300px;
}
.subcanvs{
  position:absolute;
}

Here’s how to use the starfield image to create a panning starfield on the background canvas:

var fps = 60;
var offsetLeft=0;
panStars();

function panStars() {

    // increase the left offset
    offsetLeft+=1;
    if(offsetLeft>backImage.width){ offsetLeft=0; }

    // draw the starfield image and
    // draw it again to fill the empty space on the right of the first image
    bkCtx.clearRect(0,0,background.width,background.height);
    bkCtx.drawImage(backImage,-offsetLeft,0);
    bkCtx.drawImage(backImage,backImage.width-offsetLeft,0);

    setTimeout(function() {
        requestAnimationFrame(panStars);
    }, 1000 / fps);
}

Now the front canvas is used for all your game objects.

Your game is efficient and performant with 2 canvases dedicated to their own purposes.

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/5vfVb/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
body{padding:20px;}
#container{
  position:relative;
  border:1px solid blue;
  width:300px;
  height:300px;
}
.subcanvs{
  position:absolute;
}
</style>

<script>
$(function(){

    // Paul Irish's great RAF shim
    window.requestAnimFrame = (function(callback) {
      return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
      function(callback) {
        window.setTimeout(callback, 1000 / 60);
      };
    })();

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var background=document.getElementById("background");
    var bkCtx=background.getContext("2d");

    // create an image of random stars
    var backImage=RandomStarsImage();

    // draw on the front canvas
    ctx.beginPath();
    ctx.fillStyle="red";
    ctx.rect(75,100,100,50);
    ctx.arc(175,125,25,0,Math.PI*2,false);
    ctx.closePath();
    ctx.fill();

    // start panning the random stars image across the background canvas
    var fps = 60;
    var offsetLeft=0;
    panStars();

    function panStars() {

        // increase the left offset
        offsetLeft+=1;
        if(offsetLeft>backImage.width){ offsetLeft=0; }

        // draw the starfield image and draw it again 
        // to fill the empty space on the right of the first image
        bkCtx.clearRect(0,0,background.width,background.height);
        bkCtx.drawImage(backImage,-offsetLeft,0);
        bkCtx.drawImage(backImage,backImage.width-offsetLeft,0);

        setTimeout(function() {
            requestAnimFrame(panStars);
        }, 1000 / fps);
    }

    function RandomStarsImage(){

        // draw a random starfield on the canvas
        bkCtx.beginPath();
        bkCtx.fillStyle="darkblue";
        bkCtx.rect(0,0,background.width,background.height);
        bkCtx.fill();
        bkCtx.beginPath();
        for(var n=0;n<100;n++){
            var x=parseInt(Math.random()*canvas.width);
            var y=parseInt(Math.random()*canvas.height);
            var radius=Math.random()*3;
            bkCtx.arc(x,y,radius,0,Math.PI*2,false);
            bkCtx.closePath();
        }
        bkCtx.fillStyle="white";
        bkCtx.fill();

        // create an new image using the starfield canvas
        var img=document.createElement("img");
        img.src=background.toDataURL();
        return(img);
    }

}); // end $(function(){});
</script>

</head>

<body>
    <div id="container">
      <canvas id="background" class="subcanvs" width=300; height=300;></canvas>
      <canvas id="canvas" class="subcanvs" width=300; height=300;></canvas>
    </div>
</body>
</html>

这篇关于画布:动画一个简单的星场的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆