Canvas 绘制背景小球、与鼠标交互的小球

本文介绍如何使用HTML5 Canvas绘制动态背景特效,包括随机小球移动、鼠标交互效果及离屏渲染技巧,实现流畅的视觉体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

canvas入门,跟随掘金小册如何使用 Canvas 制作出炫酷的网页背景特效学习,完成一个简单的 canvas demo

下面是代码,操作解释都有注释:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Canvas Demo</title>
    <style>
        *{
            margin: 0;
        }
        html,body {
            margin: 0;
            overflow: hidden;
            width: 100%;
            height: 100%;
            cursor: none;
            background: #000;
        }
    </style>
</head>

<body>
    <div id="app"></div>
    <canvas id="canvas"></canvas>
</body>
<script>
    const ctx = document.getElementById("canvas"),
        context = ctx.getContext("2d"), //获取 canvas 上下文
        docWidth = document.documentElement.clientWidth,
        docHeight = document.documentElement.clientHeight,
        config = {
            num: 100,
            r: 1,          //圆每次增加的半径 
            option: 0.05  // 判断圆消失的条件,数值越大,消失的越快
        },
        useCache = true; //离屏渲染

    ctx.width = docWidth;
    ctx.height = docHeight;

    //随机小球
    class Round_Item{
        constructor (index, x, y) {
            this.index = index;
            this.x = x;
            this.y = y;
            this.r = Math.random() * 2 + 1;
            var alpha = (Math.floor(Math.random() * 10) + 1) / 10 / 2; //不透明度
            this.color = "rgba(255,255,255," + alpha + ")";
            if (useCache) {
                //创建一个不渲染的离线 canvas 小球
                this.cacheCanvas = document.createElement('canvas');
                this.cacheContext = this.cacheCanvas.getContext('2d');

                //考虑到阴影,设置宽高为 6 * 半径
                this.cacheContext.width = 6 * this.r;
                this.cacheContext.height = 6 * this.r;
                this.cache();
            }
        }
        cache () {
            this.cacheContext.save(); //保存canvas状态
            this.cacheContext.fillStyle = this.color; //填充颜色
            this.cacheContext.shadowColor = "white"; //阴影颜色
            this.cacheContext.shadowBlur = this.r * 2; //阴影距离
            this.cacheContext.beginPath(); //开始路径绘制
            this.cacheContext.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI); //绘制圆形路径
            this.cacheContext.closePath(); //闭合路径
            this.cacheContext.fill(); //填充
            this.cacheContext.restore(); //恢复上一次保存的状态,即默认状态,此时上面小球已经绘制完成
        }
        draw () {
            if (!useCache) {
                context.fillStyle = this.color;
                context.shadowBlur = this.r * 2;
                context.shadowColor = '#fff';
                context.beginPath();
                context.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
                context.closePath();
                context.fill();
            } else {
                context.drawImage(this.cacheCanvas, this.x - this.r, this.y - this.r); //将离线 canvas 绘制到页面中
            }
        }
        move () {
            // 移动条件
            this.y -= 0.3;
            if (this.y <= -10) {
                this.y = docHeight + 10; //离开屏幕则重新赋 Y 值
            }
            this.draw();
        }
    }

    //鼠标滑动小球
    class Mouse_Item{
        constructor (mouseX, mouseY, radius, option, color) {
            this.mouseX = mouseX;
            this.mouseY = mouseY;
            this.radius = radius;
            this.option = option;
            this.color = color;
        }
        draw () {
            context.fillStyle = 'hsl(' + this.color + ',100%,80%)'; //hsl(h,s,l); h 色调 s 饱和度 l 亮度
            context.shadowBlur = this.radius * 2;
            context.shadowColor = '#fff';
            context.beginPath();
            context.arc(this.mouseX, this.mouseY, this.radius, 0, 2 * Math.PI, false);
            context.closePath();
            context.fill();
        }
        move () {
            this.radius += config.r; //逐渐增大半径
            this.option -= config.option; //消失条件
            this.color += .1;
            if( this.option <= 0){
                return true;
            }
            this.draw();
        }
    }

    class InitCanvas{
        constructor (count) {
            this.initRoundPopulation = count;
            this.populations = [];
            this.mousePopu = [];
        }
        init () {
            for (let i = 0; i < this.initRoundPopulation; i++) {
                this.populations.push(new Round_Item(i, Math.random() * docWidth,Math.random() * docHeight));
                this.populations[i].draw();
            }
            this.animate();
        }
        animate() {
            context.clearRect(0, 0, docWidth, docHeight); //清空 canvas 以便后面重新绘制
            
            for (let item of this.populations) {
                item.move();
            }

            if (this.mousePopu.length) {
                for (let i = 0; i< this.mousePopu.length; i++) {
                    if (this.mousePopu[i].move()) { //鼠标渲染小球,满足消失条件则去除
                        this.mousePopu.splice(i,1);
                        i--;
                    }
                }
            }
            requestAnimationFrame(_ => this.animate()); // js 动画渲染函数
        }
    }
    let initCanvas = new InitCanvas(config.num);
    initCanvas.init();
    // 监听 mousemove 事件,添加鼠标小球
    window.onmousemove = function (event) {
        mouseX = event.clientX;
        mouseY = event.clientY;
        initCanvas.mousePopu.push(new Mouse_Item(mouseX, mouseY, config.r, 1, Math.random() * 360));
    };
</script>

</html>

效果图:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值