一、前言
在观看本文之前推荐先看看这位朋友的文章(使用canvas实现贝塞尔曲线波浪特效),本文是在此基础上收到启发然后进行的变化,当然,观看与否不会影响接下来的阅读体验。
二、实现思路
其实整个波浪动画其实可以看成:在相对坐标系静止的视角下,一个正弦函数在直角坐标系上匀速平移时我们所观察到的效果。根据所选参照系的不同,我们也可以看成是:坐标系上有个点,我们在相对点静止的视角下,一个点在坐标系上按着正弦函数的规律来进行移动,此时我们所观察到的点走过的痕迹的变化就是波浪的动画效果。
从绘制角度来说,就是我们要绘制一条曲线按照正弦函数的规律去变化。在canvas中,能让曲线以正弦函数方式绘制的其中一个方法就是使用canvas提供的贝塞尔曲线绘制方法。
canvas提供的贝塞尔曲线方法有很多变种,比如一次贝塞尔、二次贝塞尔、三次贝塞尔等等,在这里我们主要使用了三次贝塞尔,三次贝塞尔曲线有一个起点、终点和两个控制点。其他的在此不做讨论。
言归正传,来讲讲Fabric.js。Fabric.js 没有提供类似于canvas中的绘制贝塞尔曲线的API(quadraticCurveTo
、bezierCurveTo
等),但是在线段绘制类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();
});
}
五、参考链接
今天的文章使用Fabric.js实现贝塞尔曲线波浪特效分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21946.html