近期在做一个气球挂件的特效需求,值此契机,来跟大家分享一下如何利用canvas以及对应的数学知识构造一个栩栩如生的气球。
10年积累的成都网站建设、成都做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有洞头免费网站建设让你可以放心的选择与我们合作。
在实现这个看似是圆鼓鼓的气球之前,先了解一下其实现思路,主要分为以下几个部分:
气球.PNG
对于这样的气球的球体部分,大家都有什么好的实现思路的?相信大家肯定会有多种多样的实现方案,我也是在看到某位大佬的效果后,感受到了利用四个三次贝塞尔曲线实现这个效果的妙处。为了看懂后续代码,先了解一下三次贝塞尔曲线的原理。(注:引用了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]
上述已经列出了三次贝塞尔曲线的效果图和公式,但是通过这个怎么跟我们的气球挂上钩呢?下面通过几张图就理解了:
如上图所示,就是实现整个气球球体的思路,具体解释如下所示:
- 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();
- }
口子部分可以简化为一个三角形,效果如下所示:
- function draw() {
- const canvas = document.getElementById('canvas');
- const ctx = canvas.getContext('2d');
- ……
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(0, 120);
- ctx.lineTo(-5, 130);
- ctx.lineTo(5, 130);
- ctx.closePath();
- ctx.stroke();
- ctx.restore();
- }
线实现的比较简单,就用了一段直线实现
- function draw() {
- const canvas = document.getElementById('canvas');
- const ctx = canvas.getContext('2d');
- ……
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(0, 120);
- ctx.lineTo(0, 300);
- ctx.stroke();
- ctx.restore();
- }
气球部分的填充用了圆形渐变效果,相比于纯色来说更加漂亮一些。
- 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;
- }
上述流程已经将一个静态的气球部分绘制完毕了,要想实现动画效果只需要利用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;
- }
本文名称:用Canvas实现一个大气球送给你
网址分享:http://www.stwzsj.com/qtweb/news30/13880.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联