【HTML】情人节给npy一颗炫酷的爱心

闲谈

兄弟们,这不情人节快要到了,我该送女朋友什么🎁呢?哦,对了,差点忘了,我好像没有女朋友。
image.pngimage.png
不过这不影响我们要过这个节日,我们可以学习技术。举个简单的🌰: 比如说,今天我们学习了如何画一颗炫酷的💗,以后找到了女朋友忘准备礼物了,是不是可以用这个救救场,🐶。

开干

首先,我们需要画一个💗的形状出来,例如下面这样
image.png
这个简单,我们通过豆包搜一波公式即可。公式如下:

x ( t ) = 16 sin ⁡ 3 ( t ) x(t) = 16\sin^3(t) x(t)=16sin3(t)
y ( t ) = 13 c o s ( t ) − 5 c o s ( 2 t ) − 2 cos ⁡ ( 3 t ) − c o s ( 4 t ) y(t) = 13cos(t) - 5cos(2t) - 2\cos(3t) - cos(4t) y(t)=13cos(t)5cos(2t)2cos(3t)cos(4t)


思路: 利用上面的公式,我们只需要根据许许多多的t去求得x,y的坐标,然后将这些点画出来即可。
使用Canvas时用到的一些函数解释,这里moveTolineTo还是有点上头的:

// 获取到一个绘图环境对象,这个对象提供了丰富的API来执行各种图形绘制和图像处理操作
ctx = canvas.getContext('2d');
/** 
该方法用于在当前路径上从当前点画一条直线到指定的 (x, y) 坐标。
当调用 lineTo 后,路径会自动延伸到新指定的点,并且如果之前已经调用了 beginPath() 或 moveTo(),则这条线段会连接到前一个点。
要看到实际的线条显示在画布上,需要调用 stroke() 方法。
*/
ctx.lineTo(x, y);
/**
此方法用于移动当前路径的起始点到指定的 (x, y) 坐标位置,但不会画出任何可见的线条。
它主要用于开始一个新的子路径或者在现有路径之间创建空隙。当你想要从一个地方不连续地移动到另一个地方绘制时,就需要使用 moveTo。
*/
ctx.moveTo(x, y);

友情提示:上面的函数是个倒的爱心,所以Y轴要取负数。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>LoveCanvas</title>
    <style>
      body {
        background: black;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas"></canvas>
  </body>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const themeColor = "#d63e83";
    // 爱心线的实体
    let loveLine = null;
    // 保存爱心方程的坐标
    let XYPoint = [];
    // 线条宽度,可自定义修改
    const lineWidth = 5;

    /**得到爱心方程的坐标 **/
    function getXYPoint() {
      const pointArr = [];
      const enlargeFactor = 20;
      for (let t = 0; t < 2 * Math.PI; t += 0.01) {
        const x = 16 * Math.pow(Math.sin(t), 3) * enlargeFactor;
        const y =
          -(
            13 * Math.cos(t) -
            5 * Math.cos(2 * t) -
            2 * Math.cos(3 * t) -
            Math.cos(4 * t)
          ) * enlargeFactor;
        // 将爱心的坐标进行居中
        pointArr.push({ x: canvas.width / 2 + x, y: canvas.height / 2 + y });
      }
      return pointArr;
    }

    class LoveLine {
      constructor(pointXY) {
        this.pointXY = pointXY;
      }
      draw() {
        for (let point of this.pointXY) {
          ctx.lineTo(point.x, point.y);
          ctx.moveTo(point.x, point.y);
        }
        ctx.strokeStyle = themeColor;
        ctx.lineWidth = lineWidth;
        ctx.stroke();
        ctx.fill();
      }
    }

    function initLoveLine() {
      XYPoint = getXYPoint();
      loveLine = new LoveLine(XYPoint);
      loveLine.draw();
    }

    function init() {
      const width = window.innerWidth;
      const height = window.innerHeight;
      canvas.width = width;
      canvas.height = height;
      initLoveLine();
    }

    // 如果需要保持在窗口大小变化时也实时更新canvas尺寸
    window.onresize = init;
    init();
  </script>
</html>

粒子特效

这么快就做好了,是不是显得不是很够诚意?
image.pngimage.png
我们可以加入一波粒子特效,这里我采用的方案是基于之前的Canvas+requestAnimationFrame来做。
效果如下:
123.gif
首先什么是requestAnimationFrame呢?参见MDN

你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

也就是我们可以使用这个函数达到每10ms刷新一次界面达到动态的效果。
首先我们定义一个粒子

// 粒子点的类
class Dot {
  constructor(x, y, initX, initY) {
    // 原始点的坐标,用来圈定范围
    this.initX = initX;
    this.initY = initY;
    this.x = x;
    this.y = y;
    this.r = 1;
    // 粒子移动的速度,也就是下一帧,粒子在哪里出现
    this.speedX = Math.random() * 2 - 1;
    this.speedY = Math.random() * 2 - 1;
    // 这个粒子最远能跑多远
    this.maxLimit = 15;
  }
  // 绘制每一个粒子的方法
  draw() {
    ctx.beginPath();
    ctx.fillStyle = themeColor;
    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.closePath();
  }
  move() {
    if (Math.abs(this.x - this.initX) >= this.maxLimit)
      this.speedX = -this.speedX;
    if (Math.abs(this.x - this.y) >= this.maxLimit)
      this.speedY = -this.speedY;
    this.x += this.speedX;
    this.y += this.speedY;
    this.draw();
  }
}

我们在定义两个使用到粒子函数的方法.

  1. initDots函数,该函数主要是将粒子点初始化,并且画出来。
function initDots(x, y) {
  XYPoint = getXYPoint();
  dots = [];
  for (let point of XYPoint) {
    for (let i = 0; i < SINGLE_DOT_NUM; i++) {
      const border = Math.random() * 5;
      const dot = new Dot(
        border + point.x,
        border + point.y,
        point.x,
        point.y
      );
      dot.draw();
      dots.push(dot);
    }
  }
}
  1. moveDots函数,顾名思义,也就是移动粒子点
function moveDots() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  loveLine.draw();
  for (const dot of dots) {
    dot.move();
  }
  animationFrame = window.requestAnimationFrame(moveDots);
}

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>LoveCanvas</title>
    <style>
      body {
        background: black;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas"></canvas>
  </body>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const themeColor = "#d63e83";
    // 爱心线的实体
    let loveLine = null;
    // 保存爱心方程的坐标
    let XYPoint = [];
    // 线条宽度,可自定义修改
    const lineWidth = 5;
    // 每个原来的点对应的粒子数目
    const SINGLE_DOT_NUM = 15;
    // 粒子点的集合
    let dots = [];
    let animationFrame = null;


    /**得到爱心方程的坐标 **/
    function getXYPoint() {
      const pointArr = [];
      const enlargeFactor = 20;
      for (let t = 0; t < 2 * Math.PI; t += 0.01) {
        const x = 16 * Math.pow(Math.sin(t), 3) * enlargeFactor;
        const y =
          -(
            13 * Math.cos(t) -
            5 * Math.cos(2 * t) -
            2 * Math.cos(3 * t) -
            Math.cos(4 * t)
          ) * enlargeFactor;
        // 将爱心的坐标进行居中
        pointArr.push({ x: canvas.width / 2 + x, y: canvas.height / 2 + y });
      }
      return pointArr;
    }

    class LoveLine {
      constructor(pointXY) {
        this.pointXY = pointXY;
      }
      draw() {
        for (let point of this.pointXY) {
          ctx.lineTo(point.x, point.y);
          ctx.moveTo(point.x, point.y);
        }
        ctx.strokeStyle = themeColor;
        ctx.lineWidth = lineWidth;
        ctx.stroke();
        ctx.fill();
      }
    }

    function initLoveLine() {
      XYPoint = getXYPoint();
      loveLine = new LoveLine(XYPoint);
      loveLine.draw();
    }

    // 粒子点的类
    class Dot {
      constructor(x, y, initX, initY) {
        this.initX = initX;
        this.initY = initY;
        this.x = x;
        this.y = y;
        this.r = 1;
        this.speedX = Math.random() * 2 - 1;
        this.speedY = Math.random() * 2 - 1;
        this.maxLimit = 15;
      }
      // 绘制每一个粒子的方法
      draw() {
        ctx.beginPath();
        ctx.fillStyle = themeColor;
        ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
        ctx.fill();
        ctx.closePath();
      }
      move() {
        if (Math.abs(this.x - this.initX) >= this.maxLimit)
          this.speedX = -this.speedX;
        if (Math.abs(this.x - this.y) >= this.maxLimit)
          this.speedY = -this.speedY;
        this.x += this.speedX;
        this.y += this.speedY;
        this.draw();
      }
    }

    function initLoveLine() {
      XYPoint = getXYPoint();
      loveLine = new LoveLine(XYPoint);
      loveLine.draw();
    }

    function initDots(x, y) {
      XYPoint = getXYPoint();
      dots = [];
      for (let point of XYPoint) {
        for (let i = 0; i < SINGLE_DOT_NUM; i++) {
          const border = Math.random() * 5;
          const dot = new Dot(
            border + point.x,
            border + point.y,
            point.x,
            point.y
          );
          dot.draw();
          dots.push(dot);
        }
      }
    }

    function moveDots() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      loveLine.draw();
      for (const dot of dots) {
        dot.move();
      }
      animationFrame = window.requestAnimationFrame(moveDots);
    }

    function init() {
      const width = window.innerWidth;
      const height = window.innerHeight;
      canvas.width = width;
      canvas.height = height;
      if (animationFrame) {
        window.cancelAnimationFrame(animationFrame);
        ctx.clearRect(0, 0, canvas.width, canvas.height);
      }
      initLoveLine();
      initDots();
      moveDots();
    }
	// 如果需要保持在窗口大小变化时也实时更新canvas尺寸
    window.onresize = init;
    init();
  </script>
</html>

注:大家如果觉得中间那条线不好看,可以去掉initLoveLine()即可。

最后

祝今天有情人终成眷属,无情人早日找到心仪的另一半,哈哈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/386340.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

寒假作业2024.2.14

1.请编程实现二维数组的杨辉三角 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <unistd.h> int main(int argc, const char *argv[]) {int n;printf("please enter n:");scanf("%d&…

数据库基本操作2

一.DML&#xff08;Data Manipulation Language&#xff09; 用来对数据库中表的数据记录进行更新 关键字&#xff1a;增删改 插入insert 删除delete 更新update 1.数据插入 insert into 表&#xff08;列名1&#xff0c;列名2&#xff0c;列名3……&#xff09;values&a…

【STM32 CubeMX】STM32中断体系结构

文章目录 前言一、中断体系的比喻二、中断的内部结构2.1 EXTI触发方式 2.2 NVIC2.3 cpu与中断2.4 外部中断控制器框图上升沿触发选择寄存器屏蔽/使能寄存器等待处理寄存器 2.5 中断优先级 总结 前言 一、中断体系的比喻 STM32中断体系如下图所示&#xff1a; 一座大型建筑物…

VueCLI核心知识1:ref属性、props配置、mixin混入

1 ref 属性 ref属性类似于js原生获取DOM元素 <template><div><h1 v-text"msg" ref"title"></h1><button click"showDom">点我输出上方的Dom元素</button><School ref"sch"></School>…

AutoGen实战应用(三):多代理协作的数据可视化

之前我完成了关于AutoGen的两篇博客&#xff0c;还没有读过这两篇博客的朋友可以先阅读以下&#xff0c;这样有助于对AutoGen的初步了解&#xff1a; AutoGen实战应用(一)&#xff1a;代码生成、执行和调试_autogen 支持的model-CSDN博客 AutoGen实战应用(二)&#xff1a;多代…

详解结构体内存对齐及结构体如何实现位段~

目录 ​编辑 一&#xff1a;结构体内存对齐 1.1对齐规则 1.2.为什么存在内存对齐 1.3修改默认对齐数 二.结构体实现位段 2.1什么是位段 2.2位段的内存分配 2.3位段的跨平台问题 2.4位段的应用 2.5位段使用的注意事项 三.完结散花 悟已往之不谏&#xff0c;知来者犹可…

2.14作业

1.请编程实现二维数组的杨辉三角。 2.请编程实现二维数组计算每一行的和以及列和。 3.请编程实现二维数组计算第二大值。 4.请使用非函数方法实现系统函数strcat,strcmp,strcpy,strlen. strcat: strcmp: strcpy: strlen:

JVM性能调优 - 服务器性能排查(7)

在排查生产环境的性能问题时,以下是一些常见的步骤和技巧: 监控系统资源:使用系统监控工具(如top、htop、nmon等)来监控服务器的CPU使用率、内存使用率、磁盘IO等系统资源情况。这可以帮助你了解系统的整体负载情况,是否存在资源瓶颈。 分析日志:查看应用程序的日志文件…

操作系统——1.3 操作系统运行环境

1.3 操作系统运行环境 一、概念 操作系统运行机制总览 应用程序与内核程序 特权指令与非特权指令 内核态与用户态 CPU在内核态与用户态的切换 操作系统运行机制的总结 中断和异常总览 中断的作用 中断的类型 内中断的例子 外中断的例子 中断的分类&am…

【51单片机】利用【时间延迟】的原理规避【按键抖动问题】

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 本章是51LCD单片机设计的一个环节&#xff0c;完整可前往相应博客查看完整传送门 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下…

PWR电源控制

PWR电源 PWR简介 PWR&#xff08;Power Control&#xff09;电源控制 PWR负责管理STM32内部的电源供电部分&#xff0c;可以实现可编程电压监测器和低功耗模式的功能 可编程电压监测器&#xff08;PVD&#xff09;可以监控VDD电源电压&#xff0c;当VDD下降到PVD阀值以下或上…

day40 Bootstrap文字背景颜色+网格系统(简单示例)

目录 Bootstrap5 颜色相关Bootstrap 网格系统网格类Bootstrap 自动布局等宽响应式列不等宽响应式列 Bootstrap5 颜色相关 <div class"container" style"background-color:plum"><h2>代表指定意义的文本颜色</h2><p class"text-m…

位运算总结(Java)

目录 位运算概述 位运算符 位运算的优先级 位运算常见应用 1. 给定一个数n&#xff0c;判断其二进制表示中的第x位是0还是1 2. 将数n的二进制表示中的第x位修改为1 3. 将数n的二进制表示中的第x位修改为0 4. 位图 例题&#xff1a;判断字符是否唯一 5. 提取数n的二进制…

【开源】SpringBoot框架开发企业项目合同信息系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合同签订模块2.4 合同预警模块2.5 数据可视化模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 合同审批表3.2.2 合同签订表3.2.3 合同预警表 四、系统展示五、核心代码5.1 查询合同…

C++ JSON解析

JSON解析 JSONCPPC实现JSON解析器 JSONCPP JSONCPP源码链接&#xff1a;https://github.com/open-source-parsers/jsoncpp JSOCPP源码下载以后&#xff0c;首先复制一份include文件夹下的json文件夹&#xff0c;头文件留着后续备用。 使用Cmake生成项目。在IDE中编译jsoncpp_…

【算法分析与设计】环形链表

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次…

缓慢变化维 常用的处理方法

什么是缓慢变化维 维度 在数仓中&#xff0c;表往往会被划分成两种类型&#xff0c;一种是 事实表&#xff0c;另一种是维度表&#xff0c;举个例子&#xff0c;比如说&#xff1a; ❝ 2024年2月14日&#xff0c;健鑫在12306上买了两张火车票&#xff0c;每张火车票400元&…

TinUI v5预发布记录

TinUI v5预发布记录 前言新控件滚动选择框菜单按钮 新样式pre1pre2pre3 新功能导入字体文件 前言 TinUI是一个从2021年正式开始并一直维护到现在的小项目&#xff0c;中间经过了四代版本的更新。因为一些原因&#xff0c;2023年&#xff0c;TinUI-4后更新较少。 TinUI发展历程…

jmeter-问题二:JMeter进行文件上传时,常用的几种MIME类型

以下是一些常用的MIME类型及其对应的文件扩展名&#xff1a; 文本类型: text/plain: 通常用于纯文本文件&#xff0c;如 .txt 文件。 text/html: 用于HTML文档&#xff0c;即 .html 文件。 application/msword: Microsoft Word文档&#xff0c;即 .doc 和 .docx 文件。 图像…

英伟达市值超越谷歌!老黄隔空回应Altman的巨资筹款计划:没必要,真的没必要!

凭借算力上的霸主地位&#xff0c;英伟达正稳步成为科技领域的下一个巨头&#xff0c;在不久的15个月前&#xff0c;英伟达的市值还不足3000亿美元。然而&#xff0c;截至昨日&#xff0c;英伟达股价飙升使其市值达到了1.83万亿美元&#xff0c;超越了Alphabet&#xff08;谷歌…