使用贝塞尔曲线实现水波球

2019-11-18 admin

前言

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

实现思路

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

裁剪后 Image 10.png

首先用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');

Image 1.png

然后用贝塞尔函数画曲线,贝塞尔曲线有三次函数和二次函数。如果画静态的水波球那么用三次贝塞尔曲线就可以了,两个控制点一个向上,一个向下就能画出一个类似水波的曲线。但如果是有动画的就不行了,这样画出的曲线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()

image5.gif

现在看起来还不错,但仔细观察可以发现球内只能显示一个周期的曲线,我想让波浪更长一点怎么弄呢?这里可以用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()

image6.gif 如果想再加一条波浪,那么再加一个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()

image7.gif

[转载]原文链接:https://segmentfault.com/a/1190000021043704

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-79403.html

文章标题:使用贝塞尔曲线实现水波球

相关文章
使用jspdf生成pdf报表
由于前台html已经动态生成报表,而且,前台有一个功能,一个date range组件,当你拖动的时候,报表会在不提交到后台的情况下动态变化。 因此需要用到js生成生报表: 用到的组件: jquery.js jspdf.js canvg.js...
2017-03-25
javaScript+turn.js实现图书翻页效果实例代码
为了实现图书翻页的效果我们在网上可以看到很多教程 在这里推荐turn.js 网上的turn.js 有api 不过是英文的  很多人看起来不方便 .关于代码也是奇形怪状在这里我将详细讲解如何使用turn.js实现翻页效果 ,本篇文章只是讲解 ...
2017-03-16
梳理前端开发使用eslint-prettier检查和格式化代码
问题痛点 在团队的项目开发过程中,代码维护所占的时间比重往往大于新功能的开发。因此编写符合团队编码规范的代码是至关重要的,这样做不仅可以很大程度地避免基本语法错误,也保证了代码的可读性。 对于代码版本管理系统(svn 和 git或者其他)...
2018-05-07
js实现手机拍照上传功能
在前段时间的项目开发中,用到了拍照上传的地方,后来发现了最为简单的一种方法,现总结如下: <form id="form" method="post" action="http:&#x2...
2017-03-06
jQuery中DOM树操作之使用反向插入方法实例分析
本文实例讲述了jQuery中DOM树操作之使用反向插入方法。分享给大家供大家参考。具体分析如下: 使用反向插入方法 这里我们先把创建的内容插人到元素前面,然后再把同一个元素插人到文档 中的另一个位置。通常,当在jQuery中操作元素时,利用...
2015-11-13
纯JS实现旋转图片3D展示效果
CSS: <style type="text/css"> #show{position:relative;margin:20px auto;width:800px;} .item{position:...
2017-03-22
Bootstrap BootstrapDialog使用详解
这里有两种展现方式 写在前面:首先你要引入的库有 css : bootstrap.min.css bootstrap-dialog.css js : jquery-1.11.1.min.js bootstrap.min.js bootstr...
2017-03-16
js实现鼠标左右移动,图片也跟着移动效果
效果:鼠标往左移,图片对应右移,鼠标往右移,图片就左移动。图片距离越远偏移距离越大。 思路:首先获取图片原先的距离。设置一个变化值,图片的最终距离等于原先的距离加上变化值 布局:大盒子里面是图片,大盒子position:relative;图...
2017-02-17
vue-awesome-swiper的使用以及API整理
一、先说一个看关于vue-awesome-swiper的一个坑 vue项目的package.json中显示的<span style=“color: orange;”>“vue-awesome-swiper”: “^2.5.4”&...
2018-04-26
vue.js实现请求数据的方法示例
vue2.0示例代码如下: var vm = new Vue({ el:"#list", data:{ gridData: "", }, ...
2017-03-20
回到顶部