canvas实现圆环范围内随机泡泡点带文字效果

要实现的效果:

  1. 圆环范围内随机点
  2. 点有镜像渐变效果
  3. 点中有文字
  4. 点可以随机大小
  5. 点可以浮动
  6. 点与点之间不重合
  7. 点的数量随机
  8. 文本跟随点的大小变化
  9. 缓存第一次位置

image.webp

一、静态效果

完整代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
  </head>
  <body>
    <canvas height="500" id="myCanvas" width="500">
    </canvas>
    <script type="text/javascript">
      // 获取 canvas 元素和绘图上下文
            const canvas = document.getElementById('myCanvas');
            const ctx = canvas.getContext('2d');

            // 圆心坐标和半径
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const outerRadius = 200;
            const innerRadius = 100;

            // 绘制圆环
            ctx.beginPath();
            ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
            ctx.stroke();

            ctx.beginPath();
            ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
            ctx.stroke();

            // 在圆环范围内随机分布点
            function getRandomPointInRing(innerRadius, outerRadius) {
                const angle = Math.random() * 2 * Math.PI;
                const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
                const x = centerX + radius * Math.cos(angle);
                const y = centerY + radius * Math.sin(angle);
                return { x, y };
            }

            // 检查两个点之间的距离
            function distance(point1, point2) {
                return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
            }

            // 创建镜像渐变
            function createMirrorGradient(ctx, x, y, radius) {
                const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
                gradient.addColorStop(0, 'rgba(255, 0, 0, 1)');
                gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0)');
                gradient.addColorStop(1, 'rgba(255, 0, 0, 1)');
                return gradient;
            }

            // 绘制随机点,确保点与点之间没有交集,并在点的中心添加文本和边框
            function drawRandomPoints(numPoints) {
                const points = [];
                while (points.length < numPoints) {
                    const point = getRandomPointInRing(innerRadius, outerRadius);
                    point.radius = Math.random() * 30; // 随机生成点的半径,范围在5到15之间
                    let isValid = true;
                    for (let i = 0; i < points.length; i++) {
                        if (distance(point, points[i]) < point.radius + points[i].radius) {
                            isValid = false;
                            break;
                        }
                    }
                    if (isValid) {
                        points.push(point);
                        const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
                        ctx.fillStyle = gradient;
                        ctx.beginPath();
                        ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
                        ctx.fill();

                        // 绘制点的边框
                        ctx.strokeStyle = '#f00';
                        ctx.lineWidth = 2;
                        ctx.stroke();

                        // 在点的中心添加文本
                        ctx.fillStyle = 'black';
                        ctx.font = '12px Arial';
                        ctx.textAlign = 'center';
                        ctx.textBaseline = 'middle';
                        ctx.fillText(Math.round(point.radius), point.x, point.y);
                    }
                }
            }

            // 在圆环内绘制 20 个随机点
            drawRandomPoints(30);
    </script>
  </body>
</html>

二、动态效果

完整代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
  </head>
  <body>
    <canvas height="500" id="myCanvas" width="500">
    </canvas>
    <script type="text/javascript">
      // 获取 canvas 元素和绘图上下文
            const canvas = document.getElementById('myCanvas');
            const ctx = canvas.getContext('2d');

            // 圆心坐标和半径
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const outerRadius = 200;
            const innerRadius = 100;

            // 在圆环范围内随机分布点
            function getRandomPointInRing(innerRadius, outerRadius) {
                const angle = Math.random() * 2 * Math.PI;
                const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
                const x = centerX + radius * Math.cos(angle);
                const y = centerY + radius * Math.sin(angle);
                return { x, y };
            }

            // 检查两个点之间的距离
            function distance(point1, point2) {
                return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
            }

            // 创建镜像渐变
            function createMirrorGradient(ctx, x, y, radius) {
                const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
                gradient.addColorStop(0, 'rgba(255, 0, 0, 1)');
                gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
                gradient.addColorStop(1, 'rgba(255, 0, 0, 1)');
                return gradient;
            }

            // 绘制点
            function drawPoint(point) {
                const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
                ctx.fill();

                // 绘制点的边框
                ctx.strokeStyle = 'black';
                ctx.lineWidth = 2;
                ctx.stroke();

                // 在点的中心添加文本
                ctx.fillStyle = 'black';
                ctx.font = '12px Arial';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(Math.round(point.radius), point.x, point.y);
            }

            // 绘制随机点,确保点与点之间没有交集
            function drawRandomPoints(numPoints) {
                const points = [];
                while (points.length < numPoints) {
                    const point = getRandomPointInRing(innerRadius, outerRadius);
                    point.radius = Math.random() * 30; // 随机生成点的半径,范围在5到15之间
                    let isValid = true;
                    for (let i = 0; i < points.length; i++) {
                        if (distance(point, points[i]) < point.radius + points[i].radius) {
                            isValid = false;
                            break;
                        }
                    }
                    if (isValid) {
                        point.originalY = point.y;
                        point.offset = Math.random() * 10; // 随机生成浮动幅度
                        points.push(point);
                    }
                }
                return points;
            }

            // 动画更新点的位置
            function updatePoints(points) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                // 绘制圆环
                ctx.beginPath();
                ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
                ctx.stroke();

                ctx.beginPath();
                ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
                ctx.stroke();

                points.forEach(point => {
                    point.y = point.originalY + Math.sin(Date.now() / 500) * point.offset;
                    drawPoint(point);
                });

                requestAnimationFrame(() => updatePoints(points));
            }

            // 初始化并开始动画
            const points = drawRandomPoints(30);
            updatePoints(points);
    </script>
  </body>
</html>

三、文本跟随点的大小变化

image.webp

完整代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
  </head>
<body>
<canvas height="500" id="myCanvas" width="500">

<script type="text/javascript">
    // 获取 canvas 元素和绘图上下文
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');

    // 圆心坐标和半径
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;
    const outerRadius = 200;
    const innerRadius = 100;

    // 在圆环范围内随机分布点
    function getRandomPointInRing(innerRadius, outerRadius) {
        const angle = Math.random() * 2 * Math.PI;
        const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
        const x = centerX + radius * Math.cos(angle);
        const y = centerY + radius * Math.sin(angle);
        return { x, y };
    }

    // 检查两个点之间的距离
    function distance(point1, point2) {
        return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
    }

    // 创建镜像渐变
    function createMirrorGradient(ctx, x, y, radius) {
        const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
        gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
        gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
        gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');
        return gradient;
    }

    // 绘制点
    function drawPoint(point) {
        const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
        ctx.fillStyle = gradient;
        ctx.beginPath();
        ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
        ctx.fill();

        // 绘制点的边框
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 2;
        ctx.stroke();

        // 在点的中心添加文本,字体大小与点的半径成比例
        ctx.fillStyle = 'black';
        const fontSize = point.radius * 0.8; // 字体大小与点的半径成比例
        ctx.font = `${fontSize}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('Text', point.x, point.y);
    }

    // 绘制随机点,确保点与点之间没有交集
    function drawRandomPoints(numPoints) {
        const points = [];
        while (points.length < numPoints) {
            const point = getRandomPointInRing(innerRadius, outerRadius);
            point.radius = Math.random() * 10 + 5; // 随机生成点的半径,范围在5到15之间
            let isValid = true;
            for (let i = 0; i < points.length; i++) {
                if (distance(point, points[i]) < point.radius + points[i].radius) {
                    isValid = false;
                    break;
                }
            }
            if (isValid) {
                point.originalY = point.y;
                point.offset = Math.random() * 20 - 10; // 随机生成浮动幅度
                points.push(point);
            }
        }
        return points;
    }

    // 动画更新点的位置
    function updatePoints(points) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 绘制圆环
        ctx.beginPath();
        ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
        ctx.stroke();

        ctx.beginPath();
        ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
        ctx.stroke();

        points.forEach(point => {
            point.y = point.originalY + Math.sin(Date.now() / 500) * point.offset;
            drawPoint(point);
        });

        requestAnimationFrame(() => updatePoints(points));
    }

    // 初始化并开始动画
    const points = drawRandomPoints(20);
    updatePoints(points);

</script>
</body>
</html>

四、缓存第一次位置

完整代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
  </head>
<body>
<canvas height="500" id="myCanvas" width="500">

<script type="text/javascript">
    // 获取 canvas 元素和绘图上下文
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');

    // 圆心坐标和半径
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;
    const outerRadius = 200;
    const innerRadius = 100;

    // 在圆环范围内随机分布点
    function getRandomPointInRing(innerRadius, outerRadius) {
        const angle = Math.random() * 2 * Math.PI;
        const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
        const x = centerX + radius * Math.cos(angle);
        const y = centerY + radius * Math.sin(angle);
        return { x, y };
    }

    // 检查两个点之间的距离
    function distance(point1, point2) {
        return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
    }

    // 创建镜像渐变
    function createMirrorGradient(ctx, x, y, radius) {
        const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
        gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
        gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
        gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');
        return gradient;
    }

    // 绘制点
    function drawPoint(point) {
        const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
        ctx.fillStyle = gradient;
        ctx.beginPath();
        ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
        ctx.fill();

        // 绘制点的边框
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 2;
        ctx.stroke();

        // 在点的中心添加文本,字体大小与点的半径成比例
        ctx.fillStyle = 'black';
        const fontSize = point.radius * 0.8; // 字体大小与点的半径成比例
        ctx.font = `${fontSize}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('Text', point.x, point.y);
    }

    // 绘制随机点,确保点与点之间没有交集
    function drawRandomPoints(numPoints) {
        const points = [];
        while (points.length < numPoints) {
            const point = getRandomPointInRing(innerRadius, outerRadius);
            point.radius = Math.random() * 10 + 5; // 随机生成点的半径,范围在5到15之间
            let isValid = true;
            for (let i = 0; i < points.length; i++) {
                if (distance(point, points[i]) < point.radius + points[i].radius) {
                    isValid = false;
                    break;
                }
            }
            if (isValid) {
                point.originalY = point.y;
                point.offset = Math.random() * 20 - 10; // 随机生成浮动幅度
                points.push(point);
            }
        }
        return points;
    }

    // 动画更新点的位置
    function updatePoints(points) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 绘制圆环
        ctx.beginPath();
        ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
        ctx.stroke();

        ctx.beginPath();
        ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
        ctx.stroke();

        points.forEach(point => {
            point.y = point.originalY + Math.sin(Date.now() / 500) * point.offset;
            drawPoint(point);
        });

        requestAnimationFrame(() => updatePoints(points));
    }

    // 初始化并开始动画
    function init() {
        if (!localStorage.getItem('cachedPoints')) {
            localStorage.setItem('cachedPoints', JSON.stringify(drawRandomPoints(20)))
        }
        updatePoints(JSON.parse(localStorage.getItem('cachedPoints')));
    }

    init();
</script>
</body>
</html>

五、自定义canvas宽高随机方向浮动

完整代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
  </head>
  <body>
    <canvas id="myCanvas">
    </canvas>
    <script type="text/javascript">
      // 获取 canvas 元素和绘图上下文
            const canvas = document.getElementById('myCanvas');

            // 设置 canvas 的宽高
            const canvasWidth = 800;  // 自定义宽度
            const canvasHeight = 600; // 自定义高度
            canvas.width = canvasWidth;
            canvas.height = canvasHeight;

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

            // 圆心坐标和半径
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const outerRadius = 200;
            const innerRadius = 100;

            // 用于缓存随机点位置的数据
            let cachedPoints = null;

            // 在圆环范围内随机分布点
            function getRandomPointInRing(innerRadius, outerRadius) {
                const angle = Math.random() * 2 * Math.PI;
                const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
                const x = centerX + radius * Math.cos(angle);
                const y = centerY + radius * Math.sin(angle);
                return { x, y };
            }

            // 检查两个点之间的距离
            function distance(point1, point2) {
                return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
            }

            // 创建镜像渐变
            function createMirrorGradient(ctx, x, y, radius) {
                const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
                gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
                gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
                gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');
                return gradient;
            }

            // 绘制点
            function drawPoint(point) {
                const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
                ctx.fill();

                // 绘制点的边框
                ctx.strokeStyle = 'black';
                ctx.lineWidth = 2;
                ctx.stroke();

                // 在点的中心添加文本,字体大小与点的半径成比例
                ctx.fillStyle = 'black';
                const fontSize = point.radius * 0.8; // 字体大小与点的半径成比例
                ctx.font = `${fontSize}px Arial`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText('Text', point.x, point.y);
            }

            // 绘制随机点,确保点与点之间没有交集
            function drawRandomPoints(numPoints) {
                const points = [];
                while (points.length < numPoints) {
                    const point = getRandomPointInRing(innerRadius, outerRadius);
                    point.radius = Math.random() * 30; // 随机生成点的半径,范围在5到15之间
                    let isValid = true;
                    for (let i = 0; i < points.length; i++) {
                        if (distance(point, points[i]) < point.radius + points[i].radius) {
                            isValid = false;
                            break;
                        }
                    }
                    if (isValid) {
                        point.targetX = point.x;
                        point.targetY = point.y;
                        points.push(point);
                    }
                }
                return points;
            }

            // 动画更新点的位置
            function updatePoints(points) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                // 绘制圆环
                ctx.beginPath();
                ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
                ctx.stroke();

                ctx.beginPath();
                ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
                ctx.stroke();

                points.forEach(point => {
                    // 如果点已经到达目标位置,生成一个新的目标位置
                    if (distance(point, { x: point.targetX, y: point.targetY }) < 1) {
                        const angle = Math.random() * 2 * Math.PI;
                        const moveDistance = Math.random() * 10; // 随机生成移动的距离,范围在10到30之间
                        point.targetX = point.x + moveDistance * Math.cos(angle);
                        point.targetY = point.y + moveDistance * Math.sin(angle);
                    }

                    // 计算移动的方向
                    const dx = point.targetX - point.x;
                    const dy = point.targetY - point.y;
                    const distanceToTarget = Math.sqrt(dx * dx + dy * dy);
                    const moveX = (dx / distanceToTarget) * 0.1; // 每帧移动的距离
                    const moveY = (dy / distanceToTarget) * 0.1; // 每帧移动的距离

                    // 更新点的位置
                    point.x += moveX;
                    point.y += moveY;

                    // 检查点是否超出了圆环范围,如果超出则反弹
                    const dxCenter = point.x - centerX;
                    const dyCenter = point.y - centerY;
                    const distanceFromCenter = Math.sqrt(dxCenter * dxCenter + dyCenter * dyCenter);

                    if (distanceFromCenter + point.radius > outerRadius) {
                        // 点超出外圆,反弹
                        const angle = Math.atan2(dyCenter, dxCenter);
                        point.x = centerX + (outerRadius - point.radius) * Math.cos(angle);
                        point.y = centerY + (outerRadius - point.radius) * Math.sin(angle);
                        point.targetX = point.x - moveX;
                        point.targetY = point.y - moveY;
                    } else if (distanceFromCenter - point.radius < innerRadius) {
                        // 点超出内圆,反弹
                        const angle = Math.atan2(dyCenter, dxCenter);
                        point.x = centerX + (innerRadius + point.radius) * Math.cos(angle);
                        point.y = centerY + (innerRadius + point.radius) * Math.sin(angle);
                        point.targetX = point.x - moveX;
                        point.targetY = point.y - moveY;
                    }

                    // 碰撞检测和处理
                    points.forEach(otherPoint => {
                        if (point !== otherPoint && distance(point, otherPoint) < point.radius + otherPoint.radius) {
                            const overlap = point.radius + otherPoint.radius - distance(point, otherPoint);
                            const angle = Math.atan2(point.y - otherPoint.y, point.x - otherPoint.x);
                            point.x += overlap * Math.cos(angle) / 2;
                            point.y += overlap * Math.sin(angle) / 2;
                            otherPoint.x -= overlap * Math.cos(angle) / 2;
                            otherPoint.y -= overlap * Math.sin(angle) / 2;
                            point.targetX = point.x - moveX;
                            point.targetY = point.y - moveY;
                            otherPoint.targetX = otherPoint.x + moveX;
                            otherPoint.targetY = otherPoint.y + moveY;
                        }
                    });

                    drawPoint(point);
                });

                requestAnimationFrame(() => updatePoints(points));
            }

            // 初始化并开始动画
            function init() {
                if (!cachedPoints) {
                    cachedPoints = drawRandomPoints(20);
                }
                updatePoints(cachedPoints);
            }

            init();
    </script>
  </body>
</html>
PS:写作不易,如要转裁,请标明转载出处。
%{ comment.page.total }条评论

猜你想看

微信小程序:前端开发宝典

最近文章
工具操作
  • 内容截图
  • 全屏
登录
注册
回顶部