canvas雪花下落怎么制作重力小球下落

简单的运动学,用canvas写弹力球
W·Axes
发布时间: 10:30:11
  声明:本文为原创文章,如需转载,请注明来源,谢谢!
  跟之前的随笔一样,因为本人仍是菜鸟一只,所以用到的技术比较简单,不适合大神观看。。。。。。
  学canvas学了有一个多礼拜了,觉得canvas真心好玩。学canvas的人想法估计都跟我差不多,抱着写游戏的态度去学canvas的。所以运动学啊、碰撞检测啊、一些简单的算法神马的是基础啊。以前没做过游戏的我学起来还真心吃力。今天就来说下用canvas写个最简单的弹力球游戏,就运用了最简单的重力作用以及碰撞检测。
  先上DEMO:&(鼠标点击canvas里的空白区域会给与小球新速度)
  【创建小球对象】
  第一步就是先创建一个小球对象,写好小球的构造函数:
var Ball = function(x , y , r , color){
this.oldx =
this.oldy =
this.vx = 0;
this.vy = 0;this.radius =
this.color =
  小球属性很简单,xy是小球的坐标,vx和vy是小球的初始水平速度和初始垂直速度。radius就是小球的半径,color是小球颜色(为了区分不同球),oldx和oldy是记录小球的上一帧的位置,后期球与球之间碰撞后用于位置修正(后面其实没用上,位置修正直接计算了,如果用oldx来设置很不严谨,不过记录一下,难免会用得到)。
  小球属性写好后,就在小球原型中写小球的动作了:
Ball.prototype = {
paint:function(){
ctx.save();
ctx.beginPath();
ctx.arc(this.x , this.y , this.radius , 0 , Math.PI*2);
ctx.fillStyle=this.
ctx.fill();
ctx.restore();
this.moving = false;
run:function(t){
if(!this.candrod) {
this.paint();
this.oldx = this.x;
this.oldy = this.y;
if(Math.abs(this.vx) & 0.01){
this.vx = 0;
else this.vx += this.vx&0? -mocali*t : mocali*t;
this.vy = this.vy + g *
this.x += t * this.vx *
this.y += t * this.vy *
if(this.y & canvas.height - ballRadius || this.y & ballRadius){
this.y = this.y & ballRadius ? ballRadius : (canvas.height - ballRadius);
this.vy = -this.vy*collarg
if(this.x & canvas.width - ballRadius || this.x & ballRadius){
this.x = this.x & ballRadius ? ballRadius : (canvas.width - ballRadius);
this.derectionX = !this.derectionX;
this.vx = -this.vx*
this.paint();
  小球的动作方法也很简单,就两个,第一个方法是把自己画出来,第二个方法就是控制小球的运动。t是当前帧与上一帧的时间差。用于计算小球的速度的增量从而得出小球的位移增量,从而计算出小球的新位置并且将小球重绘。得出新位置的同时判断小球的新位置有无超出墙壁,如果超出则进行速度修正让小球反弹。
  第二个方法里的一些常量ballRadius =30, g = 9.8 , mocali = 0.5,balls = [],collarg = 0.8,pxpm = canvas.width/20;&意思很明显:ballradius是球半径,g是重力加速度,mocali是空气阻力引起的水平方向的减速度,balls是一个用于存放小球对象的数组,collarg是弹力系数。pxpm是像素与米之间的映射,把画布当成是20米宽的区域。
  【碰撞检测】
  创建好小球对象后,就开始写碰撞了,小球与小球之间的碰撞:
function collision(){
for(var i=0;i&balls.i++){
for(var j=0;j&balls.j++){
var b1 = balls[i],b2 = balls[j];
if(b1 !== b2){
var rc = Math.sqrt(Math.pow(b1.x - b2.x , 2) + Math.pow(b1.y - b2.y , 2));
if(Math.ceil(rc) & (b1.radius + b2.radius)){
//获得碰撞后速度的增量
var ax = ((b1.vx - b2.vx)*Math.pow((b1.x - b2.x) , 2) + (b1.vy - b2.vy)*(b1.x - b2.x)*(b1.y - b2.y))/Math.pow(rc , 2)
var ay = ((b1.vy - b2.vy)*Math.pow((b1.y - b2.y) , 2) + (b1.vx - b2.vx)*(b1.x - b2.x)+(b1.y - b2.y))/Math.pow(rc , 2)
//给与小球新的速度
b1.vx = (b1.vx-ax)*
b1.vy = (b1.vy-ay)*
b2.vx = (b2.vx+ax)*
b2.vy = (b2.vy+ay)*
//获取两球斜切位置并且强制扭转
var clength = ((b1.radius+b2.radius)-rc)/2;
var cx = clength * (b1.x-b2.x)/
var cy = clength * (b1.y-b2.y)/
b1.x = b1.x+
b1.y = b1.y+
b2.x = b2.x-
b2.y = b2.y-
  每一帧都进行小球之间碰撞的判断,如果两个小球球心距离小于两球半径之和,则证明两个小球发生了碰撞。然后进行计算两个小球碰撞之后的速度变化量。ax和ay就是速度变化量。
  后面长长的公式就是这个:&,具体原理我就不说了,想了解原理就直接戳 &。 下面那段就是防止小球重复碰撞检测导致无法正常反弹,所以计算两小球的球心距离,然后算出两个小球的斜切位置,并且将两个小球的位置进行更正。
  【运动动画】
  最后一步:
canvas.onclick = function(event){
event = event || window.
var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - canvas.offsetL
var y= event.clientY + document.body.scrollTop + document.documentElement.scrollTop - canvas.offsetT
balls.forEach(function(){
this.vx = (x - this.x)/20; //初速度 m/s
this.vy = (y - this.y)/20;
function animate(){
ctx.save();
ctx.fillStyle = "rgba(255,255,255,0.2)";
ctx.fillRect(0,0,canvas.width,canvas.height)
ctx.restore();
// ctx.clearRect(0,0,canvas.width,canvas.height)
var t1 = new Date();
var t = (t1 - t0)/1000;
collision();
balls.forEach(function(){
this.run(t);
if("requestAnimationFrame" in window){
requestAnimationFrame(animate);
else if("webkitRequestAnimationFrame" in window){
webkitRequestAnimationFrame(animate);
else if("msRequestAnimationFrame" in window){
msRequestAnimationFrame(animate);
else if("mozRequestAnimationFrame" in window){
mozRequestAnimationFrame(animate);
通过点击画布的位置来给于小球初速度,然后animate就是动画的每一帧运行的方法。上面的&ctx.fillStyle = "rgba(255,255,255,0.2)";&ctx.fillRect(0,0,canvas.width,canvas.height)是给小球添加虚影,我觉得这样会更好看,如果觉得不喜欢,就直接用clearRect清除就行了。然后就是计算每一帧的时间差,然后对小球数组里小球数组进行遍历重绘。然后再加入碰撞检测的collision方法。动画也就做完了。
  至此,就已经写完了,源码地址:
来源:/axes/p/3513343.htmlcanvas 简单的小球抛物线实现和简单的应用 - 大白的博客 - CSDN博客
canvas 简单的小球抛物线实现和简单的应用
canvas动画
使用canvas实现简单的小球下落
canvas可以实现一些有趣的绘画,可以实现规则或者不规则的图形。我们这里的小球就是一个通过canvas的方法arc画出的圆。我们可以使用storke和fil来实现圆的颜色和边框,如果你还想让圆在漂亮一些,使用createRadialGradient可以实现球颜色的渐变。
= context.createRadialGradient(x,y,5,x,y,20);
grad.addColorStop(0,'rgb(231, 74, 148)');
grad.addColorStop(0.5,'rgb(99, 75, 231)');
grad.addColorStop(1,'rgb(78, 228, 156)');
if(jiaflag!=1){
Curve(cxt,x,y);
cxt.beginPath();
cxt.arc(x,y,ball.r,0,2*Math.PI);
cxt.strokeStyle="#000000";
cxt.fillStyle=
样子大概就是这样,createRadialGradient的各个参数的意思是
context.createRadialGradient(x0,y0,r0,x1,y1,r1);
渐变的开始圆的 x 坐标
渐变的开始圆的 y 坐标
开始圆的半径
渐变的结束圆的 x 坐标
渐变的结束圆的 y 坐标
结束圆的半径
小球画好了,怎么样才能让小球动起来呢?其实很简单,所谓的动画其实质就是一帧一帧的静态元素组合起来,让人眼感觉像是动的一样。所以让小球动起来,你需要在页面让不停的画小球,而且你画的小球的位置要符合运动的规律,这样你的小球就动了起来。
首先,我们创建一个对象来存放小球的信息,圆心坐标、半径、角度等等。
var ball = {
小球已经存在了我们只需要通过小球的属性画出来就行了。这里我们使用一个方法实现,方便以后使用。
function drawball(cxt,x,y){
cxt.clearRect(0,0,context.canvas.width,context.canvas.height);
= context.createRadialGradient(x,y,5,x,y,20);
grad.addColorStop(0,'rgb(231, 74, 148)');
grad.addColorStop(0.5,'rgb(99, 75, 231)');
grad.addColorStop(1,'rgb(78, 228, 156)');
cxt.beginPath();
cxt.arc(x,y,ball.r,0,2*Math.PI);
cxt.strokeStyle="#000000";
cxt.fillStyle=
cxt.stroke();
cxt.fill();
cxt.closePath();
这样我们就通过函数的方法画出了小球,到时候我们只需要通过不停的调用这个方法就可以画出运动的球了。当然,每次我们只需要出现一个小球,所以需要用clearRect来清除画布。
小球运动的轨迹,我们就用一个平抛来实现。
平抛运动大家都很熟悉,你需要知道的是水平初速度和垂直初速度还有加速度。这里我们只是简单的例子,所以并不会完全按照实际生活中的重力加速度来计算。
function update(){//通过简单的运动原理更新小球位置
ball.x = ball.x + ball.vx
ball.y = ball.y + ball.vy
ball.vy = ball.vy + ball.g
if( ball.y & (600 - ball.r)){//下边框碰撞检测
ball.y = 600 - ball.r
ball.vy = -ball.vy*0.5
if( ball.x & (ball.r)){//左边框碰撞检测
ball.vx = -ball.vx
if( ball.y & (ball.r)){//上边框碰撞检测
ball.y = ball.r
ball.vy = -ball.vy*0.5
这里我只写了上、左、下的边框,右边的没写,所以小球是可以从右边滚出画布的。我们可以通过修改的vx、vy来决定是上抛和下抛、左抛和右抛。
小球的运动轨迹
大家可能玩过愤怒的小鸟,在小鸟发射的时候会有一条小鸟经过的轨迹,那么这些轨迹有事如何实现的呢?原理和我们小球运动是一样的。不一样的是我们需要清除画布确保只有一个小球,而轨迹则需要保留。那么既然每次都会清除画布重绘,怎么样才能保留运动轨迹的小球的?也很简单我们只需要定义一个数组来存放每次的运动位置,在小球重绘的时候,使用for循环把轨迹重绘就行了。
主要的原理就这些,我这里在画布上还加了一个椭圆 权当做一个篮框,当小球坐标在椭圆内的时候就相当于进了一个球。
浏览器兼容
我的热门文章在网页中模拟一些特殊的力学运动效果,如:物体摩擦、惯性或重力效果,我们可以通过使用的贝兹曲线来完成这些特效。
物体受重力的效果是一种非常复杂的动画过程,我们可以使用CSS来模拟这一效果。典型的重力效果是小球的弹跳效果。小球受重力和自身弹力的影响不停的上下弹跳。使用CSS制作这个效果其实并不复杂。小球的HTML结构使用一个空的&div&即可。
&div id="redball"&&/div&
给小球div一些基本样式:
#redball {
border-radius: 50%;
width: 20 height: 20
background-image: radial-gradient(ellipse farthest-corner at 25% 25%, #f00,#000);
border-radius: 50%;将div设置为圆形,然后通过渐变使它产生立体感。小球的宽度和高度都使用vw作为单位,1vw等于当前屏幕宽度的1%。(关于CSS的单位可以查看。)这样做是为了是使设计具有响应式效果。
然后,我们就可以使用CSS的帧动画来制作小球受重力运动的效果。注意代码中没有使用浏览器厂商的前缀。
@keyframes bounce {
from, to {
transform: translateY(30vh);
transform: translateY(15vh);
同样,translate的值使用了vh作为单位。1vh等于当前屏幕高度的1%。
通过vw和vh作为单位的最大好处是,不必使用@media queries也能在现代浏览器中做到响应式效果。
接下来在小球上调用帧动画。
#redball {
transform-origin:
animation: bounce 1.3s cubic-bezier(0.30, 2.40, 0.85, 2.50)
注意要记得修改小球的transform-origin,这使得小球动画可以很好的定位。另外在animation中使用了一个特殊的贝兹曲线函数,用以制作重力效果。
现在,小球已经可以上下弹跳了,但是整个动画效果还不十分完美。现实生活中,小球在弹跳时,受重力和引力的作用会在弹跳过程中被轻微的挤压。所以,我们也要模拟这一效果,可以通过 CSS transforms 的scale属性来完成它。
@keyframes bounce {
from, to {
transform: translateY(30vh) scaleY(.98);
transform: translateY(15vh) scaleY(1.02);
当然,我们不可能模拟得十全十美。小球会一直弹跳下去,并不会因为引力的作用而慢慢停止。但是这些问题都是可以解决的,希望大家自己开动脑筋,动手做一下!
本文版权属于jQuery之家,转载请注明出处:4124人阅读
java(124)
Android开发(31)
1、布局界面
xmlns:android=&/apk/res/android&
xmlns:tools=&/tools&
android:layout_width=&match_parent&
android:layout_height=&match_parent&
android:paddingBottom=&@dimen/activity_vertical_margin&
android:paddingLeft=&@dimen/activity_horizontal_margin&
android:paddingRight=&@dimen/activity_horizontal_margin&
android:paddingTop=&@dimen/activity_vertical_margin&
tools:context=&.GameActivity&
android:id=&@+id/gameview&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:id=&@+id/btn_left&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_alignLeft=&@+id/gameview&
android:layout_alignParentBottom=&true&
android:text=&@string/btn_text&
android:id=&@+id/btn_right&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_alignBottom=&@+id/gameview&
android:layout_centerHorizontal=&true&
android:layout_alignParentBottom=&true&
android:text=&@string/btn_right&
&RelativeLayout xmlns:android=&/apk/res/android&
xmlns:tools=&/tools&
android:layout_width=&match_parent&
android:layout_height=&match_parent&
android:paddingBottom=&@dimen/activity_vertical_margin&
android:paddingLeft=&@dimen/activity_horizontal_margin&
android:paddingRight=&@dimen/activity_horizontal_margin&
android:paddingTop=&@dimen/activity_vertical_margin&
tools:context=&.GameActivity& &
&www.csdn.net.tetris.view.GameView
android:id=&@+id/gameview&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&/&
android:id=&@+id/btn_left&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_alignLeft=&@+id/gameview&
android:layout_alignParentBottom=&true&
android:text=&@string/btn_text& /&
android:id=&@+id/btn_right&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_alignBottom=&@+id/gameview&
android:layout_centerHorizontal=&true&
android:layout_alignParentBottom=&true&
android:text=&@string/btn_right& /&
&/RelativeLayout&
2、封装的一个绘制的图的类
package www.csdn.net.tetris.
import www.csdn.net.tetris.view.GameV
public class Block {
private int x=20,y=20;
public GameView gameV
public Block(GameView gameView){
this.gameView=gameV
} public void moveLeft(){
x-=10; gameView.invalidate(); } public void moveRight(){
x+=10; gameView.invalidate(); } public void downLoad(){
y+=10; gameView.invalidate();} public int getX() {
return } public void setX(int x) {
this.x = } public int getY() {
return } public void setY(int y) {
this.y = } }
package www.csdn.net.tetris.
import www.csdn.net.tetris.view.GameV
public class Block {
private int x=20,y=20;
public GameView gameV
public Block(GameView gameView){
this.gameView=gameV
//向左移动
public void moveLeft(){
gameView.invalidate();
//向右移动
public void moveRight(){
gameView.invalidate();
//下落方法
public void downLoad(){
gameView.invalidate();//重新绘制
public int getX() {
public void setX(int x) {
public int getY() {
public void setY(int y) {
3、创建一个画布和线程的操作
package www.csdn.net.tetris.
import android.content.C
import android.graphics.C
import android.graphics.P
import android.os.H
import android.os.M
import android.util.AttributeS
import android.view.V
import www.csdn.net.tetris.domain.B
public class GameView
extends View { public static B
public static
int dir=-1;
public static
final int DIRUP=1;
public static
final int DIRDOWN=2;
public static
final int DIRLEFT=3;
public static
final int DIRRIGHT=4;
public GameView(Context context,AttributeSet attrs) {
super(context); this.block=new Block(this);
handler=new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) { case DIRLEFT:
GameView.block.moveLeft(); break; case DIRRIGHT: GameView.block.moveRight(); break; default: GameView.block.downLoad(); break; } } }; new Thread (new Runnable(){
public void run() {
while(true){
try{ System.out.println(&子线程名称:::&+Thread.currentThread().getName());
Thread.sleep(1000);
handler.sendEmptyMessage(dir); }catch(Exception e){
e.printStackTrace(); } } } }).start(); } @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawARGB(255, 0,
Paint paint=new Paint();
paint.setARGB(255,
0); canvas.drawCircle(block.getX(), block.getY(), 10, paint);
package www.csdn.net.tetris.
import android.content.C
import android.graphics.C
import android.graphics.P
import android.os.H
import android.os.M
import android.util.AttributeS
import android.view.V
import www.csdn.net.tetris.domain.B
public class GameView extends View {
public static B
//定义方向
public static
int dir=-1;
//上下左右
public static final int DIRUP=1;
public static final int DIRDOWN=2;
public static final int DIRLEFT=3;
public static final int DIRRIGHT=4;
public GameView(Context context,AttributeSet attrs) {
super(context);
//创建俄罗斯方块对象
this.block=new Block(this);
handler=new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case DIRLEFT:
GameView.block.moveLeft();
case DIRRIGHT:
GameView.block.moveRight();
GameView.block.downLoad();
//创建线程
new Thread (new Runnable(){
public void run() {
while(true){
System.out.println(&子线程名称:::&+Thread.currentThread().getName());
//block.downLoad();
Thread.sleep(1000);
handler.sendEmptyMessage(dir);
}catch(Exception e){
e.printStackTrace();
}).start();
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置了画布的颜色
canvas.drawARGB(255, 0, 0, 255);
//设置一个画笔
Paint paint=new Paint();
paint.setARGB(255, 255, 0, 0);
canvas.drawCircle(block.getX(), block.getY(), 10, paint);
4、在MainActivity中的操作
package com.example.
import android.app.A
import android.os.B
import android.os.H
import android.os.M
import android.view.M
import android.view.V
import android.widget.B
import android.widget.T
import www.csdn.net.tetris.domain.B
import www.csdn.net.tetris.view.GameV
public class GameActivity
extends Activity { public Button btn_
public Button btn_
public Button btn_
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game); btn_start=(Button) findViewById(R.id.btn_start); btn_start.setOnClickListener(new MyOnClickListener());
System.out.println(&主线程:::&+Thread.currentThread().getName());
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu); return true;
} class MyOnClickListener
implements View.OnClickListener{ @Override public void onClick(View v) {
int id=v.getId(); switch (id) {
case R.id.btn_start: GameActivity.this.setContentView(R.layout.activity_layout);
btn_right=(Button) findViewById(R.id.btn_right); btn_left=(Button) findViewById(R.id.btn_left); btn_left.setOnClickListener(new MyOnClickListener());
btn_right.setOnClickListener(new MyOnClickListener());
break; case R.id.btn_left:
Toast.makeText(GameActivity.this,
&向左移动&, Toast.LENGTH_LONG).show();
GameView.block.moveLeft(); break; case R.id.btn_right: Toast.makeText(GameActivity.this,
&向右移动&, Toast.LENGTH_LONG).show();
GameView.block.moveRight(); break; default: break; } } } }
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:721860次
积分:8746
积分:8746
排名:第2274名
原创:188篇
转载:26篇
译文:11篇
评论:181条
阅读:14070
文章:18篇
阅读:73358
(2)(3)(6)(3)(1)(4)(5)(4)(27)(24)(23)(16)(5)(2)(6)(30)(12)(20)(1)(1)(4)(8)(6)(12)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 canvas多个小球碰撞 的文章

 

随机推荐