使用Fabric.js实现贝塞尔曲线波浪特效

news2024/9/20 9:41:57

一、前言

本文是在此基础上收到启发然后进行的变化,当然,观看与否不会影响接下来的阅读体验。

二、实现思路

其实整个波浪动画其实可以看成:在相对坐标系静止的视角下,一个正弦函数在直角坐标系上匀速平移时我们所观察到的效果。根据所选参照系的不同,我们也可以看成是:坐标系上有个点,我们在相对点静止的视角下,一个点在坐标系上按着正弦函数的规律来进行移动,此时我们所观察到的点走过的痕迹的变化就是波浪的动画效果。从绘制角度来说,就是我们要绘制一条曲线按照正弦函数的规律去变化。在canvas中,能让曲线以正弦函数方式绘制的其中一个方法就是使用canvas提供的贝塞尔曲线绘制方法。

canvas提供的贝塞尔曲线方法有很多变种,比如一次贝塞尔、二次贝塞尔、三次贝塞尔等等,在这里我们主要使用了三次贝塞尔,三次贝塞尔曲线有一个起点、终点和两个控制点。其他的在此不做讨论。

言归正传,来讲讲Fabric.js。Fabric.js 没有提供类似于canvas中的绘制贝塞尔曲线的API(quadraticCurveTobezierCurveTo等),但是在线段绘制类Path里,Fabric.js引入了SVG中path元素的线段绘制写法,而这就让我们在Fabric.js中绘制贝塞尔曲线的梦想得以触手可及。

在这里我们简单介绍一下SVG中path元素的部分语法:

2.1、M(moveto)移动和L(lineto)画直线

其中 M 表示移动到某点,L 表示画一条直接到某点,后面跟一个坐标点,建议格式如下:

"M x y" // M是命令:x横轴坐标、y纵轴坐标
"L x y" // L是命令:x横轴坐标、y纵轴坐标 

2.2、 Z(cloasPath)闭合

结束点到开始点画一条直线,形成一个闭合的区域。

语法格式就是在字符串的最后写一个Z,表示闭合。

2.3、C(curveto)三次贝塞尔曲线

三次贝塞尔曲线有一个起点、终点和两个控制点,跟二次贝塞尔相比多了一个控制点,如图,P0、P3是起点和终点,P1、P2是控制点。

// 起点(x0,y0),控制点1 (x1,y1),控制点2 (x2,y2),终点 (x3,y3)
"
M x0 y0
C x1 y1, x2 y2, x3 y3 
" 

介绍完了SVG语法之后我们来看看如何实现波浪的效果。

三、代码实现

3.1、运行环境

fabric v5.2.4

vue v3.2.25

3.2、画布搭建

HTML

<template><div class="container"><canvas class="canvas" id="fabric" width="500" height="500"></canvas></div>
</template> 

SCSS

.container {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;.canvas {border: 1px solid #ccc;}
} 

TS

 interface WaveConfig {/** 波浪颜色 */colors: string[];/** 想要的波浪高度 */waveHeight: number;
}

const config:WaveConfig ={waveHeight: 240,colors: ['#44c7b399'],
};

const canvas = new fabric.StaticCanvas('fabric'); // 初始化绑定画布
const height = canvas.getHeight(); // 获取画布的高度
const width = canvas.getWidth(); // 获取画布的宽度
const waveHeight = height - config.waveHeight; // 波浪高度
let step = 0; // 位移长度 

上述代码中的step变量代表点在x轴上走过的路程。

3.3、绘制贝塞尔曲线

接下来我们先利用Path类画出一条贝塞尔曲线。由于画线时我们需要获取点的y轴值,所以我们把step比作正弦函数的x,带入进正弦函数中拿到曲线对应的y值。

const angle = step * Math.PI / 180; // 由于step是长度,首先得转换成角度
const Y1 = Math.sin(angle) + waveHeight; // 拿到第一个控制点的高度
// 拿到第二个控制点的高度,也可以写成 Math.sin(angle + Math.PI / 2) + waveHeight
const Y2 = Math.cos(angle) + waveHeight; 

在这里我们选取画布对称分布的两个位置为控制点的x

const X1 = width / 3;
const X2 = width / 3 * 2; 

接下来绘制我们的贝塞尔曲线

const line = new fabric.Path(`M 0 ${waveHeight + Y1} C ${X1} ${waveHeight + Y1}, ${X2} ${waveHeight + Y2}, ${width} ${waveHeight + Y2}L ${width} ${height}L 0 ${height}L 0 ${height / 2 + Y1}Z`,{ fill: config.colors[0], stroke: '' }); 

最后让step改变,以启动我们的波浪动画

 let step = 0;interval = setInterval(() => {// 清空画布canvas.clear();step += config.value.speed; // 控制波浪的变化速度const angle = step * Math.PI / 180;const Y1 = Math.sin(angle) + waveHeight;const Y2 = Math.cos(angle) + waveHeight;const X1 = width / 3;const X2 = width / 3 * 2;const line = new fabric.Path(`M 0 ${waveHeight + Y1} C ${X1} ${waveHeight + Y1}, ${X2} ${waveHeight + Y2}, ${width} ${waveHeight + Y2}L ${width} ${height}L 0 ${height}L 0 ${height / 2 + Y1}Z`,{ fill: config.colors[0], stroke: '' });// 添加到画布中canvas.add(line);}, 15); 

四、完整代码

到此一个基本的动画就完成了,我们可以在上面进行拓展,通过调整一些参数来改变波浪动画的细节效果,例如波浪速度、波浪起伏高度、波浪条数等等。

HTML

<template><div class="container"><canvas class="canvas" id="fabric" width="500" height="500"></canvas></div>
</template> 

SCSS

.container {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;.canvas {border: 1px solid #ccc;}
} 

TS

import { fabric } from "fabric";
import { onMounted, ref, onBeforeUnmount } from "vue";

onMounted(() => {const canvas = new fabric.StaticCanvas('fabric');const height = canvas.getHeight();const width = canvas.getWidth();const waveTrajectory = config.value.waveTrajectory; // 波浪运动轨迹const waveFluctuate = config.value.waveFluctuate; // 波浪起伏高度const waveHeight = height - config.value.waveHeight; // 波浪高度waterStartMove();
});

onBeforeUnmount(() => {clearInterval(interval);
})

interface WaveConfig {/** 波浪条数 */lines: number;/** 波浪颜色 */colors: string[];/** 波浪高度 */waveHeight: number;/** 波浪的变化速度 */speed: number;/** 波浪运动轨迹 */waveTrajectory: number;/** 波浪的起伏高度 */waveFluctuate: number;
}

const config = ref<WaveConfig>({lines: 2,waveHeight: 240,colors: ['#44c7b399', '#44c7b34d'],speed: 2,waveTrajectory: 90,waveFluctuate: 8
});

let interval: any;

/**
 * 让水动起来
 */
function waterStartMove(): void {let step = 0;// 清空canvas.clear();step += config.value.speed; // 控制波浪的变化速度for (let i = 0; i < config.value.lines; i++) {const angle = (step + i * waveTrajectory / config.value.lines) * Math.PI / 180;const deltaHeight = Math.sin(angle) * waveFluctuate;const deltaHeightRight = Math.cos(angle) * waveFluctuate;const line = new fabric.Path(`M 0 ${waveHeight + deltaHeight} C ${width / 3} ${waveHeight + deltaHeight - waveFluctuate}, ${width / 3 * 2} ${waveHeight + deltaHeightRight - waveFluctuate}, ${width} ${waveHeight + deltaHeightRight}L ${width} ${height}L 0 ${height}L 0 ${height / 2 + deltaHeight}Z`,{ fill: config.value.colors[i], stroke: '' });canvas.add(line);}requestAnimationFrame(() => {waterStartMove();});
} 

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

如何防止订单重复提交

如何防止订单重复提交前言什么是重复下单为什么会重复下单&#xff1f;如何处理重复下单&#xff1f;利用数据库实现幂等利用Redis防重前言 最近在做电商项目&#xff0c;整理一下解决方案并帮助自己巩固知识点&#xff0c;此方案是结合了目前的业务环境&#xff0c;若有更好的…

Linux进程概念讲解

1、进程的基本概念在给进程下定义之前&#xff0c;我们先了解一下进程&#xff1a;我们在编写完代码并运行起来时&#xff0c;在我们的磁盘中会形成一个可执行文件&#xff0c;当我们双击这个可执行文件时&#xff08;程序时&#xff09;&#xff0c;这个程序会加载到内存中&am…

中央计算平台「上车」加速

随着英伟达、高通宣布在2024年开始陆续交付中央计算平台&#xff0c;Tier1正在跨域联合&#xff0c;以寻求在新的行业周期获得先发优势。 本月初&#xff0c;韩国LG电子宣布与高通达成合作协议&#xff0c;在原有座舱计算平台基础上&#xff0c;进一步拓展至智能驾驶领域。而在…

Transformer架构理解

参考&#xff1a; 1. Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, and Illia Polosukhin. 2017. Attention is all you need. In Proceedings of the 31st International Conference on Neural Information Pr…

Java 语法糖详解

本文从 Java 编译原理角度&#xff0c;深入字节码及 class 文件&#xff0c;抽丝剥茧&#xff0c;了解 Java 中的语法糖原理及用法&#xff0c;帮助大家在学会如何使用 Java 语法糖的同时&#xff0c;了解这些语法糖背后的原理。 什么是语法糖&#xff1f; 语法糖&#xff08…

金三银四一口气拿下三个offer的软件测试面试宝典,面试必看

说说你们怎么做接口自动化测试的☆☆☆☆☆ 我们是使用pythonrequesunittest来搭建我们的做接口自动化测试框架&#xff0c;使用python语言来调用第三方的类库requests来根据我们接口方法get或者post来发送接口请求&#xff0c;接收响应回来的数据进行适当的格式转换&#xff…

Python+Requests+PyTest+Excel+Allure 接口自动化测试实战

--------UnitTest框架和PyTest框架的简单认识对比与项目实战-------- 定义&#xff1a; Unittest是Python标准库中自带的单元测试框架&#xff0c;Unittest有时候也被称为PyUnit&#xff0c;就像JUnit是Java语言的标准单元测试框架一样&#xff0c;Unittest则是Python语言的标…

Python爬虫进阶 - win和linux下selenium使用代理

目录 Windows selenium配置 下载地址 Chrome Chromedriver 版本对应关系 实践测试 操作元素 浏览器操作 获取元素信息 鼠标操作 实战demo selenium添加代理 Linux selenium配置 检查服务器环境 下载安装第三方库&#xff08;最简单版&#xff09; 实践测试 代码…

【2.20】动态规划 +项目 + 存储引擎

01背包问题 现有一容量为w的背包&#xff0c;有3个物品&#xff0c;每个物品重量不同&#xff0c;价值不同&#xff0c;问&#xff0c;怎样装才能价值最大化&#xff1f; 明确dp数组含义和下标含义&#xff1a;dp[j]表示当前背包的最大价值。j表示背包容量。递推公式&#xf…

【期末复习】例题说明Prim算法与Kruskal算法

点睛Prim与Kruskal算法是用来求图的最小生成树的算法。最小生成树有n个顶点&#xff0c;n-1条边&#xff0c;不能有回路。Prim算法Prim算法的特点是从个体到整体&#xff0c;随机选定一个顶点为起始点出发&#xff0c;然后找它的权值最小的边对应的另一个顶点&#xff0c;这两个…

IEEE学生会员注册

IEEE学生会员注册0、引言IEEE 会员介绍1、IEEE学生会员会费2、加入学生会员2.1、创建/登录 IEEE 账户2.2、填写/维护 个人基本信息2.3、填写/维护 基本教育信息3、选择一个你将要加入的协会或社团4、确认购物车5、付款5.1、付款详情信息页5.2、扫码付款并验证5.3、会员确认邮件…

【前端提效】-- VsCode 实用插件推荐

EditorConfig for VS Code ***** 作用&#xff1a;多人协同开发&#xff0c;规范缩进风格&#xff0c;缩进大小&#xff0c;tab长度以及字符集等&#xff0c;解决不同IDE的编码范设置&#xff0c;在这里配置&#xff08;.editorconfig&#xff09;的代码规范规则优先级高于编辑…

Object.defineproperty方法

Object.defineproperty 的作用就是直接在一个对象上定义一个新属性&#xff0c;或者修改一个已经存在的属性Object.defineproperty可以接收三个参数Object.defineproperty(obj, prop, desc)obj : 第一个参数就是要在哪个对象身上添加或者修改属性prop : 第二个参数就是添加或修…

Ansys Zemax | 如何在存在全内反射 (TIR) 的情况下应用散射

在本文中&#xff0c;我们将展示如何利用虚拟表面来对具有全内反射 (TIR) 的物体进行建模&#xff0c;同时保持其他独特的表面特性&#xff0c;例如粗糙的表面结构。 下载 联系工作人员获取附件 简介 在OpticStudio中&#xff0c;全内反射 (TIR) 在其他表面属性&#xff08…

计网物理层

第一章&#xff1a;物理层 1、物理层的主要作用&#xff1a; ①、在不同的传输媒体上传输比特流 ②、屏蔽各种传输媒体的差异&#xff0c;为上层的数据链路层提供服务&#xff0c;使得上层的数据链路层无需考虑传输媒体是什么 2、传输媒体的种类&#xff1a; ①、导引型传…

python环境配置

python环境配置一、ADB环境配置1、ADB下载路径:2、点击下载3、解压并放到本地磁盘4、配置ADB环境变量二、Python环境配置1、Python下载路径:2、点击下载(默认下载最新的)3、解压并放到本地磁盘4、配置Python环境变量5、配置pip环境变量三、Pycharm安装1、pycharm下载路径:2、点…

“数字乡民”疑云:助农?坑农?

1月2日&#xff0c;“深入实施数字乡村发展行动&#xff0c;推动数字化应用场景研发推广”写入了国务院《关于做好2023年全面推进乡村振兴重点工作的意见》中&#xff0c;2022年中央一号文件也要求“大力推进数字乡村建设&#xff0c;推进智慧农业发展”。 在这之前&#xff0…

ArrayList删除元素时导致的java.util.ConcurrentModificationException错误的分析及源码解读

1.前言 集合对于开发者来说都不陌生&#xff0c;可以说是我们日常开发中使用最频繁的对象之一&#xff0c;尤其是ArrayList&#xff0c;可是对于一些开发者并不真正了解它&#xff0c;只是使用习惯了&#xff0c;也就按照集合中基础的一些api使用了&#xff0c;但有时候却因为错…

MacBook IDEA 顶部菜单栏不显示问题

文章目录背景&#xff1a;当前显示方式一1. 快捷键&#xff1a;双击shift 搜索 idea.vmoptions3. 在idea.vmoptions文件末尾添加 -Dapple.laf.useScreenMenuBarfalse方式二1. 访达 > 应用程序 > idea 右键 显示包内容2. 进入到bin包位置的命令终端3. 编辑文件 vi idea.pr…

硬件学习 软件Cadence day05 快速拥有 元件的原理图和PCB 分装 (Ultra Librarian 下载)

1.下载 Ultra Librarian &#xff08;实测有用&#xff09; 一个链接&#xff1a; (5条消息) Ultra Librarian 的安装和使用_lian123的博客-CSDN博客_ultra librarian 安装过程简单&#xff01;&#xff01;&#xff0c; 一直next 就行. 2.快速寻找 元器件 &#xff08;…