1、创建heart.js文件
// 定义画布的宽度和高度,以及画布的中心点坐标const CANVAS_WIDTH = 640;const CANVAS_HEIGHT = 480;const CANVAS_CENTER_X = CANVAS_WIDTH / 2;const CANVAS_CENTER_Y = CANVAS_HEIGHT / 2;// 定义图像的放大因子,用于调整爱心的大小const IMAGE_ENLARGE_FACTOR = 11;// 定义爱心的颜色const HEART_COLOR = "#FF69B4";// 生成爱心上的一个点的坐标function generateHeartCoordinate(t, shrinkRatio = IMAGE_ENLARGE_FACTOR) { // 使用参数t计算原始的x和y坐标 let x = 16 * (Math.sin(t) ** 3); let y = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)); // 根据缩放比例调整x和y坐标 x *= shrinkRatio; y *= shrinkRatio; // 将坐标转换到画布的中心 x += CANVAS_CENTER_X; y += CANVAS_CENTER_Y; // 返回调整后的坐标,向下取整以确保坐标为整数 return { x: Math.floor(x), y: Math.floor(y) };}// 绘制爱心的函数function drawHeart(context, ratio, frame) { // 清空画布 context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); // 绘制爱心的光晕部分 let heartHaloPoints = new Set(); for (let i = 0; i < 3000 + 4000 * Math.abs(customCurve(frame / 10 * Math.PI) ** 2); i++) { // 生成一个随机的t值,用于计算坐标 let t = Math.random() * 4 * Math.PI; // 使用generateHeartCoordinate函数计算坐标,并使用一个更大的缩放比例来使光晕更大 let { x, y } = generateHeartCoordinate(t, 11.5); // 计算一个力的大小,用于调整光晕的扩散效果 let force = -1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.6); // 根据力的大小调整x和y坐标 x -= ratio * force * (x - CANVAS_CENTER_X); y -= ratio * force * (y - CANVAS_CENTER_Y); // 添加一些随机的偏移量,使光晕更加自然 x += Math.floor(Math.random() * 29 - 14); y += Math.floor(Math.random() * 29 - 14); // 使用fillRect函数绘制一个2x2的正方形,作为光晕的一个点 context.fillStyle = HEART_COLOR; context.fillRect(x, y, 2, 2); } // 绘制爱心的主体部分 let allPoints = []; // 存储所有点的数组 let originalPoints = new Set(); // 存储原始点的集合 let edgeDiffusionPoints = new Set(); // 存储边缘扩散点的集合 let centerDiffusionPoints = new Set(); // 存储中心扩散点的集合 // 生成原始点 for (let i = 0; i < 2000; i++) { let t = Math.random() * 2 * Math.PI; let { x, y } = generateHeartCoordinate(t); originalPoints.add({ x, y }); } // 从原始点生成边缘扩散点 originalPoints.forEach(({ x, y }) => { for (let i = 0; i < 3; i++) { let newX = x, newY = y; newX += Math.random() * 5 - 2.5; newY += Math.random() * 5 - 2.5; edgeDiffusionPoints.add({ x: newX, y: newY }); } }); // 生成中心扩散点 for (let i = 0; i < 6000; i++) { let { x, y } = Array.from(originalPoints)[Math.floor(Math.random() * originalPoints.size)]; let newX = x, newY = y; newX += Math.random() * 15 - 7.5; newY += Math.random() * 15 - 7.5; centerDiffusionPoints.add({ x: newX, y: newY }); } // 根据力的大小调整所有点的坐标,并将它们添加到allPoints数组中 originalPoints.forEach(({ x, y }) => { let newX = x, newY = y; let force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.52); newX -= ratio * force * (x - CANVAS_CENTER_X); newY -= ratio * force * (y - CANVAS_CENTER_Y); allPoints.push({ x: Math.floor(newX), y: Math.floor(newY), size: Math.floor(Math.random() * 3 + 1) }); }); edgeDiffusionPoints.forEach(({ x, y }) => { let newX = x, newY = y; let force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.52); newX -= ratio * force * (x - CANVAS_CENTER_X); newY -= ratio * force * (y - CANVAS_CENTER_Y); allPoints.push({ x: Math.floor(newX), y: Math.floor(newY), size: Math.floor(Math.random() * 2 + 1) }); }); centerDiffusionPoints.forEach(({ x, y }) => { let newX = x, newY = y; let force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.52); newX -= ratio * force * (x - CANVAS_CENTER_X); newY -= ratio * force * (y - CANVAS_CENTER_Y); allPoints.push({ x: Math.floor(newX), y: Math.floor(newY), size: Math.floor(Math.random() * 2 + 1) }); }); // 绘制所有点 allPoints.forEach(({ x, y, size }) => { context.fillStyle = HEART_COLOR; context.fillRect(x, y, size, size); }); // 添加文本 "stormsha" 到爱心中间 context.font = "bold 24px Arial"; context.fillStyle = HEART_COLOR; context.textAlign = "center"; context.fillText("stormsha", CANVAS_CENTER_X, CANVAS_CENTER_Y);}// 定义一个自定义的曲线函数,用于调整爱心的跳动效果function customCurve(p) { return 2 * (2 * Math.sin(4 * p)) / (2 * Math.PI);}// 当文档加载完成时,开始动画document.addEventListener("DOMContentLoaded", () => { // 获取画布和2d上下文 const canvas = document.getElementById("heartCanvas"); const context = canvas.getContext("2d"); // 定义一些变量,用于控制动画 let ratio = 0; // 力的大小比例 let frame = 0; // 当前帧数 // 动画函数 function animate() { // 根据当前帧数计算力的大小比例 ratio = 10 * customCurve(frame / 10 * Math.PI); // 调用drawHeart函数绘制爱心 drawHeart(context, ratio, frame); // 增加帧数 frame++; // 请求下一帧的动画 requestAnimationFrame(animate); } // 开始动画 animate();});
这段代码实现了一个跳动的爱心动画。首先,它定义了一些常量,如画布的尺寸和爱心的颜色。然后,它定义了一个generateHeartCoordinate
函数,用于生成爱心上的一个点的坐标。
接下来,它定义了一个drawHeart
函数,用于绘制爱心。这个函数首先清空画布,然后绘制爱心的光晕部分,再绘制爱心的主体部分。最后,它添加了一些文本到爱心中间。
在文档加载完成后,代码会获取画布和2d上下文,并定义一些变量,如力的大小比例和当前帧数。然后,它会调用animate
函数开始动画。
这个函数会根据当前帧数计算力的大小比例,然后调用drawHeart
函数绘制爱心,并请求下一帧的动画。通过不断调用animate函数,就可以实现爱心的跳动效果。
2、创建love.html文件
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <!-- 指定文档使用UTF-8字符编码 --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 设置视口,以适应不同设备屏幕大小 --> <title>Beating Heart</title> <!-- 文档标题 --> <style> /* 样式表定义 */ body { background-color: black; /* 设置页面背景颜色为黑色 */ margin: 0; /* 去除页面默认边距 */ overflow: hidden; /* 隐藏超出画布的内容 */ } canvas { display: block; /* 将画布作为块级元素显示 */ margin: auto; /* 使画布在页面中居中 */ } .text { position: absolute; /* 将文本绝对定位 */ top: 50%; /* 使文本距离页面顶部50% */ left: 50%; /* 使文本距离页面左侧50% */ transform: translate(-50%, -50%); /* 将文本向左、向上平移自身宽度和高度的一半,实现在页面中居中 */ color: #FF69B4; /* 设置文本颜色为浅粉色 */ font-family: '宋体'; /* 设置文本字体为宋体 */ font-size: 18px; /* 设置文本大小为18像素 */ text-align: center; /* 使文本内容居中 */ } </style></head><body> <!-- 页面内容定义 --> <canvas id="heartCanvas" width="640" height="480"></canvas> <!-- 创建一个宽640像素、高480像素的画布,并设置其ID为heartCanvas --> <div class="text" style="top: 5%;">点燃我 温暖你</div> <!-- 创建一个文本元素,内容为“点燃我 温暖你”,并设置其样式类为text,距离页面顶部5% --> <script src="heart.js"></script> <!-- 引入heart.js脚本文件 --></body></html>
3、总结
把 heart.js
、love.html
文件放在同一个文件夹下,用浏览器打开 love.html
文件即可看到效果