uniapp 我就不想喷了,踩了很多坑,把代码贡献出来让大家少踩些坑。

实现的功能:

  • 生成n个球在canvas中运动,相互碰撞后会反弹,反弹后的速度计算我研究过了,可以参考代码直接用
  • 防止球出边框
  • 防止小球之间碰撞过度,或者说“穿模”。采用的方法是碰撞后让两个小球都多走一帧。其实这样并不能完全防止“穿模”,但可以防止小球粘在一起不停的穿模
  • uniapp中的requestAnimationFrame的使用,包括开始与停止动画
  • 利用四叉树优化了碰撞检测,网上有些示例是直接让区域内所有的小球之间进行一次碰撞检测

代码是vue3写的。uniapp官方说做动画推荐用什么renderjs,不知道直接这样写还会有什么坑,目前在h5中测试没问题。

四叉树类的代码见我上一篇文章

ball类的代码:

  1. export class Ball {
  2. // 提供圆心坐标和半径
  3. constructor(x, y, r, speedX, speedY, color, index) {
  4. this.centerX = x
  5. this.centerY = y
  6. this.r = r
  7.  
  8. this.x = x - r
  9. this.y = y - r
  10. this.width = 2 * r
  11. this.height = 2 * r
  12.  
  13. this.color = color
  14.  
  15. this.speedX = speedX
  16. this.speedY = speedY
  17.  
  18. this.index = index // 索引
  19. }
  20. // 将球填充到canvas context
  21. fillTo(ctx) {
  22. ctx.beginPath()
  23. ctx.arc(this.centerX, this.centerY, this.r, 0, 2 * Math.PI)
  24. ctx.setFillStyle(this.color)
  25. ctx.closePath()
  26. ctx.fill()
  27. }
  28.  
  29. // 判断是否与另一个球相交
  30. intersectAt(ball2) {
  31. let dx = this.centerX - ball2.centerX
  32. let dy = this.centerY - ball2.centerY
  33. let distance = Math.sqrt(dx * dx + dy * dy)
  34. return this.r + ball2.r >= distance
  35. }
  36.  
  37. // 移动 width height 是canvas的宽高
  38. move(width, height) {
  39.  
  40. this.centerX += this.speedX
  41. if (this.centerX - this.r <= 0) {
  42. this.centerX = this.r
  43. this.speedX = -this.speedX
  44. }
  45. if (this.r + this.centerX >= width) {
  46. this.centerX = width - this.r
  47. this.speedX = -this.speedX
  48. }
  49.  
  50. this.centerY += this.speedY
  51.  
  52. if (this.centerY - this.r <= 0) {
  53. this.centerY = this.r
  54. this.speedY = -this.speedY
  55. }
  56.  
  57. if (this.centerY + this.r >= height) {
  58. this.centerY = height - this.r
  59. this.speedY = -this.speedY
  60. }
  61.  
  62. this.x = this.centerX - this.r
  63. this.y = this.centerY - this.r
  64. }
  65.  
  66. }

page的代码:

  1. <template>
  2. <view>
  3. <view>
  4. <button @click="animateStart">开始</button>
  5. <button @click="animateStop">停止</button>
  6. </view>
  7. <canvas type="2d" :style="{'width':canvasSize+'px','height':canvasSize+'px','border':'1px solid black'}"
  8. canvas-id="game" id="game"></canvas>
  9. </view>
  10. </template>
  11.  
  12. <script setup>
  13. import {
  14. ref,
  15. onMounted
  16. } from 'vue'
  17.  
  18. import {
  19. onReady
  20. } from '@dcloudio/uni-app'
  21.  
  22. import {
  23. Ball
  24. } from '@/utils/Ball.js'
  25.  
  26. import {
  27. QuadTree
  28. } from '@/utils/QuadTree.js'
  29.  
  30. console.log(uni.getSystemInfoSync().screenWidth)
  31. console.log(uni.getSystemInfoSync().windowWidth)
  32.  
  33. const canvasSize = ref((uni.getSystemInfoSync().windowWidth < uni.getSystemInfoSync().windowHeight ? uni
  34. .getSystemInfoSync()
  35. .windowWidth : uni.getSystemInfoSync().windowHeight) - 100)
  36.  
  37. const size = canvasSize.value / 32
  38. const ballList = []
  39.  
  40. for (let i = 0; i < 30; i++) {
  41. let x = Math.random() * canvasSize.value,
  42. y = Math.random() * canvasSize.value,
  43. r = Math.random() * size / 1.5 + 10,
  44. color = randomHexColor()
  45. const ball = new Ball(x, y, r, Math.random() * 7 + 3, Math.random() * 5 + 5, color, i)
  46. ballList.push(ball)
  47. }
  48.  
  49. let ctx = null
  50.  
  51. onReady(() => {
  52. ctx = uni.createCanvasContext('game')
  53. console.log(ballList)
  54. })
  55.  
  56. let rAF
  57.  
  58. const animateStart = () => {
  59. render(ctx)
  60. rAF = requestAnimationFrame(() => {
  61. animateStart()
  62. })
  63. }
  64.  
  65. const animateStop = () =>{
  66. cancelAnimationFrame(rAF)
  67. }
  68.  
  69. const render = (ctx) => {
  70.  
  71. const quadTree = new QuadTree(0, 0, canvasSize.value, canvasSize.value, 0)
  72.  
  73. for (let i = 0; i < ballList.length; i++) {
  74. quadTree.insert(ballList[i])
  75. }
  76.  
  77. for (let q of quadTree.possibles()) {
  78. // console.log('*', Date.now())
  79. ctx.setFillStyle("rgba(255, 170, 80, 0.2)")
  80. ctx.setStrokeStyle('yellow')
  81. ctx.fillRect(q.x, q.y, q.width, q.height)
  82. ctx.strokeRect(q.x, q.y, q.width, q.height)
  83.  
  84. for (let i = 0; i < q.sprites.length - 1; i++) {
  85. for (let j = i + 1; j < q.sprites.length; j++) {
  86. let a = q.sprites[i]
  87. let b = q.sprites[j]
  88. if (a.intersectAt(b)) {
  89.  
  90. // 假设半径就是重量
  91. let v1, v2, m1 = a.r,
  92. m2 = b.r
  93. // 先算水平速度
  94. v1 = a.speedX
  95. v2 = b.speedX
  96.  
  97. a.speedX = (v1 * (m1 - m2) + 2 * m2 * v2) / (m1 + m2)
  98. b.speedX = (v2 * (m2 - m1) + 2 * m1 * v1) / (m1 + m2)
  99. // 再算垂直速度
  100. v1 = a.speedY
  101. v2 = b.speedY
  102.  
  103. a.speedY = (v1 * (m1 - m2) + 2 * m2 * v2) / (m1 + m2)
  104. b.speedY = (v2 * (m2 - m1) + 2 * m1 * v1) / (m1 + m2)
  105.  
  106. //发生碰撞的小球,防止穿模,多走一步
  107. a.move(canvasSize.value, canvasSize.value)
  108. b.move(canvasSize.value, canvasSize.value)
  109.  
  110. }
  111. }
  112. }
  113. }
  114. ballList.forEach((ball) => {
  115. ball.move(canvasSize.value, canvasSize.value)
  116. ball.fillTo(ctx)
  117. })
  118.  
  119. ctx.draw()
  120.  
  121. }
  122.  
  123. function randomHexColor() {
  124. //生成ffffff以内16进制数
  125. var hex = Math.floor(Math.random() * 16777216).toString(16);
  126. //while循环判断hex位数,少于6位前面加0凑够6位
  127. while (hex.length < 6) {
  128. hex = '0' + hex;
  129. }
  130. //返回‘#'开头16进制颜色
  131. return '#' + hex;
  132. }
  133. </script>
  134.  
  135. <style>
  136.  
  137. </style>

uniapp中用canvas实现小球碰撞的小动画的更多相关文章

  1. HTML5 Canvas彩色小球碰撞运动特效

    脚本简介 HTML5 Canvas彩色小球碰撞运动特效是一款基于canvas加面向对象制作的运动小球动画特效.   效果展示 http://hovertree.com/texiao/html5/39/ ...

  2. Canvas+Js制作动量守恒的小球碰撞

    目的:通过js实现小球碰撞并实现动量守恒 canvas我们就不多说了,有用着呢. 我们可以通过canvas画2D图形(圆.方块.三角形等等)3D图形(球体.正方体等待). 当然这只是基础的皮毛而已,c ...

  3. canvas 模拟小球上抛运动的物理效果

    最近一直想用学的canvas做一个漂亮的小应用,但是,发现事情并不是想的那么简单.比如,游戏的逼真效果,需要自己来coding…… 所以,自己又先做了一个小demo,算是体验一下亲手打造物理引擎的感觉 ...

  4. 【h5游戏开发】egret引擎p2物理引擎 - 小球碰撞地面搞笑的物理现象

    重力的方向和地面的问题 p2中默认的方向是从上到下,如果重力默认是正数的话,物体放到世界中是会从上面往下面飘的 p2中plane地面默认的方向是y轴的方向,而在p2中y轴的方向默认是从上往下 首先来看 ...

  5. canvas动态小球重叠效果

    前面的话 在javascript运动系列中,详细介绍了各种运动,其中就包括碰壁运动.但是,如果用canvas去实现,却是另一种思路.本文将详细介绍canvas动态小球重叠效果 效果展示 静态小球 首先 ...

  6. js实现多个小球碰撞

    实现思路:小球的移动,是通过改变小球的left和top值来改变,坐标分别为(x,y)当x/y值加到最大,即加到父级的宽度或者高度时,使x值或者y值减小,同理当x值或者y值减到最小时,同样的使x值或者y ...

  7. js实现小球碰撞游戏

    效果图:  效果图消失只是截的gif图的问题 源码: <!DOCTYPE html> <html lang="en"> <head> <m ...

  8. Canvas 动态小球重叠效果

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  9. Canvas 图片绕边旋转的小动画

    /** * 图片绕边旋转的小动画 */ function initDemo10() { var canvas = document.getElementById("demo10") ...

随机推荐

  1. 设置VisualStudio以管理员身份运行

    以vs2013为例 vs右键属性 ----- 找到目标位置如下 "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\ID ...

  2. linux篇-图解cacti监控安装

    1登录 admin admin 2点击devices localhost 3进入配置保存 4保存 http服务要启动哦 5一步步做 6graph tree 7执行/usr/bin/php /var/w ...

  3. 『忘了再学』Shell基础 — 16、位置参数变量

    目录 1.位置参数变量$n 2.位置参数变量$*和$@ 3.位置参数变量$# 位置參数变量的作用主要用于脚本的传参. 位置參数变量的名称和作用都是确定不能改变的,但是该变量的内容是可以更改的,也就是变 ...

  4. 缓存&PWA实践

    缓存&PWA 实践 一.背景 从上一篇<前端动画实现与原理分析>,我们从 Performance 进行动画的性能分析,并根据 Performance 分析来优化动画.但,前端不仅仅 ...

  5. 最大流&最小割&费用流模版

    好久都没有搞博客了.想认真写又要准备文化课期末了. ISAP 流程: 原理就是dfs找增广路. 最基础的建反向边以便反悔就不说了. 但是记录一个dep(dis)表示层数,一开始BFS(从t开始,dis ...

  6. CabloyJS部署了一套演示站点

    为了方便大家快速体验和了解CabloyJS的风格和特性,全新部署了一套演示站点.对于初次接触CabloyJS的开发者,不用下载新建项目,就可以直接体验CabloyJS了 在线演示 场景 链接/二维码 ...

  7. python变量名下划线

    xx: 公有变量 _x: 单前置下划线,保护变量,私有化属性或方法,不能用于'from module import *' 以单下划线开头的表示的是protected类型的变量.即保护类型只能允许其/类 ...

  8. printf 输出前导0

    printf ("%3d\n", 5); printf ("%03d\n", 5); 输出为

  9. 皓远的第一次博客作业(pta题目集——1-3)

    题目集总结: 前言: 一. 知识点运用: ①    Java入门的基础语法(循环,判断,字符串,数组等等),Java的基础类运用,类与对象关系调用,类间关系(聚合). ②    引(类与对象): 对象 ...

  10. Sublime Text 新建代码片段(图解)

    新建代码片段 1.打开NEW Snippet- 2.编辑代码片段 3.设置快捷键,按tab键执行 更多内容请见原文,原文转载自:https://blog.csdn.net/weixin_4451949 ...