vue 圆形 水波_使用贝塞尔曲线实现水波球

前言

最近需求中要实现一个水波球的效果,于是在网上查找了一番。发现网上的实现方式大多是用正余弦公式去绘制一个个点,然后再连接成曲线。个人感觉使用正余弦的方式有点麻烦,Canvas中有提供贝塞尔曲线函数可以画出平滑的曲线来模拟水波,于是自己动手实现了一下。效果如下https://codepen.io/geeknoble/pen/PooQzwQ

实现思路

水波球由一个圆和波浪区域组成,重点在于波浪区域的实现。波浪区域可以先画一个长方形,然后将上面的边用一条贝塞尔曲线链接,再将该区域与圆进行裁剪就能得到一个水波球图形,如图所示:

裁剪后

首先用Canvas画一个以中心为圆心,1/2宽为半径的圆。

function drawCircle(ctx, mW, color) {

ctx.beginPath();

ctx.strokeStyle = color;

// 以中心为圆点,1/2边长为半径

ctx.arc(mW / 2, mW / 2, mW / 2 - 1, 0, 2 * Math.PI);

ctx.stroke();

ctx.beginPath();

ctx.arc(mW / 2, mW / 2, mW / 2 - 2, 0, 2 * Math.PI);

ctx.clip();

}

var canvas1 = document.getElementById('canvas')

var mW = canvas1.clientWidth;

// console.log(mW);

// 设置Canvas元素的高

canvas1.style.height = mW;

// 设置Canvas画布的宽高

canvas1.width = canvas1.height = mW;

drawCircle(ctx1, mW, '#1a4768');

然后用贝塞尔函数画曲线,贝塞尔曲线有三次函数和二次函数。如果画静态的水波球那么用三次贝塞尔曲线就可以了,两个控制点一个向上,一个向下就能画出一个类似水波的曲线。但如果是有动画的就不行了,这样画出的曲线1/4处与1/2处并不对称,动起来会感觉很变扭。带动画的可以画两条二次贝塞尔曲线,这样画出前后都对称的曲线。

function drawCurve(ctx, mW, color, wav, dY) {

ctx.save();

ctx.beginPath();

ctx.moveTo(0, mW);

ctx.lineTo(0, dY);

// 三次贝塞尔曲线画一个就可以了

//ctx.bezierCurveTo(mW / 4, dY - wav, mW * 3/4, dY + wav, mW, dY)

// 二次贝塞尔曲线需要画两条

ctx.quadraticCurveTo(mW / 4, dY - wav, mW / 2, dY);

ctx.lineTo(mW / 2, dY)

ctx.quadraticCurveTo((mW * 3) / 4, dY + wav, mW, dY);

ctx.lineTo(mW, mW);

ctx.lineTo(0, mW);

ctx.fillStyle = color;

ctx.fill();

ctx.restore();

}

...

drawCircle(ctx1, mW, '#1a4768');

drawCurve(ctx2, mW, '#1c86d1', wave, mW - mW * rate);

(三次贝塞尔曲线画出的)

(二次贝塞尔曲线画出的)

动画实现

静态的波浪有了,之后就是让它动起来,只要将波浪区域水平移动起来就可以看到动态的效果了。这里我用离屏Canvas的方式来绘制动画,因为这样实现比较方便,并且性能开销小。实现方式就是一个Canvas画圆,再建一个Canvas画波浪区域,用drawImage方法不断绘制。

var canvas1 = document.getElementById('canvas')

var mW = canvas1.clientWidth;

// console.log(mW);

// 设置Canvas元素的高

canvas1.style.height = mW;

// 设置Canvas画布的宽高

canvas1.width = canvas1.height = mW;

var canvas2 = document.createElement('canvas'),

ctx2 = canvas2.getContext('2d');

canvas2.width = mW;

canvas2.height = mW;

drawCircle(ctx1, mW, '#1a4768');

drawCurve(ctx2, mW, '#1c86d1', wave, mW - mW * rate);

function animation() {

ctx1.clearRect(0, 0, mW, mW)

// 这里要画两个以免出现空白

ctx1.drawImage(canvas2, x, 0)

ctx1.drawImage(canvas2, x - mW , 0)

// 边界判断

x >= (mW - speed) ? x = 0 : x += speed

requestAnimationFrame(animation)

}

animation()

现在看起来还不错,但仔细观察可以发现球内只能显示一个周期的曲线,我想让波浪更长一点怎么弄呢?这里可以用drawImage的一个特性实现,drawImage3个参数情况下被裁剪的图片的尺寸是该图片本身的尺寸,drawImage5个参数中后两个参数是可以控制被裁剪图片的大小,实际上ctx.drawImage(canvas, 0, 0)等价于ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height),后两个参数可以对裁剪的图片进行缩放。那么把x加大波浪就会被拉长。

...

var flat = 300

function animation() {

ctx1.clearRect(0, 0, mW, mW)

// 这里要画两个以免出现空白

ctx1.drawImage(canvas2, x, 0, mW + flat, mW)

ctx1.drawImage(canvas2, x - mW - flat, 0, mW + flat, mW)

// 边界判断

x >= (mW - speed + flat) ? x = 0 : x += speed

requestAnimationFrame(animation)

}

animation()

如果想再加一条波浪,那么再加一个Canvas就行了,再画上不同的颜色,并在x轴上加个偏差值。

function animation() {

ctx1.clearRect(0, 0, mW, mW)

ctx1.drawImage(canvas2, x, 0, mW + flat, mW)

ctx1.drawImage(canvas2, x - mW - flat, 0, mW + flat, mW)

ctx1.drawImage(canvas3, x + distance, 0, mW + flat, mW)

ctx1.drawImage(canvas3, x - mW + distance - flat, 0, mW + flat, mW)

// 边界判断

x >= (mW - speed + flat) ? x = 0 : x += speed

requestAnimationFrame(animation)

}

animation()

作者:没落的宅男贵族


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部