介绍
因为最近比较闲,就想着能不能自己做个h5游戏出来,所以先做个小东西试试手,既然要做游戏当然就要涉及到很多的运动轨迹以及碰撞后事件效果等问题,所以就做了这个小demo出来
原理
说起来原理其实也很简单的,想做一个黑洞吞噬效果的话首先我们至少需要两个物体
- 黑洞
这个的话比较简单,其实就是往中间放一个黑色的小球而已,可以根据我们需要稍微做一点样式上的优化
<style>
#blackhole {
width: 40px;
height: 40px;
background: radial-gradient(black, transparent, transparent);
/* radial-gradient是渐变色方法的一种
使用三个颜色作为渐变
可以直接使用transparent设置透明色
这样就可以得到一个带有散射效果的黑色小球 */
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
}
</style>
<div id="blackhole"></div>
喏,大概就长这个样子
- 小球
小球的话可能稍微复杂一些,我这里是通过鼠标点击然后在鼠标点击位置生成一个红色小球,然后这个小球会在创建好之后向着黑洞的位置移动,代码如下
<style>
.block {
width: 30px;
height: 30px;
position: absolute;
background: radial-gradient(red, transparent, transparent);
border-radius: 10px;
}
</style>
<body id="body">
</body>
<script>
/* 通过tagname直接获取body有点问题
所以选择添加id后通过byId方式获取dom元素 */
let body = document.getElementById('body')
body.addEventListener('click',e=>{
let a = document.createElement('div')
a.className = 'block'
body.appendChild(a)
}
</script>
这部分代码很好理解,就是在点击body时创建一个红色的小球然后添加进body,接下来的话再对小球的位置进行修正
body.addEventListener('click',e=>{
let a = document.createElement('div')
a.className = 'block'
//首先获取到鼠标点击时的坐标位置
let x = e.clientX
let y = e.clientY
/* 因为小球本身时带有高度和宽度的
所以在进行位置判断的时候需要先减去小球宽高的一半 */
let ballX = x - 10
let ballY = y - 10
a.style.left = ballX + 'px'
a.style.top = ballY + 'px'
body.appendChild(a)
}
现在小球已经有了,位置也对了,接下来就要让小球动起来了,小球运动的话就需要进行刷新了,也就是说我们需要用到 setInterval() 这个方法来进行小球位置的刷新,刷新频率的话一般30ms就够了,大家都知道运动距离等于运动速度乘以运动时间,运动速度可以理解为单位事件内运动的距离,现在刷新事件已经有了,也就是30ms 所以只需要再设定一个单位距离作为速度即可。
/* 在上边我把黑洞的位置设到了屏幕正中心
所以小球的运动轨迹应该是向着正中心位置偏移 */
let h = window.innerHeight
let w = window.innerWidth
/* 速度的生成建议通过下边这种方式来进行生成
这样生成的速度本身是带有正负的
所以就不需要判断点击时小球是在中心点之上还是之下了 */
let speedX = (x - w/2)/30
let speedY = (y - h/2)/30
body.appendChild(a)
a.style.left = ballX + 'px'
a.style.top = ballY + 'px'
let itv = setInterval(()=>{//每30ms刷新一次位置
ballY -= speedY
ballX -= speedX
a.style.left = ballX + 'px'
a.style.top = ballY + 'px'
},30)
现在小球的运动已经完成了,但是还有一个问题,小球在运动到黑洞位置时不会消失,还会一直运动直到飞出屏幕之外(因为body设置了overflow:hidden属性,所以看不到了,但其实还在),所以接下来需要做一个判断,在小球运动到黑洞位置时让它消失
/* 这几行代码建议放在点击事件之外
因为他们本身不需要频繁创建
可以节省开销 */
let b = document.getElementById('blackhole')
let bw = b.clientHeight
let bh = b.clientWidth
let bt = b.offsetTop
let bl = b.offsetLeft
/* 这里的判断条件看起来很复杂,稍微解释一下
因为小球本身是基于中心点运动的
而且因为小数点等问题的缘故也是无法直接让小球坐标等于中心点的
所以应该是在小球运动到某一区间值时进行操作
小球的left应该介于黑洞的左右边界之间,即
ballX < (bl + bw - 20) && ballX > (bl - 10)
top介于黑洞的上下边界之间
ballY < (bt + bh - 20) && ballY > (bt - 10) */
if((ballX < (bl + bw - 20) && ballX > (bl - 10))&&
(ballY < (bt + bh - 20) && ballY > (bt - 10))){
clearInterval(itv) //清除计时器
body.removeChild(a)
}
现在小球已经可以到达指定位置,然后消失了,但是距离我们想要的效果还差那么一点点,我们还要黑洞会随着吞噬小球的多少而进行成长
//首先,在最外部声明一个小球成长比例的参数
let p = 1
//然后在小球碰撞到黑洞后,调整黑洞的大小及位置
p += 0.1
b.style.width = bw * p + 'px'
b.style.height = bh * p + 'px'
b.style.top = 'calc(50% - '+ bh * p/2 +'px)'
b.style.left = 'calc(50% - '+ bw * p/2 +'px)'
OK,大功告成
完整代码
<style type="text/css">
body, html{
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
}
.block {
width: 30px;
height: 30px;
position: absolute;
background: radial-gradient(red, transparent, transparent);
border-radius: 10px;
}
#blackhole {
width: 40px;
height: 40px;
background: radial-gradient(black, transparent, transparent);
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
}
</style>
<body id="body">
<div class="blackhole" id="blackhole"></div>
<script type="text/javascript">
let body = document.getElementById('body')
let b = document.getElementById('blackhole')
let h = window.innerHeight
let w = window.innerWidth
let bw = b.clientHeight
let bh = b.clientWidth
let bt = b.offsetTop
let bl = b.offsetLeft
let p = 1
body.addEventListener('click',e=>{
let a = document.createElement('div')
a.className = 'block'
let x = e.clientX
let y = e.clientY
let ballX = x - 10
let ballY = y - 10
let speedX = (x - w/2)/30 //每次运动点击位置到中心点的1/30距离
let speedY = (y - h/2)/30
body.appendChild(a)
a.style.left = ballX + 'px'
a.style.top = ballY + 'px'
let itv = setInterval(()=>{//每30ms刷新一次位置
ballY -= speedY
ballX -= speedX
a.style.left = ballX + 'px'
a.style.top = ballY + 'px'
if((ballX < (bl + bw - 20) && ballX > (bl - 10))&&
(ballY < (bt + bh - 20) && ballY > (bt - 10))){
clearInterval(itv)
body.removeChild(a)
p += 0.1
b.style.width = bw * p + 'px'
b.style.height = bh * p + 'px'
b.style.top = 'calc(50% - '+ bh * p/2 +'px)'
b.style.left = 'calc(50% - '+ bw * p/2 +'px)'
}
},30)
})
</script>
</body>
w * p + 'px'
b.style.height = bh * p + 'px'
b.style.top = 'calc(50% - '+ bh * p/2 +'px)'
b.style.left = 'calc(50% - '+ bw * p/2 +'px)'
}
},30)
})
</script>
</body>
总结
相对来说,整个制作都是很简单的,但是也稍微有了那么一点游戏的意思,代码本身并不难重点还是需要搞清楚自己的思路吧