canvas實作動态貝塞爾曲線效果,實作如下:
1、接口:
interface lineParams {
start: Array<number>;
end: Array<number>;
curveness: number;
percent: number;
}
export { lineParams };
2、js代碼:
import React, { useRef, useEffect } from 'react';
import { lineParams } from './model';
const LineAnimate = () => {
let canvas: any = null;
let ctx: any = null;
let percent: number = 0;
const canvasWrapRef = useRef(null);
const canvasRef = useRef(null);
// canvas初始化
const initCanvas = () => {
const canvasWrap = canvasWrapRef.current;
canvas = canvasRef.current;
canvas.height = canvasWrap.offsetHeight;
canvas.width = canvasWrap.offsetWidth;
ctx = canvas.getContext('2d');
};
/**
* 繪制一條曲線路徑
* @param {Object} ctx canvas渲染上下文
* @param {Array<number>} start 起點
* @param {Array<number>} end 終點
* @param {number} curveness 曲度(0-1)
* @param {number} percent 繪制百分比(0-100)
*/
const drawCurvePath = ({ start, end, curveness, percent }) => {
const cp: Array<number> = [
(start[0] + end[0]) / 2 - (start[1] - end[1]) * curveness,
(start[1] + end[1]) / 2 - (end[0] - start[0]) * curveness,
];
const t: number = percent / 100;
const p0: Array<number> = start;
const p1: Array<number> = cp;
const p2: Array<number> = end;
// 向量<p0, p1>
const v01: Array<number> = [p1[0] - p0[0], p1[1] - p0[1]];
// 向量<p1, p2>
const v12: Array<number> = [p2[0] - p1[0], p2[1] - p1[1]];
const q0: Array<number> = [p0[0] + v01[0] * t, p0[1] + v01[1] * t];
const q1: Array<number> = [p1[0] + v12[0] * t, p1[1] + v12[1] * t];
// 向量<q0, q1>
const v: Array<number> = [q1[0] - q0[0], q1[1] - q0[1]];
const b: Array<number> = [q0[0] + v[0] * t, q0[1] + v[1] * t];
ctx.moveTo(p0[0], p0[1]);
ctx.quadraticCurveTo(q0[0], q0[1], b[0], b[1]);
};
// 畫坐标軸
const drawCoordinate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = '#ccc';
ctx.fillStyle = '#ccc';
// 畫x軸
ctx.moveTo(32, canvas.height - 32);
ctx.lineTo(canvas.width - 32, canvas.height - 32);
// 畫x軸坐标三角形
ctx.moveTo(canvas.width - 40, canvas.height - 32);
ctx.lineTo(canvas.width - 40, canvas.height - 36);
ctx.lineTo(canvas.width - 32, canvas.height - 32);
ctx.lineTo(canvas.width - 40, canvas.height - 28);
ctx.lineTo(canvas.width - 40, canvas.height - 32);
// 畫y軸
ctx.moveTo(32, canvas.height - 32);
ctx.lineTo(32, 32);
// 畫y軸坐标三角形
ctx.moveTo(32, 40);
ctx.lineTo(28, 40);
ctx.lineTo(32, 32);
ctx.lineTo(36, 40);
ctx.lineTo(32, 40);
ctx.closePath();
ctx.fill();
ctx.stroke();
};
// 繪制貝塞爾曲線
const drawQuadraticLine = () => {
const params: lineParams = {
start: [32, canvas.height - 32],
end: [canvas.width - 32, 48],
curveness: -0.3,
percent,
};
ctx.beginPath();
ctx.strokeStyle = '#b17ffe';
ctx.lineWidth = 1;
drawCurvePath(params);
percent = (percent + 1) % 100;
if (percent === 0) {
setTimeout(() => {
percent = 0;
drawCoordinate();
}, 500);
}
ctx.stroke();
requestAnimationFrame(drawQuadraticLine);
};
useEffect(() => {
initCanvas();
drawCoordinate();
drawQuadraticLine();
}, []);
return (
<div ref={canvasWrapRef}>
<canvas ref={canvasRef} />
</div>
);
};
export default LineAnimate;
3、css代碼:
{
border: 1px solid #e8eaec;
display: inline-block;
height: 176px;
margin-right: 32px;
vertical-align: top;
width: 176px;
&:hover {
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
}
4、效果圖:
随筆小結,不喜勿噴,謝謝。