您现在的位置是:亿华云 > IT科技类资讯
用Canvas实现一个大气球送给你
亿华云2025-10-04 03:02:37【IT科技类资讯】5人已围观
简介一、背景近期在做一个气球挂件的特效需求,值此契机,来跟大家分享一下如何利用canvas以及对应的数学知识构造一个栩栩如生的气球。二、实现在实现这个看似是圆鼓鼓的气球之前,先了解一下其实现思路,主要分为
一、大气背景
近期在做一个气球挂件的球送特效需求,值此契机,大气来跟大家分享一下如何利用canvas以及对应的球送数学知识构造一个栩栩如生的气球。
二、大气实现
在实现这个看似是球送圆鼓鼓的气球之前,先了解一下其实现思路,大气主要分为以下几个部分:
实现球体部分; 实现气球口子部分; 实现气球的球送线部分; 进行颜色填充; 实现动画;气球.PNG
2.1 球体部分实现
对于这样的气球的球体部分,大家都有什么好的大气实现思路的?相信大家肯定会有多种多样的实现方案,我也是球送在看到某位大佬的效果后,感受到了利用四个三次贝塞尔曲线实现这个效果的大气妙处。云南idc服务商为了看懂后续代码,球送先了解一下三次贝塞尔曲线的大气原理。(注:引用了CSDN上某位大佬的球送文章,写的大气很好,下图引用于此)
三次贝塞尔曲线.gif
在上图中P0为起始点、P3为终止点,P1和P2为控制点,其最终的曲线公式如下所示:
B(t)=(1?t)^3 * P0+3t(1?t)^2 * P1+3t ^ 2(1?t) * P2+t ^ 3P3, t∈[0,1]上述已经列出了三次贝塞尔曲线的效果图和公式,但是通过这个怎么跟我们的气球挂上钩呢?下面通过几张图就理解了:
如上图所示,就是实现整个气球球体的思路,具体解释如下所示:
A图中起始点为p1,终止点为p2,控制点为c1、c2,让两个控制点重合,绘制出的效果并不是高防服务器很像气球的一部分,此时就要通过改变控制点来改变其外观; 改变控制点c1、c2,c1中y值不变,减小x值;c2中x值不变,增大y值(注意canvas中坐标方向即可),改变后就得到了图B的效果,此时就跟气球外观很像了; 紧接着按照这个方法就可以实现整个的气球球体部分的外观。 function draw() { const canvas = document.getElementById(canvas); const ctx = canvas.getContext(2d); ctx.translate(250, 250); drawCoordiante(ctx); ctx.save(); ctx.beginPath(); ctx.moveTo(0, -80); ctx.bezierCurveTo(45, -80, 80, -45, 80, 0); ctx.bezierCurveTo(80, 85, 45, 120, 0, 120); ctx.bezierCurveTo(-45, 120, -80, 85, -80, 0); ctx.bezierCurveTo(-80, -45, -45, -80, 0, -80); ctx.stroke(); ctx.restore(); } function drawCoordiante(ctx) { ctx.beginPath(); ctx.moveTo(-120, 0); ctx.lineTo(120, 0); ctx.moveTo(0, -120); ctx.lineTo(0, 120); ctx.closePath(); ctx.stroke(); }2.2 口子部分实现
口子部分可以简化为一个三角形,效果如下所示:
2.3 线部分实现
线实现的比较简单,就用了一段直线实现
2.4 进行填充
气球部分的填充用了圆形渐变效果,相比于纯色来说更加漂亮一些。
function draw() { const canvas = document.getElementById(canvas); const ctx = canvas.getContext(2d); ctx.fillStyle = getBalloonGradient(ctx, 0, 0, 80, 210); …… } function getBalloonGradient(ctx, x, y, r, hue) { const grd = ctx.createRadialGradient(x, y, 0, x, y, r); grd.addColorStop(0, hsla( + hue + , 100%, 65%, .95)); grd.addColorStop(0.4, hsla( + hue + , 100%, 45%, .85)); grd.addColorStop(1, hsla( + hue + , 100%, 25%, .80)); return grd; }2.5 动画效果及整体代码
上述流程已经将一个静态的气球部分绘制完毕了,要想实现动画效果只需要利用requestAnimationFrame函数不断循环调用即可实现。下面直接抛出整体代码,方便同学们观察效果进行调试,整体代码如下所示:
let posX = 225; let posY = 300; let points = getPoints(); draw(); function draw() { const canvas = document.getElementById(canvas); const ctx = canvas.getContext(2d); ctx.clearRect(0, 0, canvas.width, canvas.height); if (posY < -200) { posY = 300; posX += 300 * (Math.random() - 0.5); points = getPoints(); } else { posY -= 2; } ctx.save(); ctx.translate(posX, posY); drawBalloon(ctx, points); ctx.restore(); window.requestAnimationFrame(draw); } function drawBalloon(ctx, points) { ctx.scale(points.scale, points.scale); ctx.save(); ctx.fillStyle = getBalloonGradient(ctx, 0, 0, points.R, points.hue); // 绘制球体部分 ctx.moveTo(points.p1.x, points.p1.y); ctx.bezierCurveTo(points.pC1to2A.x, points.pC1to2A.y, points.pC1to2B.x, points.pC1to2B.y, points.p2.x, points.p2.y); ctx.bezierCurveTo(points.pC2to3A.x, points.pC2to3A.y, points.pC2to3B.x, points.pC2to3B.y, points.p3.x, points.p3.y); ctx.bezierCurveTo(points.pC3to4A.x, points.pC3to4A.y, points.pC3to4B.x, points.pC3to4B.y, points.p4.x, points.p4.y); ctx.bezierCurveTo(points.pC4to1A.x, points.pC4to1A.y, points.pC4to1B.x, points.pC4to1B.y, points.p1.x, points.p1.y); // 绘制气球钮部分 ctx.moveTo(points.p3.x, points.p3.y); ctx.lineTo(points.knowA.x, points.knowA.y); ctx.lineTo(points.knowB.x, points.knowB.y); ctx.fill(); ctx.restore(); // 绘制线部分 ctx.save(); ctx.strokeStyle = #000000; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(points.p3.x, points.p3.y); ctx.lineTo(points.lineEnd.x, points.lineEnd.y); ctx.stroke(); ctx.restore(); } function getPoints() { const offset = 35; return { scale: 0.3 + Math.random() / 2, hue: Math.random() * 255, R: 80, p1: { x: 0, y: -80 }, pC1to2A: { x: 80 - offset, y: -80 }, pC1to2B: { x: 80, y: -80 + offset }, p2: { x: 80, y: 0 }, pC2to3A: { x: 80, y: 120 - offset }, pC2to3B: { x: 80 - offset, y: 120 }, p3: { x: 0, y: 120 }, pC3to4A: { x: -80 + offset, y: 120 }, pC3to4B: { x: -80, y: 120 - offset }, p4: { x: -80, y: 0 }, pC4to1A: { x: -80, y: -80 + offset }, pC4to1B: { x: -80 + offset, y: -80 }, knowA: { x: -5, y: 130 }, knowB: { x: 5, y: 130 }, lineEnd: { x: 0, y: 250 } }; } function getBalloonGradient(ctx, x, y, r, hue) { const grd = ctx.createRadialGradient(x, y, 0, x, y, r); grd.addColorStop(0, hsla( + hue + , 100%, 65%, .95)); grd.addColorStop(0.4, hsla( + hue + , 100%, 45%, .85)); grd.addColorStop(1, hsla( + hue + , 100%, 25%, .80)); return grd; } 源码下载很赞哦!(2)
相关文章
- a、变更前的公司证件扫描件(代码证或者营业执照)及联系人身份证复印件、变更后的公司证件扫描件(代码证或者营业执照)及新的联系人身份证复印件;身份证复印件需本人签名,公司证件复印件需加盖公章。
- 如何在国外进行域名注册?有什么方法?
- 如何防止注册域名被骗?需要怎么做?
- 解密Elasticsearch:深入探究这款搜索和分析引擎
- 记住那句话,域名向来不属于任何人,谁先买就归谁,购买期过后,域名又不再属于任何人。
- 关于聚合根,领域事件的那点事---深入浅出理解DDD
- .net.cn是什么域名?.net.cn在域名圈咋样?
- 国内注册cm域名怎么样?cm域名有什么价值?
- 小白注册网站域名该怎么办?有什么步骤?
- 高考结束,用 Python 来分析下哪里的高考是地狱级难度