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 实现梦幻频谱可视化🧿🧿🧿

相关推荐
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test4 小时前
js下载excel示例demo
前端·javascript·excel
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json