如何实现超级酷炫的星星连线效果?

科技   2024-12-22 12:39   广东  

前言

大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~

又很多朋友最近说想要入门 Canvas,问我能不能出一篇讲解 Canvas 的文章,但是我感觉直接讲会比较无聊,还不如通过一些案例来让大家去手敲呢

所以我准备使用 Canvas 实现一个星星连线的小游戏,希望大家能喜欢~

效果 & 实现思路

期望的效果如下:


这个小游戏的开发可以分为以下几个步骤:

  • 1、绘制一个小星星,并且让它能够移动起来。
  • 2、创建一百个这样的小星星
  • 3、当小星星相互靠近的时候,将它们连接起来
  • 4、当鼠标移动的时候,生成小星星
  • 5、当鼠标点击的时候,生成五个小星星

绘制一个小星星,并且让它能够移动起来

移动星星的操作实际上并不复杂。它的原理是先清除原有的星星,然后再重新绘制,同时借助定时器,这样就能营造出星星在移动的视觉效果。不过,这里有个需要注意的地方:当星星碰到边界的时候,要让它反弹回来

达到以下移动以及反弹的效果:

画一百个小星星

创建一个 数组stars 来存储这些星星

效果如下:

当星星之间靠近时,进行连线

若两个星星的 x 坐标与 y 坐标的差值均小于50,那么就对这两个星星进行连线操作,而连线仅仅需要运用ctx.moveToctx.lineTo即可达成

大家不妨思考一下,为何两个forEach不能合并在一起执行呢?这是个颇具思考性的问题。或者大家也可以尝试将其合并执行,看看效果如何,这样就能明白了。这就算给大家布置的一个作业啦!

效果如下:

当鼠标移动的时候,生成小星星

也就是鼠标到哪,那个小星星就到哪,并且这个小星星走到哪都会跟距离近的小星星 连线

效果如下:


鼠标点击生成五个小星星

思路就是,鼠标点击,生成5个小星星,并加到 数组stars

效果如下:

完整代码

const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// 获取当前视图的宽度和高度
let aw = document.documentElement.clientWidth || document.body.clientWidth
let ah = document.documentElement.clientHeight || document.body.clientHeight
// 赋值给canvas
canvas.width = aw
canvas.height = ah

// 屏幕变动时也要监听实时宽高
window.onresize = function ({
    aw = document.documentElement.clientWidth || document.body.clientWidth
    ah = document.documentElement.clientHeight || document.body.clientHeight
    // 赋值给canvas
    canvas.width = aw
    canvas.height = ah
}

// 本游戏无论是实心,还是线条,色调都是白色
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'

function Star(x, y, r{
    // x,y是坐标,r是半径
    this.x = x
    this.y = y
    this.r = r
    // speed参数,在  -3 ~ 3 之间取值
    this.speedX = (Math.random() * 3) * Math.pow(-1Math.round(Math.random()))
    this.speedY = (Math.random() * 3) * Math.pow(-1Math.round(Math.random()))
}

Star.prototype.draw = function ({
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.r, 0Math.PI * 2)
    ctx.fill()
    ctx.closePath()
}

Star.prototype.move = function ({
    this.x -= this.speedX
    this.y -= this.speedY
    // 碰到边界时,反弹,只需要把speed取反就行
    if (this.x < 0 || this.x > aw) this.speedX *= -1
    if (this.y < 0 || this.y > ah) this.speedY *= -1
}

function drawLine(startX, startY, endX, endY{
    ctx.beginPath()
    ctx.moveTo(startX, startY)
    ctx.lineTo(endX, endY)
    ctx.stroke()
    ctx.closePath()
}

const stars = []
for (let i = 0; i < 100; i++) {
    // 随机在canvas范围内找一个坐标画星星
    stars.push(new Star(Math.random() * aw, Math.random() * ah, 3))
}

const mouseStar = new Star(003)

canvas.onmousemove = function (e{
    mouseStar.x = e.clientX
    mouseStar.y = e.clientY
}
window.onclick = function (e{
    for (let i = 0; i < 5; i++) {
        stars.push(new Star(e.clientX, e.clientY, 3))
    }
}

// 星星的移动
setInterval(() => {
    ctx.clearRect(00, aw, ah)
    // 鼠标星星渲染
    mouseStar.draw()
    // 遍历移动渲染
    stars.forEach(star => {
        star.move()
        star.draw()
    })
    stars.forEach((star, index) => {
        // 类似于冒泡排序那样,去比较,确保所有星星两两之间都比较到
        for (let i = index + 1; i < stars.length; i++) {
            if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {
                drawLine(star.x, star.y, stars[i].x, stars[i].y)
            }
        }

        if (Math.abs(mouseStar.x - star.x) < 50 && Math.abs(mouseStar.y - star.y) < 50) {
            drawLine(mouseStar.x, mouseStar.y, star.x, star.y)
        }
    })
}, 50)

结语

我是林三心,一个待过小型toG型外包公司、大型外包公司、小公司、潜力型创业公司、大公司的作死型前端选手

前端之神
一位前端小菜鸡,写过400多篇原创文章,全网有6w+个前端朋友,梦想是成为”前端之神“~
 最新文章