html鼠标点击出现小球

html鼠标点击出现小球,第1张

html绘制小球并跟随鼠标移动,Canvas跟随鼠标炫彩小球的实现

黄利好

转载

关注

0点赞·318人阅读

跟随鼠标炫彩小球

canvas没有让我失望,真的很有意思

实现效果

超级炫酷

实现原理

创建小球

给小球添加随机颜色,随机半径

鼠标移动通过实例化,新增小球

通过调用给原型新增的方法,来实现小球的动画效果

通过定时器不断地更新画布

实现过程

创建小球

通过创建函数收纳小球所有的样式,再通过实例化函数,将鼠标当前的位置传递给Ball函数,让通过实例化创建出来的小球,最后将创建出来的小球存入数组中,数组中以对象形式存放着每个小球的属性和属性值

function Ball(x, y, r) {

this.x = x

this.y = y

this.r = r

this.color = getRandom()//随机生成颜色

this.dx = parseInt(Math.random() * 10) - 5//生成随机移动的位置

this.dy = parseInt(Math.random() * 10) - 5//`-5`是让小球能向四周随机移动

ballArr.push(this)//添加小球

}

//监听鼠标移动事件

canvas.addEventListener('mousemove', function (e) {

new Ball(e.offsetX, e.offsetY, parseInt(Math.random() * 20))

/*实例化Ball为Ball对象通过__proto__来调用原型的方法*/

})

生成随机颜色

对于color这个属性,可以通过6位16进制的值来表示一种颜色

因此,可以通过随机产生一个6位的16进制数来做为随机颜色

将0到f这16个数存入数组中,通过随机生成6个0到16的索引值,这样就能通过数组的索引号随机的获取6个到0到f中的数了

split的作用是:以括号内的参数为标志符来分割字符串,返回数组

//设置随机颜色

function getRandom() {

var allType = '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f'//16进制颜色

var allTypeArr = allType.split(',')//通过','分割为数组

var color = '#'

for (var i = 0i <6i++) {

//随机生成一个0-16的数

var random = parseInt(Math.random() * allTypeArr.length)

color += allTypeArr[random]

}

return color//返回随机生成的颜色

}

渲染小球

给函数的原型链中添加render方法,让每一个通过Ball函数实例化出来的对象,带有这些方法

这个函数的作用是,通过Ball的参数生成一个圆形,在实例化的时候,会生成一个对象,这个对象里就存放的x,y,r这些值

Ball.prototype.render = function () {

ctx.beginPath()//路径开始

ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, false)//画圆,位置,半径

ctx.fillStyle = this.color//颜色

ctx.fill()//填充

}

更新小球信息

因为生成的小球x,y,r是固定的,所以小球的位置也是固定的,不会改变

因此需要通过改变每个小球的位置和半径让小球动起来,当小球的半径小于0时,调用remove方法将小球从数组中删除

/* 更新小球位置和半径 小于0时清除 */

Ball.prototype.update = function () {

this.x += this.dx//x改变

this.y += this.dy//y改变

this.r -= 0.1//半径减小

if (this.r <0) {

this.remove()//调用添加的remove方法

}

}

删除小球

这是上面调用的remove方法,当this也就是当前小球半径小于0时i,遍历整个数组,找到这个this,也就是”这个小球“,通过调用数组中的方法,删除掉数组的这个元素

splice(index,num) 方法可删除从 index 处开始删除num个元素

Ball.prototype.remove = function () {

for (var i = 0i <ballArr.lengthi++) {

if (ballArr[i] == this) {

ballArr.splice(i, 1)//找到这个小于0 的元素,删除

下面详细解释都在源码中:

<!doctype html><html><head><meta charset="utf-8"><title>无标题文档</title><style>#canvas{ background:#eee}</style></head><body><canvas id='canvas' width="500" height='500'></canvas><script>window.onload=function(){ var canvas = document.getElementById('canvas') var context = canvas.getContext('2d') //平移,主要是将坐标轴平移到中间,为了画圆定位方便 context.translate(250,250) //定义焦距 var fos = 300 //存放小球的数组 var arr = [] for(var i = 0 i <8 i++){ var arcObj = { //半径,用随机数目的是让每个小球大小不一 r:10+5*Math.random(), //起始X坐标 x:-200+i*30, //起始Y坐标 y:-100+200*Math.random(), //起始Z坐标,这里需要理解,我们要构造的是一个三维立体小球的运动 //则X,Y轴不能表达空间感,你需要充分的想象Z坐标轴是顺着你的眼睛的就是Z轴 z:i*10, //小球的运行速度 speed:20 } arr.push(arcObj) } setInterval(function(){ //清除画布,每次画之前先将上次的清除掉.然后绘出本次的,就可以形成动画效果. context.clearRect(-250,-250,500,500) //将arr排序,sort()的参数则是作为一种比较规则 var newArr = arr.sort(function (a,b){ return a.z >b.z }) //循环绘出刚才定义的几个小球 for(var i = 0 i <newArr.length i++){ //z轴的变化,每次变化都是 速度*时间+z = z由于speed未定义单位,则时间可忽略 arr[i].z += arr[i].speed //让小球来回d跳 if(arr[i].z >600 || arr[i].z <-50){ arr[i].speed *= -1 } //这里的缩放比例,一定要注意,你要想象你眼前有个球垂直从远处飞来,逐渐变大的过程,Z轴不断增加.焦距就想象成从最初你到球的距离,通过运动后,现在到球的距离和焦距就可以形成缩放比例. var scales = fos/(fos+arr[i].z) var x1 = arr[i].x*scales var y1 = arr[i].y*scales //保存之前的context绘图环境,即后续可以用context.restore方法可以恢复, //目的是让下面的context变化不影响其他的画图样式. context.save() //平移X,Y 也可以不用平移X,Y只要在下面的画圆中定义相应的X,Y也能达到相同的目的 context.translate(x1,y1) //将坐标轴缩放,目的是让小球的大小发生视觉上的变化. context.scale(scales,scales) context.beginPath() //定义放射性颜色渐变 var colorObj = context.createRadialGradient(0,0,0,0,0,arr[i].r) colorObj.addColorStop(0,'#cbc0f3') colorObj.addColorStop(1,'#06198b') context.fillStyle=colorObj context.arc(0,0,arr[i].r,0,Math.PI*2) context.fill() context.restore() } },50)}</script></body></html>

主要要理解焦距的概念,实际开发过程中,可能X轴,Y轴都有小球的运动速度分量,那才能在运动的过程中转弯,撞墙等特效.

<html>

<head>

<title>

大球吃小球by大奔

</title>

<script type="text/javascript" src="src/jscex.js"></script>

<script type="text/javascript" src="src/jscex-parser.js"></script>

<script type="text/javascript" src="src/jscex-jit.js"></script>

<script type="text/javascript" src="src/jscex-builderbase.js"></script>

<script type="text/javascript" src="src/jscex-async.js"></script>

<script type="text/javascript" src="src/jscex-async-powerpack.js"></script>

</head>

<body>

<canvas id="myCanvas" width="480" height="300" style="border:1px solid #c3c3c3">

你的浏览器改换了

</canvas>

<script type="text/javascript">

var d=document.getElementByIdx_x("myCanvas")

var cxt=d.getContext("2d")

var balls=[]

//这里为了获得随机数的向量

function getRandom(a,b){

return (a+Math.floor(Math.random()*(b-a+1)))

}

//这里对向量进行赋值

var Vector2=function(a,b){

this.x=a||0

this.y=b||0

}

//这里需要注意,对象的默认方法在这里写不会管用。例如sub

Vector2.prototype={//写对象的构造函数

constructor:Vector2,

multiplyScalar:function(s){

this.x*=s

this.y*=s

return this

},

divideScalar:function(s){

if(s){

this.x/=s

this.y/=s

}else{

this.set(0,0)

}

return this

},

dot:function(v){

return this.x*v.x+this.y*v.y//即两个向量相乘

},

lengthSq:function(){

return this.x*this.x+this.y*this.y

},

length:function(){

return Math.sqrt(this.lengthSq())

},

normalize:function(){

//这里得到的是单位向量,按照google的定义,单位的向量是,//(a,b)则a*a+b*b=1

return this.divideScalar(this.length())

},

reflectionSelf:function(v){

//这里得到的是反射向量。公式参考这个网址。

//blog.physwf.com/?p=42

var nv=v.normalize()

this.sub(nv.multiplyScalar(2*this.dot(nv)))

},

distanceToSquared:function(v){//求出两点之间的距离

var dx=this.x-v.x,

dy=this.y-v.y

return dx*dx+dy*dy

}

}

Vector2.sub=function(v1,v2){//这里重写sub方法

return new Vector2(v1.x-v2.x,v1.y-v2.y)

}

for(var i=0i<40i++){//初始化40个小球

var ball={

position:new Vector2(getRandom(20,600),getRandom(20,600)),

r:getRandom(6,20),

speed:new Vector2(getRandom(-200,200),getRandom(-200,200)),

mass:1,//这是小球的质量

restitution:1//这是d性系数

}

balls.push(ball)

}

var filterBalls=[]

for(var i=0i<balls.lengthi++){

var overlapCount=0

for(var j=i+1j<balls.lengthj++){//两个两个比较防止重复,而且初始化的位置不能重 //叠,否则符合碰撞的条件。去掉这个判断以后,效果不太显著,可以多放些球试试。

var distance=balls[i].position.distanceToSquared(balls[j].position)

var l=balls[i].r+balls[j].r

if(distance<=(l*l)){

overlapCount++

}

}

if(overlapCount===0){

filterBalls.push(balls[i])

}

}

balls=filterBalls//这里可以去掉试试。

cxt.fillStyle="#030303"

cxt.fillRect(0,0,d.width,d.height)

function init(){

cxt.fillStyle="#fff"

for(i in balls){

cxt.beginPath()

cxt.arc(balls[i].position.x,balls[i].position.y,balls[i].r,0,Math.PI*2,true)

cxt.closePath()

cxt.fill()

}

}

init()

var cyc=20

var moveAsync2=eval_r(Jscex.compile("async",function(){

var tag=0

while(true){

try{

cxt.fillStyle="rgba(0,0,0,3)"

cxt.fillRect(0,0,d.width,d.height)

cxt.fillStyle="#fff"

for(var i=0i<balls.lengthi++){

//这里是为了两个小球比较会重复所以,每次比较都是i与i+1//开始相比较

for(var j=i+1j<balls.lengthj++){

collisionSolver(balls[i],balls[j])

}

}

for(i in balls){

cxt.beginPath()

cxt.arc(balls[i].position.x,balls[i].position.y,balls[i].r,0,Math.PI*2,true)

cxt.closePath()

cxt.fill()

if(balls[i].r+balls[i].position.x>d.width){

//如果小球x轴跑出了画布的范围

balls[i].position.x=d.width-balls[i].r

//小球的位置返回到画布的边缘位置

balls[i].speed.x*=-1

//同时x轴的方向变为反方向

}if(balls[i].position.x<balls[i].r){

//如果小球的x坐标小于小球的半径。肯定画不成完整的圆了,所以要归位

balls[i].position.x=balls[i].r

balls[i].speed.x*=-1 }if(balls[i].r+balls[i].position.y>d.height){//同理y轴

balls[i].position.y=d.height-balls[i].r

balls[i].speed.y*=-1

}

if(balls[i].position.y<balls[i].r){

balls[i].position.y=balls[i].r

balls[i].speed.y*=-1

} balls[i].position.x+=balls[i].speed.x*cyc/1000

//小球的x轴不断按照速度增大

balls[i].position.y+=balls[i].speed.y*cyc/1000

}

}catch(e){

alert(e)

}

$await(Jscex.Async.sleep(cyc))

}

}))

function collisionSolver(bodyA,bodyB){//判断小球发生碰撞的时候的变化。

var vB=bodyB.speed

var vA=bodyA.speed

var l=bodyA.r+bodyB.r

var distSqr=bodyA.position.distanceToSquared(bodyB.position)

var isTouching=distSqr<=l*l? true:false

//判断两圆心之间的距离如果小于两半径之和的平方。则为true

var normal=Vector2.sub(bodyB.position,bodyA.position).normalize()

//请看上面的解释,所以得到的是B相对于A的单位向量。

var ratio=bodyA.r/l//这是一个比例

var contactPoint=new Vector2()

//根据平行线切割的三角形,两边的边的比例相等,

contactPoint.x=bodyA.position.x+(bodyB.position.x-bodyA.position.x)*ratio

contactPoint.y=bodyA.position.y+(bodyB.position.y-bodyA.position.y)*ratio

var rA=Vector2.sub(contactPoint,bodyA.position)

//这两个地方没有找到是哪里用到的?????

var rB=Vector2.sub(contactPoint,bodyB.position)

var vrn=Vector2.sub(vA,vB).dot(normal)

//这里得到的是Va相对于vB的速度向量与两球的圆心的单位向量相乘。

///a*b=|a|*|b|*cos@.所以如果vrn大于零,则夹角小于90度。

if(isTouching&&vrn>0){

//这里是冲量公式的一个部分

var normalMass=1/(1/bodyA.mass+1/bodyB.mass)

var restitution=(bodyA.restitution+bodyB.restitution)/2

var normalImpulse=-normalMass*vrn*(1+restitution)

bodyA.speed.x+=normalImpulse*normal.x/bodyA.mass

//这里总之是一个大球一个小球,所以速度一个增大一个减小

bodyA.speed.y+=normalImpulse*normal.y/bodyA.mass

bodyB.speed.x-=normalImpulse*normal.x/bodyB.mass

bodyB.speed.y-=normalImpulse*normal.y/bodyB.mass

}

}

moveAsync2().start()

</script>

</body>

</html>


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/7666347.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-08
下一篇 2023-04-08

发表评论

登录后才能评论

评论列表(0条)

保存