HTML流光爱心

文章目录

序号目录
1HTML满屏跳动的爱心(可写字)
2HTML五彩缤纷的爱心
3HTML满屏漂浮爱心
4HTML情人节快乐
5HTML蓝色爱心射线
6HTML跳动的爱心(简易版)
7HTML粒子爱心
8HTML蓝色动态爱心
9HTML跳动的爱心(双心版)
10HTML橙色动态粒子爱心
11HTML旋转爱心
12HTML爱情树
13HTML3D相册
14HTML旋转相册
15HTML基础烟花秀
16HTML炫酷烟花秀
17HTML粉色烟花秀
18HTML新春烟花
19HTML龙年大吉
20HTML圣诞树
21HTML大雪纷飞
22HTML想见你
23HTML元素周期表
24HTML飞舞的花瓣
25HTML星空特效
26HTML黑客帝国字母雨
27HTML哆啦A梦
28HTML流星雨
29HTML沙漏爱心
30HTML爱心字母雨
31HTML爱心流星雨
32HTML生日蛋糕
33HTML流光爱心

写在前面

本期博主给大家推荐一个由HTML代码实现的、红蓝色线条组成的流光爱心,一起来看看吧。

完整代码

<!DOCTYPE html>
<!--<div class="menu__subheader"><br>微信公众号:雁卿雁卿呀-->
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>流光爱心</title>
  <meta name="author" content="Logical Digit">
  </head>
      <!-- css部分 -->
 <style>
  body {
    background-color: #000;
    margin: 0;
    overflow: hidden;
    background-repeat: no-repeat;
}
</style>
  <body>
    <!-- 绘画爱心 -->
    <canvas id="canvas" width="1400" height="600"></canvas>
    <!-- js部分 -->
  </body>
  <script>
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Initialize the GL context
var gl = canvas.getContext('webgl');
if (!gl) {
    console.error("Unable to initialize WebGL.");
}
//Time step
var dt = 0.015;
//Time
var time = 0.0;
//************** Shader sources **************
var vertexSource = `
attribute vec2 position;
void main() {
  gl_Position = vec4(position, 0.0, 1.0);
}
`;
 
var fragmentSource = `
precision highp float;
uniform float width;
uniform float height;
vec2 resolution = vec2(width, height);
uniform float time;
#define POINT_COUNT 8
vec2 points[POINT_COUNT];
const float speed = -0.5;
const float len = 0.25;
float intensity = 0.9;
float radius = 0.015;
//https://www.shadertoy.com/view/MlKcDD
//Signed distance to a quadratic bezier
float sdBezier(vec2 pos, vec2 A, vec2 B, vec2 C){    
  vec2 a = B - A;
  vec2 b = A - 2.0*B + C;
  vec2 c = a * 2.0;
  vec2 d = A - pos;
  float kk = 1.0 / dot(b,b);
  float kx = kk * dot(a,b);
  float ky = kk * (2.0*dot(a,a)+dot(d,b)) / 3.0;
  float kz = kk * dot(d,a);      
  float res = 0.0;
  float p = ky - kx*kx;
  float p3 = p*p*p;
  float q = kx*(2.0*kx*kx - 3.0*ky) + kz;
  float h = q*q + 4.0*p3;
  if(h >= 0.0){ 
    h = sqrt(h);
    vec2 x = (vec2(h, -h) - q) / 2.0;
    vec2 uv = sign(x)*pow(abs(x), vec2(1.0/3.0));
    float t = uv.x + uv.y - kx;
    t = clamp( t, 0.0, 1.0 );
    // 1 root
    vec2 qos = d + (c + b*t)*t;
    res = length(qos);
  }else{
    float z = sqrt(-p);
    float v = acos( q/(p*z*2.0) ) / 3.0;
    float m = cos(v);
    float n = sin(v)*1.732050808;
    vec3 t = vec3(m + m, -n - m, n - m) * z - kx;
    t = clamp( t, 0.0, 1.0 );
    // 3 roots
    vec2 qos = d + (c + b*t.x)*t.x;
    float dis = dot(qos,qos);
        
    res = dis;
    qos = d + (c + b*t.y)*t.y;
    dis = dot(qos,qos);
    res = min(res,dis);
    
    qos = d + (c + b*t.z)*t.z;
    dis = dot(qos,qos);
    res = min(res,dis);
    res = sqrt( res );
  }
    
  return res;
}
//http://mathworld.wolfram.com/HeartCurve.html
vec2 getHeartPosition(float t){
  return vec2(16.0 * sin(t) * sin(t) * sin(t),
              -(13.0 * cos(t) - 5.0 * cos(2.0*t)
              - 2.0 * cos(3.0*t) - cos(4.0*t)));
}
//https://www.shadertoy.com/view/3s3GDn
float getGlow(float dist, float radius, float intensity){
  return pow(radius/dist, intensity);
}
float getSegment(float t, vec2 pos, float offset, float scale){
  for(int i = 0; i < POINT_COUNT; i++){
    points[i] = getHeartPosition(offset + float(i)*len + fract(speed * t) * 6.28);
  }
    
  vec2 c = (points[0] + points[1]) / 2.0;
  vec2 c_prev;
  float dist = 10000.0;
    
  for(int i = 0; i < POINT_COUNT-1; i++){
    //https://tinyurl.com/y2htbwkm
    c_prev = c;
    c = (points[i] + points[i+1]) / 2.0;
    dist = min(dist, sdBezier(pos, scale * c_prev, scale * points[i], scale * c));
  }
  return max(0.0, dist);
}
void main(){
  vec2 uv = gl_FragCoord.xy/resolution.xy;
  float widthHeightRatio = resolution.x/resolution.y;
  vec2 centre = vec2(0.5, 0.5);
  vec2 pos = centre - uv;
  pos.y /= widthHeightRatio;
  //Shift upwards to centre heart
  pos.y += 0.02;
  float scale = 0.000015 * height;
  
  float t = time;
    
  //Get first segment
  float dist = getSegment(t, pos, 0.0, scale);
  float glow = getGlow(dist, radius, intensity);
    
  vec3 col = vec3(0.0);
    
  //White core
  col += 10.0*vec3(smoothstep(0.003, 0.001, dist));
  //Pink glow
  col += glow * vec3(0.94,0.14,0.4);
    
  //Get second segment
  dist = getSegment(t, pos, 3.4, scale);
  glow = getGlow(dist, radius, intensity);
    
  //White core
  col += 10.0*vec3(smoothstep(0.003, 0.001, dist));
  //Blue glow
  col += glow * vec3(0.2,0.6,1.0);
        
  //Tone mapping
  col = 1.0 - exp(-col);
  //Output to screen
   gl_FragColor = vec4(col,1.0);
}
`;
 
//************** Utility functions **************
 
window.addEventListener('resize', onWindowResize, false);
 
function onWindowResize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.uniform1f(widthHandle, window.innerWidth);
    gl.uniform1f(heightHandle, window.innerHeight);
}
 
 
//Compile shader and combine with source
function compileShader(shaderSource, shaderType) {
    var shader = gl.createShader(shaderType);
    gl.shaderSource(shader, shaderSource);
    gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        throw "Shader compile failed with: " + gl.getShaderInfoLog(shader);
    }
    return shader;
}
 
//From https://codepen.io/jlfwong/pen/GqmroZ
//Utility to complain loudly if we fail to find the attribute/uniform
function getAttribLocation(program, name) {
    var attributeLocation = gl.getAttribLocation(program, name);
    if (attributeLocation === -1) {
        throw 'Cannot find attribute ' + name + '.';
    }
    return attributeLocation;
}
 
function getUniformLocation(program, name) {
    var attributeLocation = gl.getUniformLocation(program, name);
    if (attributeLocation === -1) {
        throw 'Cannot find uniform ' + name + '.';
    }
    return attributeLocation;
}
 
//************** Create shaders **************
 
//Create vertex and fragment shaders
var vertexShader = compileShader(vertexSource, gl.VERTEX_SHADER);
var fragmentShader = compileShader(fragmentSource, gl.FRAGMENT_SHADER);
 
//Create shader programs
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
 
gl.useProgram(program);
 
//Set up rectangle covering entire canvas 
var vertexData = new Float32Array([-1.0, 1.0, // top left
    -1.0, -1.0, // bottom left
    1.0, 1.0, // top right
    1.0, -1.0, // bottom right
]);
 
//Create vertex buffer
var vertexDataBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);
 
// Layout of our data in the vertex buffer
var positionHandle = getAttribLocation(program, 'position');
 
gl.enableVertexAttribArray(positionHandle);
gl.vertexAttribPointer(positionHandle,
    2, // position is a vec2 (2 values per component)
    gl.FLOAT, // each component is a float
    false, // don't normalize values
    2 * 4, // two 4 byte float components per vertex (32 bit float is 4 bytes)
    0 // how many bytes inside the buffer to start from
);
 
//Set uniform handle
var timeHandle = getUniformLocation(program, 'time');
var widthHandle = getUniformLocation(program, 'width');
var heightHandle = getUniformLocation(program, 'height');
 
gl.uniform1f(widthHandle, window.innerWidth);
gl.uniform1f(heightHandle, window.innerHeight);
 
function draw() {
    //Update time
    time += dt;
 
    //Send uniforms to program
    gl.uniform1f(timeHandle, time);
    //Draw a triangle strip connecting vertices 0-4
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
 
    requestAnimationFrame(draw);
}
 
draw();
</script>
</html>

代码分析

这段代码通过HTML、CSS和WebGL结合实现了一个“流光爱心”效果,以下是对其的详细分析。

1. HTML结构

在HTML部分,主要是一个canvas元素,它是用于绘制心形动画的区域。canvas元素通过其ID属性canvas在JavaScript中被引用,用于初始化WebGL上下文。

<canvas id="canvas" width="1400" height="600"></canvas>

在这里,宽度和高度只是初始设置,代码通过JavaScript调整canvas的大小以适应浏览器窗口。

2. CSS部分

CSS的作用是设置整个网页的背景颜色为黑色,并使画布占据整个窗口,同时禁用滚动条以确保效果全屏展示。

body {

    background-color: #000;
    margin: 0;
    overflow: hidden;
    background-repeat: no-repeat;
}

3. WebGL上下文初始化

JavaScript部分通过getContext('webgl')获取WebGL绘图上下文,这是一种用于在网页中绘制2D和3D图形的API。

var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var gl = canvas.getContext('webgl');
if (!gl) {
    console.error("Unable to initialize WebGL.");
}

这段代码确保WebGL能够正常运行,如果无法初始化WebGL,会在控制台输出错误信息。

4. 着色器程序

代码中定义了顶点着色器和片段着色器,用于处理图形的顶点和颜色计算。顶点着色器vertexSource负责定义绘制区域,而片段着色器fragmentSource则负责生成心形动画的光效。

  • 顶点着色器:这个简单的顶点着色器只定义了矩形区域,覆盖整个canvas

attribute vec2 position;
void main() {
  gl_Position = vec4(position, 0.0, 1.0);
}
  • 片段着色器:这是核心部分,计算并绘制了心形的流光效果。着色器使用了多种图形技术,如二次贝塞尔曲线距离函数来生成心形轮廓,getGlow函数实现光晕效果,getHeartPosition函数计算心形轨迹。

vec2 getHeartPosition(float t){
  return vec2(16.0 * sin(t) * sin(t) * sin(t),
              -(13.0 * cos(t) - 5.0 * cos(2.0*t)
              - 2.0 * cos(3.0*t) - cos(4.0*t)));
}

这个公式是数学中的经典“心形曲线公式”,通过调整时间参数t,可以得到心形轨迹上的点。

5. 动态渲染

draw函数中,程序使用requestAnimationFrame(draw)不断调用自己来实现帧动画,逐帧更新time变量,使得心形的流光效果随着时间变化而持续运行。

function draw() {
    time += dt;
    gl.uniform1f(timeHandle, time);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    requestAnimationFrame(draw);
}

每一帧,WebGL都会根据当前的时间time重新计算心形上每个点的位置,进而生成流光效果。

6. 响应窗口变化

代码监听了窗口大小的变化,使用onWindowResize函数动态调整画布的大小,确保动画始终适应窗口尺寸,并且心形保持居中显示。

window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.uniform1f(widthHandle, window.innerWidth);
    gl.uniform1f(heightHandle, window.innerHeight);
}

总结

这段代码通过WebGL技术生成了一个心形流光效果。核心部分在于数学公式计算出的心形轨迹和光晕效果,通过着色器来实现。WebGL的强大之处在于它能直接操控GPU,生成高效的动画效果。

写在后面

我是一只有趣的兔子,感谢你的喜欢!

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

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

相关文章

类和对象(3)

博客ID&#xff1a;LanFuRenC系列专栏&#xff1a;C语言重点部分 C语言注意点 C基础 Linux 数据结构 C注意点 声明等级&#xff1a;黑色->蓝色->红色 欢迎新粉加入&#xff0c;会一直努力提供更优质的编程博客&#xff0c;希望大家三连支持一下啦 目录 1.拷贝构造 …

ACM第三次考核题解

ACM第三次考核题解 题目序号难度题目编号题目考察知识点1签到题A这是一道很难的题&#xff01;&#xff01;&#xff01;输出2迷之难度F神说要有光&#xff0c;于是有了手电筒贪心3简单BThis is a real English problem&#xff01;思维 英语4简单C玩具简单排序5简单I“近义词…

速通数据结构与算法第六站 树堆

系列文章目录 速通数据结构与算法系列 1 速通数据结构与算法第一站 复杂度 http://t.csdnimg.cn/sxEGF 2 速通数据结构与算法第二站 顺序表 http://t.csdnimg.cn/WVyDb 3 速通数据结构与算法第三站 单链表 http://t.csdnimg.cn/cDpcC 4 速通…

一文上手SpringSecurity【九】

在校验token的过滤器当中, 由于需要根据用户名称, 去查询出要认证的对象,然后再去数据库当中查询出角色列表、权限列表.频繁的操作数据库,可能会带来性能瓶颈, 那么我们该如何解决呢? 我们可以引入Redis, 将认证的对象,存储到Redis当中,在校验token的过滤器当中,可以直接从Red…

9.29 LeetCode 3304、3300、3301

思路&#xff1a; ⭐进行无限次操作&#xff0c;但是 k 的取值小于 500 &#xff0c;所以当 word 的长度大于 500 时就可以停止操作进行取值了 如果字符为 ‘z’ &#xff0c;单独处理使其变为 ‘a’ 得到得到操作后的新字符串&#xff0c;和原字符串拼接 class Solution { …

ServletContainerInitializer接口详解

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhlServletContainerInitializer概述 ServletContainerInitializer是Servlet 3.0规范中引入的一个接口,它的主要目的是允许开发者在Servlet容器(如Tomcat、Jetty等)启动时执行一些自定义的初始化代…

synchronized相关知识

1、对象头Markword 2、锁升级过程 无锁 偏向锁&#xff1a;只有一个线程过来加锁&#xff0c;Markword对应变化&#xff1a;偏向线程ID存储当前线程ID&#xff0c;偏向锁标志位置成1&#xff0c;锁标志位置为01&#xff1b;此后如果当前线程再次获取锁&#xff0c;只需对比偏…

《十年国庆游,洞察中国旅游新趋势》

作者&#xff1a;侯炯 一、十年国庆旅游数据总览 过去十年&#xff0c;中国国庆旅游市场呈现出丰富的变化和强劲的发展态势。从接待游客人次来看&#xff0c;2014 年接待国内游客 4.75 亿人次&#xff0c;到 2019 年已增长至 7.82 亿人次&#xff0c;2023 年国内旅游出游人数更…

【预备理论知识——1】深度学习:概率论概述

简单地说&#xff0c;机器学习就是做出预测。 概率论 掷骰子 假设我们掷骰子&#xff0c;想知道看到1的几率有多大&#xff0c;而不是看到另一个数字。 如果骰子是公平的&#xff0c;那么所有六个结果{1,…, 6}都有相同的可能发生&#xff0c; 因此我们可以说 1 发生的概率为1…

【数据结构】图的最小生成树

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C游记》《进击的C》《Linux迷航》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、最小生成树的概念二、Kruskal算法2.1 思想2.2 实现 三、Prim算法3.1 思想3.2 实现 四、Kruskal和Prim的对比…

container_of 函数的分析

这个函数的目的是&#xff0c; 通过结构体里面的内容 找到 大结构体的 基地址。 函数的原型是&#xff1a;  &#xff30;&#xff34;&#xff32;是指针 &#xff54;&#xff59;&#xff50;&#xff45; &#xff0c; &#xff4d;&#xff45;&#xff4d;&#xff…

新手上路:Anaconda虚拟环境创建和配置以使用PyTorch和DGL

文章目录 前言步骤 1: 安装 Anaconda步骤 2: 创建新的 Anaconda 环境步骤 3: 安装最新版本的 PyTorch步骤 4: 安装特定版本的 PyTorch步骤 5: 安装最新版本的 DGL步骤 6: 安装特定版本的 DGL步骤 7: Pycharm中使用虚拟环境解释器第一种情况&#xff1a;创建新项目第二种情况&am…

Word办公自动化的一些方法

1.Word部分内容介绍 word本身是带有格式的一种文档&#xff0c;有人说它本质是XML&#xff0c;所以一定要充分利用标记了【样式】的特性来迅速调整【格式】&#xff0c;从而专心编辑文档内容本身。 样式&#xff08;集&#xff09; 编号&#xff08;多级关联样式编号&#xff…

Tomcat系列漏洞复现

CVE-2017-12615——Tomcat put⽅法任意⽂件写⼊漏洞 漏洞描述 当 Tomcat运⾏在Windows操作系统时&#xff0c;且启⽤了HTTP PUT请求⽅法&#xff08;例如&#xff0c;将 readonly初始化参数由默认值设置为false&#xff09;&#xff0c;攻击者将有可能可通过精⼼构造的攻击请求…

探索 Snowflake 与 Databend 的云原生数仓技术与应用实践 | Data Infra NO.21 回顾

上周六&#xff0c;第二十一期「Data Infra 研究社」在线上与大家相见。活动邀请到了西门子数据分析师陈砚林与 Databend 联合创始人王吟&#xff0c;为我们带来了一场关于 Snowflake 和 Databend 的技术探索。Snowflake&#xff0c;这个市值曾超过 700 亿美元的云原生数据仓库…

Android 安卓内存安全漏洞数量大幅下降的原因

谷歌决定使用内存安全的编程语言 Rust 向 Android 代码库中写入新代码&#xff0c;尽管旧代码&#xff08;用 C/C 编写&#xff09;没有被重写&#xff0c;但内存安全漏洞却大幅减少。 Android 代码库中每年发现的内存安全漏洞数量&#xff08;来源&#xff1a;谷歌&#xff09…

资质申请中常见的错误有哪些?

在申请建筑资质的过程中&#xff0c;企业可能会犯一些常见的错误&#xff0c;以下是一些需要避免的错误&#xff1a; 1. 资料准备不充分&#xff1a; 申请资质需要提交大量的资料&#xff0c;包括企业法人资料、财务报表、业绩证明等。资料不齐全或不准确都可能导致申请失败。…

汽车线束之故障诊断方案-TDR测试

当前&#xff0c;在汽车布局中的线束的性能要求越来越高。无法通过简单的通断测试就能满足性能传输要求。早起对智能化要求不高&#xff0c;比如没有激动雷达、高清摄像、中央CPU等。 近几年的智能驾驶对网络传输要求越来越高&#xff0c;不但是高速率&#xff0c;还需要高稳定…

【C++题目】7.双指针_和为 s 的两个数字

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; LCR 179.查找总价格为目标值的两个商品 题目描述&#xff1a; 解法 解法一&#xff08;暴力解法&#xff0c;会超时&#xff09; 两层 for 循环列出所有两个数字的组合…

一种使用 SUMO + Python 联合仿真平台

一种使用 SUMO Python 联合仿真平台&#xff08;一&#xff09; 本文适用人群包括但不仅限于【交通运输】【车辆工程】【自动化控制】【计算机科学与技术】等专业本科生、研究生、博士生。本文通过在Pycharm平台&#xff0c;使用Python语言 Traci工具包&#xff0c;调用SUMO客…