<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>定位图标内的双重波浪特效</title>
</head>
<body>
<canvas id="canvas-online-rate" width="80" height="80"></canvas>
<script>
var ctx = document.querySelector("#canvas-online-rate").getContext("2d");
var k0 = 0;
var k1 = Math.PI / 2;
// 开始
function init() {
k0 -= 0.05; // X轴偏移量
k1 -= 0.025; // X轴偏移量
// 清空重绘
ctx.clearRect(0, 0, 80, 80);
// 外轮廓
ctx.beginPath();
const circle = circleCurve(ctx);
const bc = quadraticBesselCurve(ctx);
ctx.closePath();
ctx.strokeStyle = "#09bac0";
ctx.stroke();
// 绘制函数图像1
ctx.beginPath();
deawWave(ctx, bc, circle, k0);
ctx.closePath();
ctx.fillStyle = "#086873aa";
ctx.fill();
// 绘制函数图像2
ctx.beginPath();
deawWave(ctx, bc, circle, k1);
ctx.fillStyle = "#09bac0";
ctx.fill();
// 动画
window.requestAnimationFrame(init);
}
/**
* 创建半圆弧
* 返回半圆弧点集合和圆曲线方程
*/
function circleCurve(ctx) {
const o = { x: 40, y: 30 }; // 圆心
const r = 20; // 半径
// 圆的曲线方程:根据x求y
const circleY = (x) => {
const y = Math.sqrt(r * r - (x - o.x) * (x - o.x)) + o.y;
if (y > 30) {
return 30 - (y - 30);
}
return y;
};
ctx.arc(o.x, o.y, r, 0, Math.PI, true);
// 保存圆的曲线点集合
const circle = [];
for (let i = 0; i < 2 * r; i++) {
const x = o.x - r + i;
const y = circleY(x);
circle.push({ x, y });
}
return circle;
}
/**
* 创建二次贝塞尔曲线
* 返回二次贝塞尔曲线点集合
*/
function quadraticBesselCurve(ctx) {
const bcList = [];
// 左侧函数点
const lp0 = { x: 20, y: 30 };
const lp1 = { x: 20, y: 50 };
const lp2 = { x: 40, y: 70 };
// 右侧函数点
const rp0 = { x: 40, y: 70 };
const rp1 = { x: 60, y: 50 };
const rp2 = { x: 60, y: 30 };
// 根据设定好的点绘制曲线并保存点坐标
ctx.quadraticCurveTo(lp1.x, lp1.y, lp2.x, lp2.y);
for (let i = 0; i < 40; i++) {
const t = i / 40;
const x = (1 - t) * (1 - t) * lp0.x + 2 * t * (1 - t) * lp1.x + t * t * lp2.x;
const y = (1 - t) * (1 - t) * lp0.y + 2 * t * (1 - t) * lp1.y + t * t * lp2.y;
bcList.push({ x, y });
}
ctx.quadraticCurveTo(rp1.x, rp1.y, rp2.x, rp2.y);
for (let i = 1; i <= 40; i++) {
const t = i / 40;
const x = (1 - t) * (1 - t) * rp0.x + 2 * t * (1 - t) * rp1.x + t * t * rp2.x;
const y = (1 - t) * (1 - t) * rp0.y + 2 * t * (1 - t) * rp1.y + t * t * rp2.y;
bcList.push({ x, y });
}
return bcList;
}
/**
* 绘制波浪区域范围
*/
function deawWave(ctx, bc, circle, k) {
const a = 3; // 振幅
const w = (3 * Math.PI) / 40; // 角速度
const y = 30; // 偏距 10~70
// 正弦函数
const sinFunc = (x, k) => {
return a * Math.sin(w * x + k) + y;
};
let bcStartIdx = 0;
let bcEndIdx = bc.length;
if (y >= 30) {
for (let i = 0; i < bc.length; i++) {
const ix = bc[i].x;
const iy = sinFunc(ix, k);
if (iy <= bc[i].y) {
if (!bcStartIdx) {
bcStartIdx = i + 1;
}
ctx.lineTo(ix, iy);
} else if (i > bc.length / 2) {
bcEndIdx = i;
break;
}
}
} else {
for (let i = 0; i < circle.length; i++) {
const ix = circle[i].x;
const iy = sinFunc(ix, k);
if (iy > circle[i].y) {
ctx.lineTo(ix, iy);
} else {
ctx.lineTo(ix, circle[i].y);
}
}
}
const newBc = bc.slice(bcStartIdx, bcEndIdx).reverse();
for (let i = 0; i < newBc.length; i++) {
ctx.lineTo(newBc[i].x, newBc[i].y);
}
ctx.closePath();
}
init();
</script>
</body>
</html>