JavaScript+Canvas 实现Bubble Effect

JavaScript+Canvas 实现Bubble Effect

——————————————————

最近对网页的交互特效很感兴趣,于是自己动手做了一个气泡特效,程序根据鼠标的位置每隔一段时间创建新的气泡,实现起来其实很简单,只需要Canvas画图功能和定时器就可以实现,也就是说任何有绘图接口的编程语言都可以实现气泡特效,闲话不多说,先贴一张运行效果图片吧。

气泡特效

实际运行效果比GIF图片中的效果要更加流畅,下面有程序代码,也可以直接在github上下载代码。

接下来我会首先说明以下设计思路,然后分享一下实现过程中遇到的困难和解决过程,最后会把代码贴出来。

一、设计思路


1、动画原理。
说到用代码实现物体运动效果,有过编程经历的小伙伴们首先想到的一定是timer(定时器),每隔固定时间调用一次画图函数,但使用JavaScript实现动画效果不推荐使用setInterval函数!这个函数完全可以实现动画效果,但有可能受到很大的限制,如果动画效果稍微复杂一点,setInterval就有可能无法顺利实现动画,导致动画丢帧等问题,好在现在各大浏览器基本上都实现了requestAnimationFrame函数,这个函数会让系统自己决定什么执行画图函数,但会尽量快!requestAnimationFrame每执行一次就会先重画所有气泡,之后根据速度改变改变每个气泡的位置。

2、运动轨迹。
气泡特效最重要的一点是要模拟气泡运动的轨迹,这其中涉及到速度、方向,如果你仔细观察,会发现每个气泡上升的速度是一定的,而平移的速度会慢慢降到0,这样就基本上可以推断出气泡对象的属性:x\y(横纵坐标)、radius(半径)、vx\vy(水平移动速度和向上移动速度)、horizontal direction(水平运动方向)、color(颜色),最后还要判断气泡是否超出了边界,所以还有state(状态)属性。

3、鼠标响应及时间间隔。
要让气泡产生的位置根据鼠标的位置移动, 程序中定义了两个全局变量MouseX和MouseY, 气泡产生时就是根据这两个全局变量来确定初始位置,设立鼠标响应事件mousemove, 每当鼠标移动,触发事件,改变MouseX和MouseY的值就实现根据鼠标位置产生气泡。

时间间隔通过一个频率计数器来实现,每执行一次requestAnimationFrame,计数器加一,固定次数之后产生新汽泡。

二、实现细节


1、内存压力。
因为程序会不断的产生气泡,如果每次创建一个气泡就多创建一个对象,无疑会对内存造成巨大的压力。用数组管理气泡对象的好处不仅仅是便于索引,还可以为内存缓解压力!

每次重画气泡的时候都要判断气泡的状态,如果气泡已经超出了画布的范围,那么就不会再画这个气泡,这就一意味着该气泡在数组中对应的位置空出来了!我们可以充分利用这一点,反正要遍历数组,那么在遍历的过程中记录下出界气泡的索引,然后在该索引处创建新的气泡,原来的对象没有引用指向就会自动由内存管理器处理,这样一来就解决了内存的问题。还有一个问题就是如果数组中所有气泡都在界内,此时需要扩充数组大小,从我几次的测试结果来看,数组大小一般稳定在26左右,运行结果良好。


三、代码如下:

html
<!DOCTYPE html>
<html>
  <head>
    <title>Bubbles</title>
    <meta name="content-type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <style>
        *{
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body{
            height: 100vh;
            width: 100vw;
            background-color: #000000;
            overflow: hidden;
        }
    </style>
  </head>
  <body>
      <canvas id="canvas"></canvas>
      <script src="../js/canvas.js"></script>
  </body>
</html>
JavaScript
var Container  = document.querySelector("body");
var canvas     = document.querySelector("#canvas");
var WIDTH  = Container.clientWidth;
var HEIGHT = Container.clientHeight;
canvas.width  = Container.clientWidth;
canvas.height = Container.clientHeight;
var ctx = canvas.getContext('2d');
var colors = ['#70f3ff','#44cef6','#871F78','#faff72','rgb(246,22,103)','#00e079','#b0a4e3','#f3d3e7','#ff2d51']
var bubbleArray = new Array(20);
const LEFT  = -1;
const RIGHT = 1;
const RISING = 1;
const GONE = -1;
var MouseX = 0;
var MouseY = 0;
function setSize() {/*the size of canvas will be resized if the size of window is changed*/
    Container  = document.querySelector("body");
    canvas     = document.querySelector("#canvas");
    canvas.width  = Container.clientWidth;
    canvas.height = Container.clientHeight;
}
window.addEventListener('resize',setSize,false);

var bubble = {     /*气泡的原型,用来创建新的水泡*/
    x:300,y:1000,radius:30,vx:10,vy:-3,
    color:'#70f3ff',state:GONE,direction:1,
    Draw:function(){

        ctx.beginPath();
        ctx.moveTo(this.x,this.y);

        ctx.arc(this.x, this.y, Math.abs(this.radius), 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fillStyle = this.color;
        ctx.fill();
    },
    /*
       bubbles movements, check the position of the bubble first,
       and change state if the bubble is beyond the boundary
    */
    Move:function () {
        if(this.y>=0){
            this.x += this.vx*this.direction;
            this.y += this.vy;
            this.vx *= .95;
            this.radius -= .001;
        }else{
            this.state = GONE;
        }
    }
};


/*
* create a new bubble,using bubble as it's protetype
*
* x: x coordinate of the center
* y: y coordinate of the center
* return: the bubble been created
*
* */
function NewBubble(x,y){
    var b = Object.create(bubble);
    b.x = x;
    b.y = y;
    b.state = RISING;
    b.radius = Math.random()*10+5;
    b.vx = Math.random()*5;
    var flag = Math.floor(Math.random()*2); //for direction
    var color = Math.floor(Math.random()*colors.length);
    b.color = colors[color];
    if(flag == 0){
        b.direction = LEFT;
    }else{
        b.direction = RIGHT;
    }
    return b ;
}

/*
* draw bubbles,those bubbles of whom the state is RISING will
* be painted. and create a new bubble every fixed interval
*
* */
var iterator = 0;
var frequency = 10;
function DrawBubbles(){
    ctx.clearRect(0,0,canvas.width,canvas.height);
    var avaIndex = -1;
    var flag = true;
    for(var i=0;i<bubbleArray.length;i++){  /*moving and repaintng of bubbles*/
        if(bubbleArray[i] == undefined || bubbleArray[i].state == GONE) {
            avaIndex = i;
        }else{
            bubbleArray[i].Draw();
            bubbleArray[i].Move();
        }
    }
    if(iterator>=frequency){    //check if it's time to create a new bubble
        if(avaIndex == -1){
            bubbleArray[bubbleArray.length] = NewBubble(MouseX,MouseY);
            bubbleArray[bubbleArray.length-1].Draw();
            bubbleArray[bubbleArray.length-1].Move();
        }else{
            bubbleArray[avaIndex] = NewBubble(MouseX,MouseY);
            bubbleArray[avaIndex].Draw();
            bubbleArray[avaIndex].Move();
        }
    }else{
        flag = false;
    }

    iterator = flag? 0 : iterator+1;
    window.requestAnimationFrame(DrawBubbles);
}
canvas.addEventListener('mousemove',function (event) {
    MouseX = event.clientX;
    MouseY = event.clientY;
},true);

window.requestAnimationFrame(DrawBubbles);

本次分享到这里就结束了,希望你喜欢这个特效,如果有什么更好的想法和建议,请踊跃发言,也希望喜欢前端的小伙伴可以联系我,大家互相交流、学习、进步。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现拓扑图可以使用 Vue3 和 Canvas 结合来完成。下面是一个简单的实现过程: 1. 首先,需要安装 Vue3 和 Canvas 库: ``` bash npm install vue@next npm install canvas --save ``` 2. 在 Vue3 中创建一个组件,用于渲染拓扑图。在组件中引入 Canvas 库: ``` javascript <template> <canvas ref="canvas"></canvas> </template> <script> import { onMounted, ref } from 'vue' import Canvas from 'canvas' export default { setup() { const canvasRef = ref(null) onMounted(() => { const canvas = canvasRef.value const ctx = canvas.getContext('2d') // 在这里进行绘制 }) return { canvasRef } } } </script> ``` 3. 在组件的 `onMounted` 钩子函数中,获取 Canvas 的上下文对象 `ctx`,并进行绘制。可以使用 Canvas 的 API 画出线条、圆形等形状,也可以使用外部库来绘制更复杂的图形。 4. 在绘制时,可以将节点和线条信息存储在数组中,以方便后续的更新和交互。例如: ``` javascript // 存储节点和线条信息的数组 const nodes = [ {x: 100, y: 100, r: 20, color: '#ff0000'}, {x: 200, y: 200, r: 30, color: '#00ff00'} ] const links = [ {source: 0, target: 1}, {source: 1, target: 2} ] // 绘制节点 nodes.forEach(node => { ctx.beginPath() ctx.arc(node.x, node.y, node.r, 0, Math.PI * 2) ctx.fillStyle = node.color ctx.fill() }) // 绘制线条 links.forEach(link => { const source = nodes[link.source] const target = nodes[link.target] ctx.beginPath() ctx.moveTo(source.x, source.y) ctx.lineTo(target.x, target.y) ctx.stroke() }) ``` 以上就是一个简单的 Vue3 和 Canvas 实现拓扑图的过程。需要注意的是,Vue3 的模板中不能直接使用 Canvas,需要通过 `ref` 引用实现。另外,绘制时需要注意节点和线条的位置信息,以及 Canvas 的坐标系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值