一、星星
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>star</title>
<script type="text/javascript">
window.onload = function () {
C = Math.cos; // cache Math objects
S = Math.sin;
U = 0;
w = window;
j = document;
d = j.getElementById("c");
c = d.getContext("2d");
W = d.width = w.innerWidth;
H = d.height = w.innerHeight;
c.fillRect(0, 0, W, H); // resize <canvas> and draw black rect (default)
c.globalCompositeOperation = "lighter"; // switch to additive color application
c.lineWidth = 0.2;
c.lineCap = "round";
var bool = 0,
t = 0; // theta
d.onmousemove = function (e) {
if(window.T) {
if(D==9) { D=Math.random()*15; f(1); }
clearTimeout(T);
}
X = e.pageX; // grab mouse pixel coords
Y = e.pageY;
a=0; // previous coord.x
b=0; // previous coord.y
A = X, // original coord.x
B = Y; // original coord.y
R=(e.pageX/W * 999>>0)/999;
r=(e.pageY/H * 999>>0)/999;
U=e.pageX/H * 360 >>0;
D=9;
g = 360 * Math.PI / 180;
T = setInterval(f = function (e) { // start looping spectrum
c.save();
c.globalCompositeOperation = "source-over"; // switch to additive color application
if(e!=1) {
c.fillStyle = "rgba(0,0,0,0.02)";
c.fillRect(0, 0, W, H); // resize <canvas> and draw black rect (default)
}
c.restore();
i = 25; while(i --) {
c.beginPath();
if(D > 450 || bool) { // decrease diameter
if(!bool) { // has hit maximum
bool = 1;
}
if(D < 0.1) { // has hit minimum
bool = 0;
}
t -= g; // decrease theta
D -= 0.1; // decrease size
}
if(!bool) {
t += g; // increase theta
D += 0.1; // increase size
}
q = (R / r - 1) * t; // create hypotrochoid from current mouse position, and setup variables (see: http://en.wikipedia.org/wiki/Hypotrochoid)
x = (R - r) * C(t) + D * C(q) + (A + (X - A) * (i / 25)) + (r - R); // center on xy coords
y = (R - r) * S(t) - D * S(q) + (B + (Y - B) * (i / 25));
if (a) { // draw once two points are set
c.moveTo(a, b);
c.lineTo(x, y)
}
c.strokeStyle = "hsla(" + (U % 360) + ",100%,50%,0.75)"; // draw rainbow hypotrochoid
c.stroke();
a = x; // set previous coord.x
b = y; // set previous coord.y
}
U -= 0.5; // increment hue
A = X; // set original coord.x
B = Y; // set original coord.y
}, 16);
}
j.onkeydown = function(e) { a=b=0; R += 0.05 }
d.onmousemove({pageX:300, pageY:290})
}
</script>
</head>
<body style="margin:0px;padding:0px;width:100%;height:100%;overflow:hidden;">
<canvas id="c"></canvas>
</body>
</html>
运行结果
代码讲解:
这段代码是一个基于 HTML5 Canvas 的交互式动态图形页面,核心是通过数学公式生成随鼠标移动而变化的彩色内摆线(hypotrochoid)动画,具体细节如下:
-
基础设置:页面加载时初始化 Canvas 画布,使其尺寸与窗口大小一致,初始填充黑色背景,并设置绘图模式为 “lighter”(叠加颜色,让线条叠加时更鲜艳),线条宽度为 0.2px 且端点为圆形。
-
鼠标交互:监听鼠标移动事件,获取鼠标坐标(X、Y),并以此计算内摆线的关键参数:
- R 和 r:基于鼠标在窗口中的位置计算(X/W 和 Y/H 比例映射),用于控制内摆线的整体大小和形态
- U:基于鼠标 X 坐标计算的色相值,控制线条颜色
- D:内摆线的偏移参数,影响曲线的 “缩进” 程度
-
动画逻辑:通过
setInterval
每 16ms(约 60 帧 / 秒)执行一次绘图函数,实现流畅动画:- 每次绘制前用半透明黑色矩形覆盖画布,产生 “轨迹逐渐消失” 的效果
- 循环绘制 25 条相似曲线(通过 i 从 25 递减控制),形成层次感
- 内摆线通过数学公式计算:
x = (R - r) * cos(t) + D * cos(q)
、y = (R - r) * sin(t) - D * sin(q)
(其中 q 是基于 R、r、t 的中间变量) - D 值会在 0.1 到 450 之间自动增减(通过 bool 变量控制方向),使曲线大小周期性变化;t 值随 D 的变化同步增减,驱动曲线旋转
-
颜色变化:使用 HSLA 色彩模式,通过 U 值的不断递减(每次 - 0.5)实现色相循环,呈现彩虹般的颜色渐变效果,透明度固定为 0.75。
-
额外交互:按下键盘任意键时,会增加 R 值(每次 + 0.05),改变内摆线的整体形态比例。
-
初始触发:页面加载后会模拟一次鼠标在 (300,290) 位置的移动,确保动画自动启动。
整体效果是随鼠标移动产生动态变化的彩色曲线簇,线条会逐渐扩散又收缩,颜色不断循环,形成富有流动感的视觉效果。
二、爱心
<!DOCTYPE html>
<html>
{#jishugang#}
<head>
<meta charset="utf-8" />
<title>爱心</title>//还有这里
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background: #000;
overflow: hidden;
}
canvas {
display:block;
position: absolute;
width: 100%;
height: 100%;
}
#pinkboard{
animation: anim 1.5s ease-in-out infinite;
-webkit-animation: anim 1.5s ease-in-out infinite;
-o-animation: anim 1.5s ease-in-out infinite;
-moz-animation: anim 1.5s ease-in-out infinite;
}
#name {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin-top: -20px;
font-size: 46px;
color: #ea80b0;
}
@keyframes anim {
0% {
transform: scale(0.8);
}
25% {
transform: scale(0.7);
}
50% {
transform: scale(1);
}
75% {
transform: scale(0.7);
}
100% {
transform: scale(0.8);
}
}
@-webkit-keyframes anim {
0% {
-webkit-transform: scale(0.8);
}
25% {
-webkit-transform: scale(0.7);
}
50% {
-webkit-transform: scale(1);
}
75% {
-webkit-transform: scale(0.7);
}
100% {
-webkit-transform: scale(0.8);
}
}
@-o-keyframes anim {
0% {
-o-transform: scale(0.8);
}
25% {
-o-transform: scale(0.7);
}
50% {
-o-transform: scale(1);
}
75% {
-o-transform: scale(0.7);
}
100% {
-o-transform: scale(0.8);
}
}
@-moz-keyframes anim {
0% {
-moz-transform: scale(0.8);
}
25% {
-moz-transform: scale(0.7);
}
50% {
-moz-transform: scale(1);
}
75% {
-moz-transform: scale(0.7);
}
100% {
-moz-transform: scale(0.8);
}
}
</style>
</head>
<body>
<canvas id='canv'></canvas>
<canvas id="pinkboard"></canvas>
<div id="name" style="color: pink;">亲爱的小姐,能做我的宝贝吗</div>//在这里改
<script>
var c = document.getElementById('canv');//获取canvas的Id
var $ = c.getContext('2d');/*设置绘制方式*/
var u = 0;
var go = function() {
var sc, g, g1, i, j, p, x, y, z, w, a, cur,
d = new Date() / 1000,
rnd = shift(),
rnd1 = d,
rnd2 = 2.4,
rnd3 = d * 0.2,
rnd1c = Math.cos(rnd1),
rnd1s = Math.sin(rnd1),
rnd2c = Math.cos(rnd2),
rnd2s = Math.sin(rnd2);
c.width = window.innerWidth;
c.height = window.innerHeight;
sc = Math.max(c.width, c.height);
$.translate(c.width * 0.5, c.height * 0.5);//从中心开始绘制
$.scale(sc, sc);//放大最大数值
/*线性渐变*/
g = $.createLinearGradient(-1, -2, 1, 2);
g.addColorStop(0.0, 'hsla(338, 95%, 25%, 1)');
g.addColorStop(0.5, 'hsla(260, 95%, 5%, 1)');
g.addColorStop(1.0, 'hsla(338, 95%, 30%, 1)');
$.fillStyle = g;//颜色
$.fillRect(-0.5, -0.5, 1, 1);
$.globalCompositeOperation = 'lighter';
$.rotate(rnd3 % Math.PI * 2);/*旋转*/
for (i = 0; i < 300; i += 1) {
p = rnd();
x = (p & 0xff) / 128 - 1;
y = (p >>> 8 & 0xff) / 128 - 1;
z = (p >>> 16 & 0xff) / 128 - 1;
w = (p >>> 24 & 0xff) / 256;
z += d * 0.5;
z = (z + 1) % 2 - 1;
a = (z + 1) * 0.5;
if (a < 0.9) {
$.globalAlpha = a / 0.7;
}else {
a -= 0.9;
$.globalAlpha = 1 - a / 0.1;
}
cur = x * rnd1c + y * rnd1s;
y = x * rnd1s - y * rnd1c;
x = cur;
cur = y * rnd2c + z * rnd2s;
z = y * rnd2s - z * rnd2c;
y = cur;
z -= 0.65;
if (z >= 0) {
continue;
}
sc = 0.1 / z;
x *= sc;
y *= sc;
$.save();
g1 = $.createRadialGradient(1, 1, 2, 1, 1, 1);
g1.addColorStop(0.0, 'hsla('+i+', 70%, 40%,.8)');
g1.addColorStop(0.5, 'hsla('+i+', 75%, 50%, 1)');
g1.addColorStop(1.0, 'hsla('+i+', 80%, 60%, .8)');
$.fillStyle = g1;
$.translate(x, y);
$.scale(sc * 0.017, sc * 0.017);
$.beginPath();
$.moveTo(2, 0);
for (j = 0; j < 10; j += 1) {
$.rotate(Math.PI*2 * 0.1);
$.lineTo(j % 2 + 1, 0);
}
$.arc(10, 10, 1, 0, Math.PI * 2);
$.rotate(Math.PI * 2 * 0.1);
$.closePath();
$.fill();
$.restore();
}
};
/*
Marsaglia's Xorshift128 PRG: http://en.wikipedia.org/wiki/Xorshift
*/
var shift = function(x, y, z, w) {
x = x || 1234567;
y = y || 362436069;
z = z || 521288629;
w = w || 88675123;
return function() {
var s = x ^ (x << 11);
x = y;
y = z;
z = w;
w = (w ^ (w >>> 19)) ^ (s ^ (s >>> 8));
return w;
};
}
window.addEventListener('resize', function() {
c.width = window.innerWidth;
c.height = window.innerHeight;
}, false);
window.requestAnimationFrame = window.requestAnimationFrame||
window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var run = function() {
window.requestAnimationFrame(run);
go();
}
run();
</script>
<script>
var settings = {
particles: {
length: 500,
duration: 2,
velocity: 100,
effect: -0.75,
size: 30,
},
};
(function () {
var b = 0;
var c = ["ms", "moz", "webkit", "o"];
for (var a = 0; a < c.length && !window.requestAnimationFrame; ++a) {
window.requestAnimationFrame = window[c[a] + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[c[a] + "CancelAnimationFrame"] ||
window[c[a] + "CancelRequestAnimationFrame"];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function (h, e) {
var d = new Date().getTime();
var f = Math.max(0, 16 - (d - b));
var g = window.setTimeout(function () {
h(d + f);
}, f);
b = d + f;
return g;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (d) {
clearTimeout(d);
};
}
})();
var Point = (function () {
function Point(x, y) {
this.x = typeof x !== "undefined" ? x : 0;
this.y = typeof y !== "undefined" ? y : 0;
}
Point.prototype.clone = function () {
return new Point(this.x, this.y);
};
Point.prototype.length = function (length) {
if (typeof length == "undefined")
return Math.sqrt(this.x * this.x + this.y * this.y);
this.normalize();
this.x *= length;
this.y *= length;
return this;
};
Point.prototype.normalize = function () {
var length = this.length();
this.x /= length;
this.y /= length;
return this;
};
return Point;
})();
var Particle = (function () {
function Particle() {
this.position = new Point();
this.velocity = new Point();
this.acceleration = new Point();
this.age = 0;
}
Particle.prototype.initialize = function (x, y, dx, dy) {
this.position.x = x;
this.position.y = y;
this.velocity.x = dx;
this.velocity.y = dy;
this.acceleration.x = dx * settings.particles.effect;
this.acceleration.y = dy * settings.particles.effect;
this.age = 0;
};
Particle.prototype.update = function (deltaTime) {
this.position.x += this.velocity.x * deltaTime;
this.position.y += this.velocity.y * deltaTime;
this.velocity.x += this.acceleration.x * deltaTime;
this.velocity.y += this.acceleration.y * deltaTime;
this.age += deltaTime;
};
Particle.prototype.draw = function (context, image) {
function ease(t) {
return --t * t * t + 1;
}
var size = image.width * ease(this.age / settings.particles.duration);
context.globalAlpha = 1 - this.age / settings.particles.duration;
context.drawImage(
image,
this.position.x - size / 2,
this.position.y - size / 2,
size,
size
);
};
return Particle;
})();
var ParticlePool = (function () {
var particles,
firstActive = 0,
firstFree = 0,
duration = settings.particles.duration;
function ParticlePool(length) {
particles = new Array(length);
for (var i = 0; i < particles.length; i++)
particles[i] = new Particle();
}
ParticlePool.prototype.add = function (x, y, dx, dy) {
particles[firstFree].initialize(x, y, dx, dy);
firstFree++;
if (firstFree == particles.length) firstFree = 0;
if (firstActive == firstFree) firstActive++;
if (firstActive == particles.length) firstActive = 0;
};
ParticlePool.prototype.update = function (deltaTime) {
var i;
if (firstActive < firstFree) {
for (i = firstActive; i < firstFree; i++)
particles[i].update(deltaTime);
}
if (firstFree < firstActive) {
for (i = firstActive; i < particles.length; i++)
particles[i].update(deltaTime);
for (i = 0; i < firstFree; i++) particles[i].update(deltaTime);
}
while (
particles[firstActive].age >= duration &&
firstActive != firstFree
) {
firstActive++;
if (firstActive == particles.length) firstActive = 0;
}
};
ParticlePool.prototype.draw = function (context, image) {
if (firstActive < firstFree) {
for (i = firstActive; i < firstFree; i++)
particles[i].draw(context, image);
}
if (firstFree < firstActive) {
for (i = firstActive; i < particles.length; i++)
particles[i].draw(context, image);
for (i = 0; i < firstFree; i++) particles[i].draw(context, image);
}
};
return ParticlePool;
})();
(function (canvas) {
var context = canvas.getContext("2d"),
particles = new ParticlePool(settings.particles.length),
particleRate =
settings.particles.length / settings.particles.duration,
time;
function pointOnHeart(t) {
return new Point(
160 * Math.pow(Math.sin(t), 3),
130 * Math.cos(t) -
50 * Math.cos(2 * t) -
20 * Math.cos(3 * t) -
10 * Math.cos(4 * t) +
25
);
}
var image = (function () {
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d");
canvas.width = settings.particles.size;
canvas.height = settings.particles.size;
function to(t) {
var point = pointOnHeart(t);
point.x =
settings.particles.size / 2 +
(point.x * settings.particles.size) / 350;
point.y =
settings.particles.size / 2 -
(point.y * settings.particles.size) / 350;
return point;
}
context.beginPath();
var t = -Math.PI;
var point = to(t);
context.moveTo(point.x, point.y);
while (t < Math.PI) {
t += 0.01;
point = to(t);
context.lineTo(point.x, point.y);
}
context.closePath();
context.fillStyle = "#ea80b0";
context.fill();
var image = new Image();
image.src = canvas.toDataURL();
return image;
})();
function render() {
requestAnimationFrame(render);
var newTime = new Date().getTime() / 1000,
deltaTime = newTime - (time || newTime);
time = newTime;
context.clearRect(0, 0, canvas.width, canvas.height);
var amount = particleRate * deltaTime;
for (var i = 0; i < amount; i++) {
var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());
var dir = pos.clone().length(settings.particles.velocity);
particles.add(
canvas.width / 2 + pos.x,
canvas.height / 2 - pos.y,
dir.x,
-dir.y
);
}
particles.update(deltaTime);
particles.draw(context, image);
}
function onResize() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
window.onresize = onResize;
setTimeout(function () {
onResize();
render();
}, 10);
})(document.getElementById("pinkboard"));
</script>
</body>
</html>
运行结果:
代码介绍:
这段代码是一个带有动画效果的爱心网页,主要由 HTML、CSS 和 JavaScript 组成,实现了动态爱心图形和文字展示效果。以下是代码的详细介绍:
整体结构
- 采用 HTML5 标准文档结构,包含
<head>
和<body>
标签 - 通过两个
<canvas>
元素实现动画效果 - 包含一个
<div>
元素用于展示文字 - 使用 CSS 进行样式设置,JavaScript 实现动画逻辑
主要功能模块
-
HTML 结构
- 定义了两个 canvas 画布(
canv
和pinkboard
),分别用于不同的动画效果 - 定义了一个
name
容器用于显示文字信息
- 定义了两个 canvas 画布(
-
CSS 样式
- 设置页面背景为黑色,去除默认边距
- 使 canvas 占满整个屏幕,实现全屏动画效果
- 为
pinkboard
画布添加缩放动画(anim
),使其呈现呼吸效果 - 定位文字在屏幕中心,设置粉色字体
- 兼容多种浏览器的动画前缀(
-webkit-
、-o-
、-moz-
)
-
JavaScript 动画逻辑
-
第一个脚本为
canv
画布实现粒子效果:- 使用 Xorshift128 随机数生成算法创建随机粒子
- 通过 requestAnimationFrame 实现平滑动画
- 粒子随时间旋转、移动,形成动态背景效果
- 响应窗口大小变化,保持全屏显示
-
第二个脚本为
pinkboard
画布实现爱心粒子动画:- 定义粒子类(
Particle
)和粒子池(ParticlePool
)管理粒子 - 使用心形曲线公式(
pointOnHeart
)生成爱心形状 - 粒子从心形路径发射,形成流动的爱心效果
- 实现粒子的生命周期管理(初始化、更新、绘制)
- 定义粒子类(
-
视觉效果
- 黑色背景上有彩色粒子组成的动态背景
- 中心有一个脉动的粉色爱心(由粒子组成)
- 爱心中央显示 "亲爱的小姐,能做我的宝贝吗" 文字
- 整体效果具有深度感和动态美感,适合作为表白页面
技术亮点
- 结合 Canvas 绘图 API 实现复杂动画效果
- 使用数学公式生成心形曲线
- 粒子系统的优化管理(对象池模式)
- 适配不同浏览器的动画实现
- 响应式设计,适配不同屏幕尺寸
可以通过修改文字内容(name
元素)、颜色值或动画参数来调整页面效果。