【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.mzph.cn/news/681610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于少样本学习、零样本学习、单样本学习中的support set和query set的概念解析

梳理一下关于少样本学习&#xff08;如few-shot learning&#xff09;、零样本学习&#xff08;zero-shot learning&#xff09;、单样本学习&#xff08;one-shot learning&#xff09;中的support set和query set的概念&#xff0c;以及为何测试集中会涉及到“unseen”&#…

Linux_进程

进程创建 进程退出码 进程等待 程序替换 Shell作为命令行解释器是一个进程&#xff0c;它也有自己的数据结构task_struct和代码和数据。为了防止用户输入的指令造成Shell崩溃&#xff0c;所以Shell执行用户输入的指令是通过创建一个子进程来执行的。例如lspwd等等。 一.进程…

数据工程工程师学习路线图

数据工程岗位要求 Skill Sets required: - Hands on experience enabling data via Adobe Analytics and/or Google Analytics - Understanding of how customer level data is captured and stitched with behavioural data - Experience working with Testing (QA) and D…

浏览器内核的主要功能模块介绍

浏览器内核是浏览器的核心部分&#xff0c;负责解析网页内容、渲染页面和处理用户交互。一个典型的浏览器内核主要包括以下几个功能模块&#xff1a; 1. **解析器&#xff08;Parser&#xff09;**&#xff1a; 解析器负责解析网页内容&#xff0c;包括HTML…

Django学习全纪录:Django开发环境的搭建

导言 对于Django,它是Python的一个开发框架,之前系统地学习过。遗憾的是,对于一些遇到的问题,没有及时地记录下来。因此,我将它重新捡起,进行学习和实践。从搭建环境开始,重新去学习它,期望可以从开发的角度,重新理解软件测试的工作。在今后的测试生涯中,如虎添翼。 …

【Java 数据结构】泛型进阶

泛型 1 什么是泛型2 引出泛型2.1 语法 3 泛型类的使用3.1 语法3.2 示例3.3 类型推导(Type Inference) 泛型是如何编译的擦除机制裸类型4 泛型的上界4.1 语法4.2 示例4.3 复杂示例 5 泛型方法5.1 定义语法5.2 示例5.3 使用示例-可以类型推导5.4 使用示例-不使用类型推导 6 通配符…

【RISC-V DSP设计】基于CEVA DSP架构的指令集分析(二)-函数列表

目录 表3-1&#xff1a;定点滤波器功能 表3-2&#xff1a;定点快速傅里叶变换&#xff08;FFT&#xff09;函数 表3-3&#xff1a;定点数学函数 表3-4&#xff1a;定点三角函数 表3-5&#xff1a;定点向量函数 表3-6&#xff1a;定点矩阵函数 表3-7&#xff1a;浮点滤波…

如何在极低成本硬件上落地人工智能算法 —— 分布式AI

一、背景 分布式AI的发展前景非常广阔&#xff0c;随着5G、6G等高速网络通信技术的普及和边缘计算能力的提升&#xff0c;以及AI算法和硬件的不断优化进步&#xff0c;分布式AI将在多个领域展现出强大的应用潜力和市场价值&#xff1a; 1. **物联网&#xff08;IoT&#xff0…

react【四】css

文章目录 1、css1.1 react和vue css的对比1.2 内联样式1.3 普通的css1.4 css modules1.5 在react中使用less1.6 CSS in JS1.6.1 模板字符串的基本使用1.6.2 styled-components的基本使用1.6.3 接受传参1.6.4 使用变量1.6.5 继承样式 避免代码冗余1.6.6 设置主题色 1.7 React中添…

正月初五迎财神

大家好&#xff0c;我是小悟 正月初五&#xff0c;人们在这一天迎接财神&#xff0c;祈求财运亨通、事业顺利。按照习俗&#xff0c;家家户户都会燃放鞭炮、点灯笼、摆设祭品&#xff0c;以示虔诚。 早晨&#xff0c;太阳刚刚升起&#xff0c;大家便早早起床&#xff0c;开始准…

【数据存储+多任务爬虫】

数据存储 peewee模块 第三方模块&#xff0c;也需要在cmd中安装。 from peewee import *db MySQLDatabase("spider",host"127.0.0.1",port3306,userroot,password123456 )# 类》表 class Person(Model):name CharField(max_length20) # 类型/约束bi…

核心篇-OSPF技术之序(中)

文章目录 一. 实验专题1.1. 实验1&#xff1a;配置多区域OSPF1.1.1. 实验目的1.1.2. 实验拓扑1.1.3. 实验步骤&#xff08;1&#xff09;配置地址&#xff08;2&#xff09;运行OSPF 1.1.4. 实验调试&#xff08;1&#xff09;查看路由器信息&#xff08;2&#xff09;创建环回…

Spring Boot 笔记 010 创建接口_更新用户头像

1.1.1 usercontroller中添加updateAvatar&#xff0c;校验是否为url PatchMapping("updateAvatar")public Result updateAvatar(RequestParam URL String avatarUrl) {userService.updateAvatar(avatarUrl);return Result.success();} 1.1.2 userservice //更新头像…

【UDS】搞懂时间参数

文章目录 背景时间参数的定义应用层相关会话层相关传输层相关网络层相关实际案例分析背景 TBD. 时间参数的定义 注意,这些时间参数都是超时阈值,需要理解为什么要有这些阈值,在哪一端判断这些阈值的,无需“死记硬背”它们的含义。 应用层相关 【P2 Client】 P2 Client 的…

滑模控制器是用来干什么的,生活中的应用案例有哪些,滑模控制器中的滑模是什么意思,来源是什么?

问题描述&#xff1a;滑模控制器是用来干什么的&#xff0c;生活中的应用案例有哪些&#xff0c;滑模控制器中的滑模是什么意思&#xff0c;来源是什么&#xff1f; 问题解答&#xff1a; 滑模控制器&#xff08;Sliding Mode Controller&#xff09;是一种用于控制系统的控制…

数学建模:K-means聚类手肘法确定k值(含python实现)

原理 当K-means聚类的k值不被指定时&#xff0c;可以通过手肘法来估计聚类数量。   在聚类的过程中&#xff0c;随着聚类数的增大&#xff0c;样本划分会变得更加精细&#xff0c;每个类别的聚合程度更高&#xff0c;那么误差平方和&#xff08;SSE&#xff09;会逐渐变小&am…

YOLOv8改进 | Conv篇 | 利用FasterBlock二次创新C2f提出一种全新的结构(全网独家首发,参数量下降70W)

一、本文介绍 本文给大家带来的改进机制是利用FasterNet的FasterBlock改进特征提取网络,将其用来改进ResNet网络,其旨在提高计算速度而不牺牲准确性,特别是在视觉任务中。它通过一种称为部分卷积(PConv)的新技术来减少冗余计算和内存访问。这种方法使得FasterNet在多种设…

LeetCode、72. 编辑距离【中等,二维DP】

文章目录 前言LeetCode、72. 编辑距离【中等&#xff0c;二维DP】题目链接与分类二维DP 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容…

nvm 安装nodejs教程【详细】

目录 一、安装nvm 二、配置镜像 三、安装nodejs 安装 查看正在用的nodejs版本 切换版本 一、安装nvm 双击安装包&#xff1a; 无脑下一步即可&#xff0c;当然你可以自定义你自己的安装目录。 安装完后&#xff0c;打开环境变量&#xff0c;你会发现nvm为我们自动配置好…

TinUI v5预发布记录

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