Canvas 实现元球融合效果🔮

写在最前

近期使用Canvas实现一款关于"元球融合"的效果,本想是用 threejs 蹭一波跨窗口球体融合特效的热度,但发现已经为时已晚了。索性就用Canvas实现元球融合效果吧,冲!

原理

原理非常非常简单,只需求出这4个点的位置然后画出三阶贝塞尔曲线就行。

步骤

  1. 初始化画布
  2. 画圆
  3. 获取圆上点P1坐标
  4. 获取圆过点P1切线上的一点P3
  5. 画贝塞尔曲线

初始化画布

js 复制代码
let c1 = { x: 300, y: 430, radius: 80 };
let c2 = { x: 0, y: 0, radius: 80 };

let canvas = document.querySelector("#canvas") as HTMLCanvasElement;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context = canvas.getContext("2d")!;

setCanvasBg();
js 复制代码
/**
 * 设置背景色
 */

function setCanvasBg() {
    context.fillStyle = "#000000";
    context.fillRect(0, 0, canvas.width, canvas.height);
}

画圆

封装画圆函数

js 复制代码
/**
 * 画圆
 * @param x
 * @param y
 * @param radius
 */

function drawCircle(x, y, radius) {
    context.save();
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI);
    context.closePath();
    context.stroke();
    context.fillStyle = "#FFFFFF";
    context.fill();
    context.restore();
}

我们写一个draw函数用于绘制

js 复制代码
function draw(){
   // 画圆
    drawCircle(c1.x, c1.y, c1.radius);
    drawCircle(c2.x, c2.y, c2.radius);
   
   // 画贝塞尔曲线 
}

获取圆上坐标

想要得到较为丝滑的效果,圆上点坐标与切线点坐标是需要可变化的。

我们可以看到当小球在同一水平上,两小球距离越远,在圆上的点是会稍微往两球中间挪动一段距离。

所以我们需要根据距离去设置一个最大高度的比例变量

点会在Max和MIN的方向上移动

并且两个圆上的点会根据两球位置进行一个角度的旋转,这个角度就是两球圆心的角度

所以我们将两球圆心的坐标相减,使用 Math.atan2 API可以求出角度

所以我们很容易得到以下获取圆上坐标函数

let radian = Math.atan2(c2.y - c1.y, c2.x - c1.x);

js 复制代码
/**
 * 获取圆上坐标
 * @param {*} maxHeightPercent
 */

function getCirclePoint(circle, maxHeightPercent, isReverse, radian) {
    let { x: cx, y: cy, radius } = circle;

    let thetaInRadians = Math.asin(maxHeightPercent);
    let x = Math.cos(thetaInRadians + radian) * radius;
    let y = Math.sin(thetaInRadians + radian) * radius;
    return {
        x: cx + (isReverse ? -x : x),
        y: cy + y,
    };
}

这样线上点就求出来了

获取圆过点切线上的坐标

设定切线上坐标点为T,只需要求出 线段PD与线段DT的长度。

PT 斜边 是常量我们设置的,且 ∠PTD = ∠TVC ,∠TVC = 180 - PCV - CPV; 已知切线VP 与 CP夹角为90度;则∠PVC = 90 - ∠PCV; PCV依然可以用 Math.atan2 将P,C两点带入则可以求出。

所以我们可以很轻松的得出下面函数

js 复制代码
/**
 * 获取切线上的一点
 */

function getTangentPoi(center, point, isReverse) {
    let distance = getCircleDistance();
    let length = (isReverse ? -distance : distance) / 4;

    const r = Math.atan2(point.y - center.y, point.x - center.x);
    let angle = 90 + (r / Math.PI) * 180;
    const radian = (angle / 180) * Math.PI;

    let x = Math.cos(radian) * length;
    let y = Math.sin(radian) * length;

    return {
        x: point.x + x,
        y: point.y + y,
    };
}

中间的4个点就是切线上的点

画贝塞尔曲线

canvas有贝塞尔曲线API,直接用就好了

js 复制代码
/**
 * canvas画贝塞尔曲线
 */

function drawBezierPath(maxHeightPercent) {
    let radian = Math.atan2(c2.y - c1.y, c2.x - c1.x);

    let p1Top = getCirclePoint(c1, -maxHeightPercent, false, radian);
    let p1Bottom = getCirclePoint(c1, maxHeightPercent, false, radian);
    let p2Top = getCirclePoint(c2, -maxHeightPercent, true, -radian);
    let p2Bottom = getCirclePoint(c2, maxHeightPercent, true, -radian);


    let c1tangentTop = getTangentPoi(getCircleCenterPoi(c1), p1Top, false);
    let c1tangentBottom = getTangentPoi(getCircleCenterPoi(c1), p1Bottom, true);
    let c2tangentTop = getTangentPoi(getCircleCenterPoi(c2), p2Top, true);
    let c2tangentBottom = getTangentPoi(getCircleCenterPoi(c2), p2Bottom, false);


    context.beginPath();
    context.moveTo(p1Top.x, p1Top.y);
    context.bezierCurveTo(
        c1tangentTop.x,
        c1tangentTop.y,
        c2tangentTop.x,
        c2tangentTop.y,
        p2Top.x,
        p2Top.y
    );
    context.lineTo(p2Bottom.x, p2Bottom.y);
    context.bezierCurveTo(
        c2tangentBottom.x,
        c2tangentBottom.y,
        c1tangentBottom.x,
        c1tangentBottom.y,
        p1Bottom.x,
        p1Bottom.y
    );
    context.strokeStyle = "1";
    context.lineTo(p1Top.x, p1Top.y);
    context.closePath();

    context.stroke();
    context.fillStyle = "#FFFFFF";
    context.fill();
}

至此 元球融合就算完成了

结尾

喜欢的话帮忙点个赞 + 关注吧,将持续更新 Threejs 相关的文章,谢谢浏览!

源码地址

往期链接:

🔗# Threejs 实现虚拟摇杆遨游星空 ✨✨

🔗# Threejs 让人眼前一亮的隧道穿越 🌌🌌🌌

🔗# 实现抛物线跳跃交互底部导航栏🎈🎈🎈

🔗# Threejs 中秋佳节感受闽南名俗 | 中秋博饼🥮🥮🥮

🔗# Threejs glTF编辑器功能详解🎯🎯🎯

🔗# Threejs 实现梦幻频谱可视化🧿🧿🧿

相关推荐
让开,我要吃人了2 小时前
HarmonyOS开发实战(5.0)实现二楼上划进入首页效果详解
前端·华为·程序员·移动开发·harmonyos·鸿蒙·鸿蒙系统
everyStudy3 小时前
前端五种排序
前端·算法·排序算法
甜兒.4 小时前
鸿蒙小技巧
前端·华为·typescript·harmonyos
Jiaberrr7 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy8 小时前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白8 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、8 小时前
Web Worker 简单使用
前端
web_learning_3218 小时前
信息收集常用指令
前端·搜索引擎
tabzzz8 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百8 小时前
Vuex详解
前端·javascript·vue.js