贝塞尔曲线理解与应用

贝塞尔曲线并非是由贝塞尔发明的,但是是因为他把这个东西应用到当时的汽车领域而闻名的,所以取名为贝塞尔曲线。
在我看来,用简单的话来理解一下贝塞尔曲线,他是通过少量几个点,使用一套公式,生成一条平滑曲线。

原理

先盗用人家的图,嘿嘿。

Alt text
平面ABC 3个点。
Alt text
在AB上找一个点D,在BC上找一个点E,使得AD:AB = BE:BC
Alt text
然后在DE上找一个点F,使得DF:DE = AD:AB = BE:BC 接着,我们将D点从A点 --> B点慢慢移动,在这个过程中,会产生一系列的F点,将这些F点相连,就会形成一条曲线,嘿嘿,就是我们的贝塞尔曲线,
Alt text
从这里可以看出,这里有3个关键点,起始点、终止点、控制点。 数学上的推理验证,这里就不讲了,直接给出公式。

二阶贝塞尔曲线,一个控制点

Alt text
Alt text

三阶贝塞尔曲线,二个控制点

Alt text
Alt text

一阶贝塞尔曲线,就是一条直线

Alt text
Alt text

为了完整性,我给出贝塞尔曲线的n阶通式

Alt text
想看这个公式推导,我给出一个文章链接 n公式推导推导。 但是在一般应用中,二阶,三阶贝塞尔曲线是已经够用了。

应用

先简单的来使用一下,通过公式来描绘曲线。

***d2(){this.name = '二次贝赛尔曲线方程';let _this = this;let oCanvas = document.querySelector("#canvas"),oGc = oCanvas.getContext('2d');let percent = 0;function animate() {oGc.clearRect(0, 0, 800, 800);oGc.beginPath();oGc.strokeStyle = 'red';oGc.moveTo( 40, 80 );//oGc.quadraticCurveTo( 137, 80, 140, 280 );_this.d2_(oGc,[40, 80],[137, 80],[140, 280],percent);oGc.stroke();percent = (percent   1) % 100;requestAnimationFrame(animate);}animate()},d2_(oGc,start,cp,end, percent){for (var t = 0; t <=  percent / 100; t  = 0.01) { var x = this.quadraticBezier(start[0], cp[0], end[0], t); var y = this.quadraticBezier(start[1], cp[1], end[1], t);oGc.lineTo(x, y);} },quadraticBezier(p0, p1, p2, t) {var k = 1 - t;return k * k * p0   2 * (1 - t) * t * p1   t * t * p2; // 这个方程就是二次贝赛尔曲线方程 },***

这个就是根据公式描述出相关的点,然后连接起来。 但是在实际应用中,很大程度上会在canvas中绘图,canvas提供2个api,
quadraticCurveTo:二阶贝塞尔曲线,参数是 控制点,结束点
bezierCurveTo :三阶贝塞尔曲线,参数是 控制点1,控制点2,结束点
你们发现没,它们没有开始点,它们的开始点是画笔开始的位置。

在举一个例子,画起伏波浪

直接讲思路,就是先画一个静止的波浪

好,现在来看一下,这个该怎么入手,先把这个轮廓描绘出来,要描绘,先拆分, 它是由一条曲线,3条直接拼接而成,有了这个思路,已经完成了一半, 那条曲线该如何绘制,其实我觉得思路不止一种,我们应该先自己给这个曲线下定义,我认为他应该是半圆的弧连接,应该是椭圆的弧链接,应该是其他。我先给它下一个定义

Alt text

我用二阶和三阶分别来描述这个曲线,1,2,3,4这4个点描述出来了,那么这个曲线也就绘制完成了
1: (0.5d,waveH)
2: (d, 0)
3: (1.5d,-waveH)
4: (2d,0)
我选择的这个规则是很中规中矩的,上一个波形是画2个二阶贝塞尔曲线,下一个波形是画一个3阶贝塞尔曲线。这个就可以把静止的波形给绘制出来了,然后你想象一个给这个坐标加横向偏移,加纵向偏移,他就可以起伏波动了

***init2(){this.name = '2阶';let c = document.getElementById("myCanvas"),ctx = c.getContext("2d"),waveWidth = 800,offset = 0, //xwaveHeight = 20, // 波浪大小waveCount = 5,startX = -200,startY = 208,progress = 0,  //高度progressStep = 0.5,d2 = waveWidth / waveCount,d = d2 / 2,hd = d / 2;ctx.fillStyle = "rgba(0,222,255, 0.2)";function tick() {offset -= 4;  // x 移动progress  = progressStep;if (progress > 220 || progress < 0) progressStep *= -1;if (-1 * offset === d2) offset = 0;ctx.clearRect(0, 0, c.width, c.height);ctx.beginPath();let offsetY = startY - progress; //y 坐标高低ctx.moveTo(startX - offset, offsetY);for (var i = 0; i < waveCount; i  ) {var dx = i * d2;var offsetX = dx   startX - offset;ctx.quadraticCurveTo(offsetX   hd, offsetY   waveHeight, offsetX   d, offsetY);ctx.quadraticCurveTo(offsetX   hd   d, offsetY - waveHeight, offsetX   d2, offsetY);}ctx.lineTo(startX   waveWidth, 300);ctx.lineTo(startX, 300);ctx.fill();requestAnimationFrame(tick);}tick();},
***

上面是二阶贝塞尔曲线,用三阶画的话,就是
ctx.quadraticCurveTo(offsetX hd, offsetY waveHeight, offsetX d, offsetY);
ctx.quadraticCurveTo(offsetX hd d, offsetY - waveHeight, offsetX d2, offsetY);

换成
ctx.bezierCurveTo(offsetX hd, offsetY waveHeight, offsetX d hd, offsetY-waveHeight, offsetX d2, offsetY );
就可以了。

其实我觉得贝塞尔曲线在使用过程中,最关键的是控制点的选择,不同点的选择,会展现不同的效果,但是选择控制点,是一件挺有意思的事。
下面我们再来看一个案例,粘性拖动

Alt text
要实现这个功能,来理一下思路,首先来描绘一下这个轮廓,一样的套路,是不是,来,思考一下,这个图形是由什么组成的。
画的丑,别介意,这么看这个轮廓,是不是出来了,你可以想象,是由2个半圆的圆弧和2条曲线,可以先画ABCD这个路径,再画2个圆,这样这个轮廓就出来了。接下来,再看这个曲线如何完成。这个曲线开始和结束点已经有了,再找一个控制点也能画出来,那么控制点在哪里,我下的定义简单粗暴,在2圆心的链接线的终点,然后再把ABCD 4个点描述出来,这个路径就解决了,如何描述ABCD,请允许我盗图
Alt text
如何让这个图形动起来,可以这么想第一个圆,可以是手开始触摸的点,也可以自己先写死,另一个圆是手拖动的位置,所以只要动态的改变第二个圆心的位置,那么这个拖动的效果就出来了。 我在拖动的时候,d的距离在改变,那么制定一个规则,d越大,第一个圆的半径就越小,那么基本上就可以实现了。

***data() {return {radius: 7,x: 300,//手移动y: 300,//手移动anchorX: 200,// 控制点anchorY: 200,// 控制点startX: 100, //开始startY: 100,//开始}},mounted() {document.removeEventListener('touchstart', this.wrapTouchStart);document.addEventListener("touchstart", this.wrapTouchStart);document.removeEventListener('touchmove', this.wrapTouchMove);document.addEventListener('touchmove', this.wrapTouchMove);document.removeEventListener('touchend', this.wrapTouchEnd);document.addEventListener('touchend', this.wrapTouchEnd);document.removeEventListener('touchcancel', this.wrapTouchCancel);document.addEventListener('touchcancel', this.wrapTouchCancel);},methods: {wrapTouchStart(e) {},wrapTouchMove(e) {this.x = e.changedTouches[0].clientX;this.y = e.changedTouches[0].clientY;this.anchorX = (e.changedTouches[0].clientX   this.startX) / 2;this.anchorY = (e.changedTouches[0].clientY   this.startY) / 2;this.d2();},wrapTouchEnd() {this.radius = 20;// 手势坐标this.x = 300;this.y = 300;// 控制点坐标this.anchorX = 200;this.anchorY = 200;// 起点坐标this.startX = 100;this.startY = 100;},wrapTouchCancel() {let oCanvas = document.querySelector("#canvas"),ctx = oCanvas.getContext('2d');ctx.clearRect(0, 0, 360, 600);},d2() {let _this = this;let oCanvas = document.querySelector("#canvas");ctx = oCanvas.getContext('2d');ctx.strokeStyle = 'red';var distance = Math.sqrt(Math.pow(this.y - this.startY, 2)   Math.pow(this.x - this.startX, 2));this.radius = -distance / 15   20;// 当气泡拉到一定程度,断开链条且链条消失//if (this.radius < 7) {if(distance > 250){ctx.clearRect(0, 0, 360, 600);ctx.beginPath();ctx.arc(this.x, this.y, 20, 0, 2 * Math.PI);ctx.strokeStyle = 'red';ctx.fill();console.log('end');return;}let sin = (this.x - this.startX) / distance;let cos = (this.y - this.startY) / distance;var x1 = this.startX - this.radius * cos;var y1 = this.startY   this.radius * sin;var x2 = this.x - 20 * cos;var y2 = this.y   20 * sin;var x3 = this.x   20 * cos;var y3 = this.y - 20 * sin;var x4 = this.startX   this.radius * cos;var y4 = this.startY - this.radius * sin;ctx.clearRect(0, 0, 360, 600);ctx.beginPath();ctx.moveTo(x1, y1);ctx.quadraticCurveTo(this.anchorX, this.anchorY, x2, y2);ctx.lineTo(x3, y3);ctx.quadraticCurveTo(this.anchorX, this.anchorY, x4, y4);ctx.lineTo(x1, y1);ctx.fillStyle = 'red'; ctx.stroke();ctx.fill();// 两圆圈ctx.beginPath();ctx.arc(this.startX, this.startY, this.radius, 0, 2 * Math.PI)ctx.arc(this.x, this.y, 20, 0, 2 * Math.PI)ctx.strokeStyle = 'red';ctx.fill();},}***

到这里,应该要结束了,但是我想说这控制点,其实还有其他选择,还有一种是是AC连线的中点,和BD连线的中点,具体的项目我晚一点附上地址。

by cs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云服务器布置_【阿里云ECS】(一)云服务器上安装RStudio-server

【阿里云ECS】&#xff08;一&#xff09;云服务器上安装RStudio-server最近注册了阿里云个人版&#xff0c;打算研究研究shiny部署问题。进了阿里云ECS因为是Ubuntu16.04的对于安装R和RStudio还是要学习实践一下的。第一步&#xff0c;安装R语言。我们使用apt的方式进行安装,步…

H5前期知识点总结 9月12日

知识点补充&#xff1a; 属性继承例子&#xff0c;color、font&#xff08;font-size/style/family/weight&#xff09; 1、浏览器的默认字体大小是16px,谷歌浏览器的最小字体是10px,其他浏览器的最小字体是12px。 2、通配符选择器 “*”&#xff0c;即选中body里所有的标签。 …

Spring MVC:Ajax和JQuery

今天&#xff0c;我想演示如何将AJAX集成到Spring MVC应用程序中。 我将在客户端使用JQuery来发送请求和接收响应。 本教程将基于我以前关于Spring MVC和REST服务的教程之一。 在本文中&#xff0c;您将了解如何在异步请求的帮助下使Web应用程序更具交互性。 准备工作 我需要通…

手把手带你写一个JavaScript类型判断小工具

业务写了很多&#xff0c;依然不是前端大神&#xff0c;我相信这是很多‘入坑’前端开发同学的迷茫之处&#xff0c;个人觉得前端职业发展是有路径可寻的&#xff0c;前期写业务是一个积累过程&#xff0c;后期提炼总结&#xff0c;比如编程思想&#xff0c;父子类的原型继承&a…

yolov3之pytorch源码解析_springmvc源码架构解析之view

说在前面前期回顾sharding-jdbc源码解析 更新完毕spring源码解析 更新完毕spring-mvc源码解析 更新完毕spring-tx源码解析 更新完毕spring-boot源码解析 更新完毕rocketmq源码解析 更新完毕dubbbo源码解析 更新完毕netty源码解析 更新完毕spring源码架构更新完毕springmvc源码架…

Xstream将XML转换为javabean的问题

1.问题&#xff1a;Xstream is not security 解决方法&#xff1a;加上 2.问题&#xff1a;如果没有第二行代码&#xff0c;会出现xstream forbiddenclassexception 解决方法&#xff1a;加上第二行&#xff0c;其中参数是要进行解析的对象&#xff01; 调用该方法&#xff1a;…

蚂蚁属性细微差别

每隔一段时间&#xff0c;我会想起Ant属性的一些细微差别 &#xff0c;一旦忘记它们&#xff0c;在与Ant交互时会引起混乱。 特别是&#xff0c; Ant属性 通常是不可变的 &#xff08;不包括Ant 1.8版本的 局部属性 &#xff09;&#xff0c;并且在其首次设置时“永久”设置&am…

《从零构建前后分离的web项目》准备 - 前端了解过关了吗?

前端基础架构和硬核介绍 技术栈的选择 首先我们构建前端架构需要对前端生态圈有一切了解&#xff0c;并且最好带有一定的技术前瞻性&#xff0c;好的技术架构可能日后会方便的扩展&#xff0c;减少重构的次数&#xff0c;即使重构也不需要大动干戈&#xff0c;我通常选型技术栈…

联想w540笔记本参数_2020年12月笔记本电脑推荐!联想、惠普、华为笔记本电脑推荐!18款高性价比笔记本电脑推荐!!!...

前言&#xff1a;笔记本电脑&#xff0c;主要分为三种&#xff1a;轻薄本<全能本<游戏本。轻薄本&#xff0c;又称办公笔记本电脑&#xff0c;因为轻薄方便携带&#xff0c;通常用于移动办公使用。全能本&#xff0c;就是把轻薄本的集显变成了小独显&#xff0c;增加了独…

【BZOJ 1098】办公楼(补图连通块个数,Bfs)

补图连通块个数这大概是一个套路吧&#xff0c;我之前没有见到过&#xff0c;想了好久都没有想出来QaQ 事实上这个做法本身就是一个朴素算法&#xff0c;但进行巧妙的实现&#xff0c;就可以分析出它的上界不会超过 $O(n m)$。 接下来介绍一下这个技巧&#xff1a; 很显然一个…

Spring MVC:资源

我从博客读者那里收到的最常见的问题之一是如何在Spring MVC的应用程序中使用CSS和javascript文件。 因此&#xff0c;这是撰写有关Spring MVC中资源使用情况的文章的好机会。 通常&#xff0c;我将使用基于Java的配置方法。 如今&#xff0c;很难想象没有CSS和JavaScript文件…

c语言读文件空格间隔,c语言文件流实现按单个词读取(以空格、分号等作间隔)...

c语言文件流实现按词读取(以空格、分号等作间隔)1.基本描述在之前的作业中&#xff0c;认真编写代码&#xff0c;从中发现不少知识积累上的欠缺。编程中使用到的c语言文件读取&#xff0c;要求是按照获取到一个完整的词&#xff0c;并对其进行处理。写有java或c语言源程序的.tx…

山东专升本access知识点_全国各省份每年的专升本考试大纲啥时候公布?考纲公布之前你该做什么?...

?星标/置顶专升本招考下一个上岸的就是你最近小编在专升本招考后台收到很多同学的留言&#xff0c;大多同学都是看到好多省都公布了专升本考试政策和大纲&#xff0c;却迟迟不见自己所在的省份出&#xff0c;于是火急火燎地找到小编来问&#xff1a;XX省的考试大纲到底啥时候出…

观点|蚂蚁金服玉伯:我们是如何从前端技术进化到体验科技的?

小蚂蚁说&#xff1a;王保平&#xff0c;花名玉伯。熟悉前端和SeaJS的人一定对这个名字不陌生。作为前端领域的一枚大大大牛&#xff0c;他现在担任蚂蚁金服体验技术部负责人。本文&#xff0c;他分享了他从前端一路进阶升级到体验科技的个人思考&#xff0c;并详细介绍了体验科…

excel 电阻并联计算_电路分析基础(5)-关于电阻,有些话我还是要说一说

电阻定义&#xff1a;导体对电流的阻碍作用就叫该导体的电阻。不同的导体&#xff0c;电阻一般不同&#xff0c;电阻是导体本身的一种性质。定义式如下&#xff1a;因此&#xff0c;我们应该清楚了欧姆定律的本质到底是什么&#xff0c;为啥电阻跟电压和电流没有关系&#xff0…

针对新手的Java EE7和Maven项目–第2部分–为我们的应用程序定义一场简单的战争...

从第一部分恢复 第1部分 我们刚刚定义了父 pom。 一种特殊的pom类型&#xff0c;它最终定义了我们的应用程序将要使用的库。 它还配置了所有用于打包我们应用程序每个模块的Maven工具。 您可以在此处 签出 -1部分示例代码。 因此&#xff0c;到目前为止&#xff0c;在将要开发…

postman实现从response headers中获取cookie,并将其设置为环境变量

1.最近在学习postman的使用方法,为了保证后续模块操作&#xff0c;必须在登录时获取的session值&#xff0c;并将其设置为环境变量&#xff0c;session的位置处于response headers里面返回的set-cookie参数&#xff0c;并且将set-cookie中的session通过split方法截取出来. 写法…

010 pandas的DataFrame

一&#xff1a;创建 1.通过二维数组进行创建 2.取值 取列,取位置的值 3.切片取值 这个和上面的有些不同&#xff0c;这里先取行&#xff0c;再取列 4.设定列索引 这里使用的行索引与上面不同。 5.通过字典的方式创建 6.索引 包含行索引&#xff0c;与列索引 7.修改列索引 转载于…

unity烘培单个物体_Unity可编程渲染管线(SRP)教程:二、自定义着色器

本文翻译自Catlike Coding&#xff0c;原作者&#xff1a;Jasper Flick。本文经原作者授权&#xff0c;转载请说明出处。原文链接在下&#xff1a;https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/custom-shaders/​catlikecoding.com本章内容如下&…

一套比较完整的前端技术选型,需要规整哪些东西,你知道不?

1. 背景及现状 随着前端开发复杂度的日益增加&#xff0c;各种优秀的组件框架也遍地开花。同时&#xff0c;我们面临业务规模的快速发展和工程师团队的不断扩张&#xff0c;如何解决资源整合、模块开发、项目部署、性能优化等问题势在必行。 2. 目标 根据背景和现状的分析&a…