《点燃我,温暖你》中李洵编写的爱心 html 代码让很多人感到十分震撼,这段代码不仅显现了编程的吸引力,还让代码带上了诗意的色彩,现在就详细解读这段代码的特别之处。
偶像剧《点燃我,温暖你》里,李洵用编程语言,编写出爱心图案,以此向女主角传递爱慕之情。这个程序迅速走红,吸引众多人争相效仿。它展现了剧中角色的真挚情感,也让观众领悟到编程可以如此富有诗意,进而引发人们关注编程中的创意构思与情感传递。
这段爱心代码在构造上具备基础的 HTML 框架,涵盖了头部和主体两个部分。头部部分负责设定页面的基本参数,主体部分则用来塑造爱心的整体轮廓。它熟练地结合了 HTML 的标记以及 CSS 的样式规范,借助反复套嵌和特性配置,清晰地勾勒出爱心的形态,为后续的动态表现做好了铺垫。
代码的关键在于灵活运用 css 的图形特性与色彩过渡手法。通过调整元素的尺寸、边框弧度等参数,能够构建出精致细腻的爱心外形。接着借助色彩过渡功能,为爱心增添逼真的明暗层次,让整体效果显得富有立体感,如同一个真正在搏动的心脏。
为了让爱心显得更富活力,程序中加入了动态效果,借助关键帧技术,爱心能够实现明灭、大小调整等变化,这样的处理增强了画面的吸引力,也更生动地表达了那种炽热的情感,使人感觉仿佛看到了一个充满活力、持续搏动的爱心。
李洵编写的这段充满善意的程序,给许多人开辟了代码创意展现的新途径,让大家明白编程并非只有枯燥的命令,同样可以化作温馨的言辞传递情感。对于学习编程的人来说,这算是一个很不错的范例,能够帮助他们掌握代码的革新用法,从而点燃更多创造性的火花和思路。
以下是实现代码
<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 标题将动态设置 -->
<title>一起过七夕呀!</title>
<style>
/* 页面样式,让画布居中并充满屏幕 */
body, html {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: black;
}
canvas {
background-color: black;
}
</style>
</head>
<body>
<!-- 用于绘制爱心的画布 -->
<canvas id="heartCanvas" width="640" height="640"></canvas>
<script>
// --- 配置常量 ---
const CANVAS_WIDTH = 640; // 画布的宽
const CANVAS_HEIGHT = 640; // 画布的高
const CANVAS_CENTER_X = CANVAS_WIDTH / 2; // 画布中心的X轴坐标
const CANVAS_CENTER_Y = CANVAS_HEIGHT / 2; // 画布中心的Y轴坐标
const IMAGE_ENLARGE = 11; // 放大比例
const HEART_COLOR = "#ff7171"; // 心的颜色
// 获取画布及其2D上下文
const canvas = document.getElementById('heartCanvas');
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
const ctx = canvas.getContext('2d');
// --- 核心函数 (Python to JavaScript) ---
/**
* “爱心函数生成器”
* @param {number} t - 参数
* @param {number} shrink_ratio - 放大比例
* @returns {{x: number, y: number}} 坐标
*/
function heart_function(t, shrink_ratio = IMAGE_ENLARGE) {
// 基础函数
const x = 16 * (Math.sin(t) ** 3);
const y = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
// 放大
const finalX = x * shrink_ratio;
const finalY = y * shrink_ratio;
// 移到画布中央
return {
x: finalX + CANVAS_CENTER_X,
y: finalY + CANVAS_CENTER_Y
};
}
/**
* 随机内部扩散
* @param {number} x - 原x
* @param {number} y - 原y
* @param {number} beta - 强度
* @returns {{x: number, y: number}} 新坐标
*/
function scatter_inside(x, y, beta = 0.15) {
const ratio_x = -beta * Math.log(Math.random());
const ratio_y = -beta * Math.log(Math.random());
const dx = ratio_x * (x - CANVAS_CENTER_X);
const dy = ratio_y * (y - CANVAS_CENTER_Y);
return { x: x - dx, y: y - dy };
}
/**
* 抖动 (用于光环)
* @param {number} x - 原x
* @param {number} y - 原y
* @param {number} ratio - 比例
* @returns {{x: number, y: number}} 新坐标
*/
function shrink(x, y, ratio) {
const force = -1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.6);
const dx = ratio * force * (x - CANVAS_CENTER_X);
const dy = ratio * force * (y - CANVAS_CENTER_Y);
return { x: x - dx, y: y - dy };
}
/**
* 自定义曲线函数,调整跳动周期
* @param {number} p - 参数
* @returns {number} 正弦
*/
function curve(p) {
return 2 * (3 * Math.sin(4 * p)) / (2 * Math.PI);
}
/**
* 生成指定范围内的随机整数
* @param {number} min
* @param {number} max
* @returns {number}
*/
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 从数组中随机选择一个元素
* @param {Array<any>} arr
* @returns {any}
*/
function randomChoice(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
// --- 爱心类 (Python Class to JavaScript Class) ---
class Heart {
constructor(generate_frame = 20) {
this._points = new Set(); // 原始爱心坐标集合
this._edge_diffusion_points = new Set(); // 边缘扩散效果点坐标集合
this._center_diffusion_points = new Set();// 中心扩散效果点坐标集合
this.all_points = {}; // 每帧动态点坐标
this.generate_frame = generate_frame;
this.build(2000);
for (let frame = 0; frame < generate_frame; frame++) {
this.calc(frame);
}
}
build(number) {
// 爱心轮廓
for (let i = 0; i < number; i++) {
const t = Math.random() * 2 * Math.PI; // 随机参数
const { x, y } = heart_function(t);
this._points.add({ x: Math.floor(x), y: Math.floor(y) });
}
// 爱心内扩散
for (const point of this._points) {
for (let i = 0; i < 3; i++) {
const { x, y } = scatter_inside(point.x, point.y, 0.05);
this._edge_diffusion_points.add({ x: Math.floor(x), y: Math.floor(y) });
}
}
// 爱心内再次扩散
const point_list = Array.from(this._points);
for (let i = 0; i < 4000; i++) {
const random_point = randomChoice(point_list);
const { x, y } = scatter_inside(random_point.x, random_point.y, 0.17);
this._center_diffusion_points.add({ x: Math.floor(x), y: Math.floor(y) });
}
}
calc_position(x, y, ratio) {
const force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520); // 魔法参数
const dx = ratio * force * (x - CANVAS_CENTER_X) + randomInt(-1, 1);
const dy = ratio * force * (y - CANVAS_CENTER_Y) + randomInt(-1, 1);
return { x: x - dx, y: y - dy };
}
calc(frame) {
const ratio = 10 * curve(frame / 10 * Math.PI); // 圆滑的周期的缩放比例
const halo_radius = Math.floor(4 + 6 * (1 + curve(frame / 10 * Math.PI)));
const halo_number = Math.floor(3000 + 4000 * Math.abs(curve(frame / 10 * Math.PI) ** 2));
const all_points = [];
const heart_halo_point = new Set(); // 使用Set防止光环点重复
// 光环
for (let i = 0; i < halo_number; i++) {
const t = Math.random() * 2 * Math.PI;
let { x, y } = heart_function(t, 11.6); // 魔法参数
({ x, y } = shrink(x, y, halo_radius));
if (!heart_halo_point.has(`${Math.floor(x)},${Math.floor(y)}`)) {
heart_halo_point.add(`${Math.floor(x)},${Math.floor(y)}`);
x += randomInt(-14, 14);
y += randomInt(-14, 14);
const size = randomChoice([1, 2, 2]);
all_points.push({ x, y, size });
}
}
const process_points = (points_set) => {
for (const point of points_set) {
let { x, y } = this.calc_position(point.x, point.y, ratio);
const size = (points_set === this._points) ? randomInt(1, 3) : randomInt(1, 2);
all_points.push({ x, y, size });
}
};
// 轮廓
process_points(this._points);
// 内容
process_points(this._edge_diffusion_points);
process_points(this._center_diffusion_points);
this.all_points[frame] = all_points;
}
render(render_frame) {
const current_points = this.all_points[render_frame % this.generate_frame];
ctx.fillStyle = HEART_COLOR;
// 绘制前重置阴影,避免粒子也带上阴影
ctx.shadowBlur = 0;
ctx.shadowColor = 'transparent';
for (const {x, y, size} of current_points) {
ctx.fillRect(x, y, size, size);
}
}
}
/**
* ✨ 新增:从URL获取参数的辅助函数
* @param {string} name 参数名
* @returns {string | null} 参数值或null
*/
function getUrlParameter(name) {
const params = new URLSearchParams(window.location.search);
return params.get(name);
}
/**
* ✨ 修改:在画布中央绘制文本,文本内容来自URL
*/
function drawText() {
// 从URL获取'text'参数并解码
const urlText = getUrlParameter('text');
// 如果URL中没有文本,则使用默认文本
const text = urlText ? decodeURIComponent(urlText) : "一起过七夕呀!";
// 设置字体、大小和样式
ctx.font = 'bold 38px "KaiTi", "STKaiti", "Microsoft YaHei", cursive';
ctx.fillStyle = '#ffc7c7';
// 设置文本水平和垂直居中
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// 为文字添加一个匹配爱心颜色的柔和光晕
ctx.shadowColor = 'rgba(255, 113, 113, 0.8)'; // 使用爱心颜色的半透明光晕
ctx.shadowBlur = 10; // 光晕的模糊半径
// 在画布中心绘制文字
ctx.fillText(text, CANVAS_CENTER_X, CANVAS_CENTER_Y);
}
// --- 动画主循环 ---
let frame = 0;
const heart = new Heart();
let lastTime = 0;
const frameInterval = 160;
function draw(currentTime) {
requestAnimationFrame(draw);
const deltaTime = currentTime - lastTime;
if (deltaTime > frameInterval) {
lastTime = currentTime - (deltaTime % frameInterval);
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); // 清空画布
heart.render(frame); // 渲染爱心粒子
drawText(); // 在爱心上渲染文字
frame++; // 帧数增加
}
}
// ✨ 修改:动态设置页面标题
const pageTitleText = getUrlParameter('text');
if (pageTitleText) {
document.title = decodeURIComponent(pageTitleText);
}
// 启动动画循环
requestAnimationFrame(draw);
</script>
</body></html>