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

实现的功能:

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

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

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

ball类的代码:

export class Ball {
    // 提供圆心坐标和半径
    constructor(x, y, r, speedX, speedY, color, index) {
        this.centerX = x
        this.centerY = y
        this.r = r

        this.x = x - r
        this.y = y - r
        this.width = 2 * r
        this.height = 2 * r

        this.color = color

        this.speedX = speedX
        this.speedY = speedY
        
        this.index = index // 索引
    }
    // 将球填充到canvas context
    fillTo(ctx) {
        ctx.beginPath()
        ctx.arc(this.centerX, this.centerY, this.r, 0, 2 * Math.PI)
        ctx.setFillStyle(this.color)
        ctx.closePath()
        ctx.fill()
    }

    // 判断是否与另一个球相交
    intersectAt(ball2) {
        let dx = this.centerX - ball2.centerX
        let dy = this.centerY - ball2.centerY
        let distance = Math.sqrt(dx * dx + dy * dy)
        return this.r + ball2.r >= distance
    }

    // 移动 width height 是canvas的宽高
    move(width, height) {

        this.centerX += this.speedX
        if (this.centerX - this.r <= 0) {
            this.centerX = this.r
            this.speedX = -this.speedX
        }
        if (this.r + this.centerX >= width) {
            this.centerX = width - this.r
            this.speedX = -this.speedX
        }
        
        this.centerY += this.speedY
        
        if (this.centerY - this.r <= 0) {
            this.centerY = this.r
            this.speedY = -this.speedY
        }
        
        if (this.centerY + this.r >= height) {
            this.centerY = height - this.r
            this.speedY = -this.speedY
        }

        this.x = this.centerX - this.r
        this.y = this.centerY - this.r
    }


}

page的代码:

<template>
    <view>
        <view>
            <button @click="animateStart">开始</button>
            <button @click="animateStop">停止</button>
        </view>
        <canvas type="2d" :style="{'width':canvasSize+'px','height':canvasSize+'px','border':'1px solid black'}"
            canvas-id="game" id="game"></canvas>
    </view>
</template>

<script setup>
    import {
        ref,
        onMounted
    } from 'vue'

    import {
        onReady
    } from '@dcloudio/uni-app'

    import {
        Ball
    } from '@/utils/Ball.js'

    import {
        QuadTree
    } from '@/utils/QuadTree.js'

    console.log(uni.getSystemInfoSync().screenWidth)
    console.log(uni.getSystemInfoSync().windowWidth)

    const canvasSize = ref((uni.getSystemInfoSync().windowWidth < uni.getSystemInfoSync().windowHeight ? uni
        .getSystemInfoSync()
        .windowWidth : uni.getSystemInfoSync().windowHeight) - 100)

    
    const size = canvasSize.value / 32
    const ballList = []

    for (let i = 0; i < 30; i++) {
        let x = Math.random() * canvasSize.value,
            y = Math.random() * canvasSize.value,
            r = Math.random() * size / 1.5 + 10,
            color = randomHexColor()
        const ball = new Ball(x, y, r, Math.random() * 7 + 3, Math.random() * 5 + 5, color, i)
        ballList.push(ball)
    }

    let ctx = null

    onReady(() => {
        ctx = uni.createCanvasContext('game')
        console.log(ballList)
    })


    let rAF

    const animateStart = () => {
        render(ctx)
        rAF = requestAnimationFrame(() => {
            animateStart()
        })
    }
    
    const animateStop = () =>{
        cancelAnimationFrame(rAF)
    }

    const render = (ctx) => {

        const quadTree = new QuadTree(0, 0, canvasSize.value, canvasSize.value, 0)


        for (let i = 0; i < ballList.length; i++) {
            quadTree.insert(ballList[i])
        }


        for (let q of quadTree.possibles()) {
            // console.log('*', Date.now())
            ctx.setFillStyle("rgba(255, 170, 80, 0.2)")
            ctx.setStrokeStyle('yellow')
            ctx.fillRect(q.x, q.y, q.width, q.height)
            ctx.strokeRect(q.x, q.y, q.width, q.height)

            for (let i = 0; i < q.sprites.length - 1; i++) {
                for (let j = i + 1; j < q.sprites.length; j++) {
                    let a = q.sprites[i]
                    let b = q.sprites[j]
                    if (a.intersectAt(b)) {

                        // 假设半径就是重量
                        let v1, v2, m1 = a.r,
                            m2 = b.r
                        // 先算水平速度
                        v1 = a.speedX
                        v2 = b.speedX

                        a.speedX = (v1 * (m1 - m2) + 2 * m2 * v2) / (m1 + m2)
                        b.speedX = (v2 * (m2 - m1) + 2 * m1 * v1) / (m1 + m2)
                        // 再算垂直速度
                        v1 = a.speedY
                        v2 = b.speedY

                        a.speedY = (v1 * (m1 - m2) + 2 * m2 * v2) / (m1 + m2)
                        b.speedY = (v2 * (m2 - m1) + 2 * m1 * v1) / (m1 + m2)

                        //发生碰撞的小球,防止穿模,多走一步
                        a.move(canvasSize.value, canvasSize.value)
                        b.move(canvasSize.value, canvasSize.value)

                    }
                }
            }
        }
        ballList.forEach((ball) => {
            ball.move(canvasSize.value, canvasSize.value)
            ball.fillTo(ctx)
        })

        ctx.draw()

    }

    function randomHexColor() {
        //生成ffffff以内16进制数
        var hex = Math.floor(Math.random() * 16777216).toString(16);
        //while循环判断hex位数,少于6位前面加0凑够6位
        while (hex.length < 6) {
            hex = '0' + hex;
        }
        //返回‘#'开头16进制颜色
        return '#' + hex;
    }
</script>

<style>

</style>

标签智能推荐:

uniapp webview引入另一个uniapp打包的h5传值问题

1.文档背景:最近有朋友问我我的https://www.cnblogs.com/lizhao123/p/12005868.html这个webview传值收不到(原因引入了另一个uniapp打包的h5)2.实现方案首先你要引入的另一个uniapp的h5,这个uniapp项目中需要引入官方的https://gitee.com/dcloud/uni-app/raw/dev/dist/uni.webvie

更改uniapp中,input的placeholder样式不生效的问题

场景:需要更改uniapp中input输入框的placeholder样式使用了UNIapp的placeholder-class和placeholder-style未生效解决:.input为input类名.input::placeholder{font-size:28rpx;color:#AAAAAA;}

UniApp入门教程- 介绍和开发准备

uniapp开发最合适的开发工具HBuilderX,下载并安装&nbsp;https://www.dcloud.io/hbuilderx.html&nbsp;UniApp特点:=》数据绑定和页面生命周期类似Vue.js=》接口能力靠近微信小程序规范=》组件封装接近小程序=》flex布局兼容多端运行&nbsp;后续计划推出Uniapp入门教程系列(3)(4)(5)UniApp入门教程-项目优化&nb

多线程的JavaScript: Web Worker

executionthreadofawebapplication.优点允许JavaScript脚本创建多个线程,从而充分利用CPU的多核计算能力,不会阻塞主线程(一般指UI渲染线程)的运行,兼容性良好,基本覆盖了所有主流浏览器局限WebWorker本质上没有突破JS的单线程的性质,WebWorker脚步不能直接操作DOM节点,不能使用绝大多数的BOMAPI。上下文环境是DedicatedWorke

深夜debug:一个不常遇到的HbuilderX自动化测试运行问题

unnywasher-uniapp:mp-weixin]atObject.Module._extensions..js(node:internal/modules/cjs/loader:1153:10)02:12:29.918[sunnywasher-uniapp:mp-weixin]at/Applications/HBuilderX.app/Contents/HBuilderX/plugins/

uniapp-组件引用错误,仅支持 import 方式引入组件

组件引用错误,仅支持import方式引入组件uniapp,删掉一个import的导入,但是忘记删掉components内的内容了。

uniapp在其他编辑器运行的问题

一、不同方式创建uniapp的区别通过cli命令创建出来的uniapp可以在任意编译器(vscode、webstorm等)利用npmrunserve或npmrundev:h5运行通过hbuilder创建的uniapp不可以在其他编译器运行(尝试过迁移项目,失败,会出现插件缺失或路径等各种问题,尝试通过删除node_modules重新下载插件包也不能解决问题)所以想要用其他编译器运行uniapp,只

uniapp 发起请求 request 返回数据出现小数科学计数法的解决

uniapp的发起请求APIuni.request返回的数据出现小数科学计数法。原因是request方法的参数dataType默认值是json,uniapp会尝试对返回的数据做一次JSON.parse。那么如果返回的是2021111222001414245754264151这种很大的数字,JSON.parse之后会得到2.0211112220014142e+27.解决:dataType设置为tex

uniapp中导入JS-SDK,报wx.config is not a function

问题:如图原因:uniapp内部有同样叫WX的全局对象,把JSSDK的WX对象覆盖了解决方案,重命名constjsWx=require('@/utils/jweixin.js')

uniapp 微信登录提示signature不一致

1.登录微信开放平台检查uniapp打包包名2.安装apk-&gt;GenSignature应用通过包名获取签名3.修改微信开放平台的签名