# Bumping Heart ❤️
```html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> Heart </TITLE>
<META NAME="Generator" CONTENT="EditPlus">
<META NAME="Author" CONTENT="">
<META NAME="Keywords" CONTENT="">
<META NAME="Description" CONTENT="">
<style>
@keyframes bumping {
0% {
width: 50%;
height: 50%;
}
40% {
width: 50%;
height: 50%;
}
50% {
width: 55%;
height: 55%;
}
57% {
width: 45%;
height: 45%;
}
65% {
width: 50%;
height: 50%;
}
100% {
width: 50%;
height: 50%;
}
}
html, body {
height: 100%;
padding: 0;
margin: 0;
background: black;
}
body {
display: flex;
align-items: center;
align-content: center;
justify-content: center;
}
canvas#pinkboard {
position: absolute;
width: 50%;
height: 50%;
animation-name: bumping;
animation-duration: 1.5s;
animation-delay: 5s;
animation-iteration-count: infinite;
z-index: 1000;
transform: scale(0.85);
}
canvas#innerLayer {
position: absolute;
width: 50%;
height: 50%;
animation-name: bumping;
animation-duration: 1.5s;
animation-delay: 5s;
animation-iteration-count: infinite;
transform: scale(1.1);
}
</style>
</HEAD>
<BODY>
<canvas id="pinkboard"></canvas>
<canvas id="innerLayer"></canvas>
<script>
/*
* Settings
*/
var settings = {
particles: {
length: 500, // maximum amount of particles
duration: 0.5, // particle duration in sec
velocity: 75, // particle velocity in pixels/sec
effect: 0, // play with this for a nice effect
size: 30, // particle size in pixels
},
};
var settings2 = {
particles: {
length: 10000, // maximum amount of particles
duration: 4.5, // particle duration in sec
velocity: 50, // particle velocity in pixels/sec
effect: -0.75, // play with this for a nice effect
size: 3, // particle size in pixels
},
};
/*
* RequestAnimationFrame polyfill by Erik Möller
*/
(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)}}}());
class Point {
constructor(x, y) {
this.x = (typeof x !== 'undefined') ? x : 0;
this.y = (typeof y !== 'undefined') ? y : 0;
}
clone() {
return new Point(this.x, this.y);
};
normalize() {
var length = this.length();
this.x /= length;
this.y /= length;
return this;
};
length(l) {
if (typeof l == 'undefined')
return Math.sqrt(this.x * this.x + this.y * this.y);
this.normalize();
this.x *= l;
this.y *= l;
return this;
};
}
class Particle {
constructor(settingConfig) {
this.position = new Point();
this.velocity = new Point();
this.acceleration = new Point();
this.age = 0;
this.settings = settingConfig;
}
initialize(x, y, dx, dy) {
this.position.x = x;
this.position.y = y;
this.velocity.x = dx;
this.velocity.y = dy;
this.acceleration.x = dx * this.settings.particles.effect;
this.acceleration.y = dy * this.settings.particles.effect;
this.age = 0;
};
update(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;
};
draw(context, image) {
function ease(t) {
return (--t) * t * t + 1;
}
var size = image.width * ease(this.age / this.settings.particles.duration);
context.globalAlpha = 1 - this.age / this.settings.particles.duration;
context.drawImage(image, this.position.x - size / 2, this.position.y - size / 2, size, size);
};
}
class ParticlePool {
constructor(length, settingConfig) {
// create and populate particle pool
this.particles = new Array(length);
this.settings = settingConfig
for (var i = 0; i < this.particles.length; i++)
this.particles[i] = new Particle(this.settings);
this.firstActive = 0,
this.firstFree = 0,
this.duration = this.settings.particles.duration;
}
add(x, y, dx, dy) {
this.particles[this.firstFree].initialize(x, y, dx, dy);
// handle circular queue
this.firstFree++;
if (this.firstFree == this.particles.length) this.firstFree = 0;
if (this.firstActive == this.firstFree ) this.firstActive++;
if (this.firstActive == this.particles.length) this.firstActive = 0;
}
update(deltaTime) {
var i;
// update active particles
if (this.firstActive < this.firstFree) {
for (i = this.firstActive; i < this.firstFree; i++)
this.particles[i].update(deltaTime);
}
if (this.firstFree < this.firstActive) {
for (i = this.firstActive; i < this.particles.length; i++)
this.particles[i].update(deltaTime);
for (i = 0; i < this.firstFree; i++)
this.particles[i].update(deltaTime);
}
// remove inactive particles
while (this.particles[this.firstActive].age >= this.duration && this.firstActive != this.firstFree) {
this.firstActive++;
if (this.firstActive == this.particles.length) this.firstActive = 0;
}
}
draw(context, image) {
// draw active particles
if (this.firstActive < this.firstFree) {
for (let i = this.firstActive; i < this.firstFree; i++)
this.particles[i].draw(context, image);
}
if (this.firstFree < this.firstActive) {
for (let i = this.firstActive; i < this.particles.length; i++)
this.particles[i].draw(context, image);
for (let i = 0; i < this.firstFree; i++)
this.particles[i].draw(context, image);
}
};
}
/*
* Putting it all together
*/
(function(canvas) {
var context = canvas.getContext('2d'),
particles = new ParticlePool(settings.particles.length, settings),
particleRate = settings.particles.length / settings.particles.duration, // particles/sec
time;
// get point on heart with -PI <= t <= PI
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
);
}
// creating the particle image using a dummy canvas
var image = (function() {
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
canvas.width = settings.particles.size;
canvas.height = settings.particles.size;
// helper function to create the path
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;
}
// create the path
context.beginPath();
var t = -Math.PI;
var point = to(t);
context.moveTo(point.x, point.y);
while (t < Math.PI) {
t += 0.01; // baby steps!
point = to(t);
context.lineTo(point.x, point.y);
}
context.closePath();
// create the fill
context.fillStyle = '#e07eaa';
context.fill();
// create the image
var image = new Image();
image.src = canvas.toDataURL();
return image;
})();
// render that thing!
function render() {
// next animation frame
requestAnimationFrame(render);
// update time
var newTime = new Date().getTime() / 1000,
deltaTime = newTime - (time || newTime);
time = newTime;
// clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// create new particles
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);
}
// update and draw particles
particles.update(deltaTime);
particles.draw(context, image);
}
// handle (re-)sizing of the canvas
function onResize() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
window.onresize = onResize;
// delay rendering bootstrap
setTimeout(function() {
onResize();
render();
}, 10);
})(document.getElementById('pinkboard'));
(function(canvas) {
var context = canvas.getContext('2d'),
particles = new ParticlePool(settings2.particles.length, settings2),
particleRate = 500, // particles/sec
time;
// get point on heart with -PI <= t <= PI
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
);
}
// creating the particle image using a dummy canvas
var image = (function() {
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
canvas.width = settings2.particles.size;
canvas.height = settings2.particles.size;
// helper function to create the path
function to(t) {
var point = pointOnHeart(t);
point.x = settings2.particles.size/2 + point.x * settings2.particles.size/350;
point.y = settings2.particles.size/2 - point.y * settings2.particles.size/350;
return point;
}
// create the path
context.beginPath();
var t = -Math.PI;
var point = to(t);
context.moveTo(point.x, point.y);
while (t < Math.PI) {
if (t < 0.1 || t > 0.1) {
t += 0.01; // baby steps!
} else {
t += 0.1
}
point = to(t);
context.lineTo(point.x, point.y);
}
context.closePath();
// create the fill
context.fillStyle = '#e07eaa';
context.fill();
// create the image
var image = new Image();
image.src = canvas.toDataURL();
return image;
})();
// render that thing!
function render() {
// next animation frame
requestAnimationFrame(render);
// update time
var newTime = new Date().getTime() / 1000,
deltaTime = newTime - (time || newTime);
time = newTime;
// clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// create new particles
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(settings2.particles.velocity);
particles.add(canvas.width / 2 + pos.x, canvas.height / 2 - pos.y, dir.x, -dir.y);
}
// update and draw particles
particles.update(deltaTime);
particles.draw(context, image);
}
// handle (re-)sizing of the canvas
function onResize() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
window.onresize = onResize;
// delay rendering bootstrap
setTimeout(function() {
onResize();
render();
}, 10);
})(document.getElementById('innerLayer'));
var colours=new Array('#f00', '#f06', '#f0f', '#f6f', '#f39', '#f9c'); // colours of the hearts
var minisize=10; // smallest size of hearts in pixels
var maxisize=20; // biggest size of hearts in pixels
var hearts=100; // maximum number of hearts on screen
var over_or_under="over"; // set to "over" for hearts to always be on top, or "under" to allow them to float behind other objects
/*****************************
JavaScript Love Heart Cursor
* (c)2013+ mf2fm web-design *
* http://www.mf2fm.com/rv *
* DON'T EDIT BELOW THIS BOX *
*****************************/
var x=ox=400;
var y=oy=300;
var swide=800;
var shigh=600;
var sleft=sdown=0;
var herz=new Array();
var herzx=new Array();
var herzy=new Array();
var herzs=new Array();
var kiss=false;
if (typeof('addRVLoadEvent')!='function') function addRVLoadEvent(funky) {
var oldonload=window.onload;
if (typeof(oldonload)!='function') window.onload=funky;
else window.onload=function() {
if (oldonload) oldonload();
funky();
}
}
addRVLoadEvent(mwah);
function mwah() { if (document.getElementById) {
var i, heart;
for (i=0; i<hearts; i++) {
heart=createDiv("auto", "auto");
heart.style.visibility="hidden";
heart.style.zIndex=(over_or_under=="over")?"1001":"0";
heart.style.color=colours[i%colours.length];
heart.style.pointerEvents="none";
if (navigator.appName=="Microsoft Internet Explorer") heart.style.filter="alpha(opacity=75)";
else heart.style.opacity=0.45;
heart.appendChild(document.createTextNode(String.fromCharCode(9829)));
document.body.appendChild(heart);
herz[i]=heart;
herzy[i]=false;
}
set_scroll();
set_width();
herzle();
}}
function herzle() {
var c;
if (Math.abs(x-ox)>1 || Math.abs(y-oy)>1) {
ox=x;
oy=y;
for (c=0; c<hearts; c++) if (herzy[c]===false) {
herz[c].firstChild.nodeValue=String.fromCharCode(9829);
herz[c].style.left=(herzx[c]=x-minisize/2)+"px";
herz[c].style.top=(herzy[c]=y-minisize)+"px";
herz[c].style.fontSize=minisize+"px";
herz[c].style.fontWeight='normal';
herz[c].style.visibility='visible';
herzs[c]=minisize;
break;
}
}
for (c=0; c<hearts; c++) if (herzy[c]!==false) blow_me_a_kiss(c);
setTimeout("herzle()", 30);
}
document.onmousedown=pucker;
document.onmouseup=function(){clearTimeout(kiss);};
function pucker() {
ox=-1;
oy=-1;
kiss=setTimeout('pucker()', 100);
}
function blow_me_a_kiss(i) {
herzy[i]-=herzs[i]/minisize+i%2;
herzx[i]+=(i%5-2)/5;
if (herzy[i]<sdown-herzs[i] || herzx[i]<sleft-herzs[i] || herzx[i]>sleft+swide-herzs[i]) {
herz[i].style.visibility="hidden";
herzy[i]=false;
}
else if (herzs[i]>minisize+1 && Math.random()<2.5/hearts) break_my_heart(i);
else {
if (Math.random()<maxisize/herzy[i] && herzs[i]<maxisize) herz[i].style.fontSize=(++herzs[i])+"px";
herz[i].style.top=herzy[i]+"px";
herz[i].style.left=herzx[i]+"px";
}
}
function break_my_heart(i) {
var t;
herz[i].firstChild.nodeValue=String.fromCharCode(9676);
herz[i].style.fontWeight='bold';
herzy[i]=false;
for (t=herzs[i]; t<=maxisize; t++) setTimeout('herz['+i+'].style.fontSize="'+t+'px"', 60*(t-herzs[i]));
setTimeout('herz['+i+'].style.visibility="hidden";', 60*(t-herzs[i]));
}
document.onmousemove=mouse;
function mouse(e) {
if (e) {
y=e.pageY;
x=e.pageX;
}
else {
set_scroll();
y=event.y+sdown;
x=event.x+sleft;
}
}
window.onresize=set_width;
function set_width() {
var sw_min=999999;
var sh_min=999999;
if (document.documentElement && document.documentElement.clientWidth) {
if (document.documentElement.clientWidth>0) sw_min=document.documentElement.clientWidth;
if (document.documentElement.clientHeight>0) sh_min=document.documentElement.clientHeight;
}
if (typeof(self.innerWidth)=='number' && self.innerWidth) {
if (self.innerWidth>0 && self.innerWidth<sw_min) sw_min=self.innerWidth;
if (self.innerHeight>0 && self.innerHeight<sh_min) sh_min=self.innerHeight;
}
if (document.body.clientWidth) {
if (document.body.clientWidth>0 && document.body.clientWidth<sw_min) sw_min=document.body.clientWidth;
if (document.body.clientHeight>0 && document.body.clientHeight<sh_min) sh_min=document.body.clientHeight;
}
if (sw_min==999999 || sh_min==999999) {
sw_min=800;
sh_min=600;
}
swide=sw_min;
shigh=sh_min;
}
window.onscroll=set_scroll;
function set_scroll() {
if (typeof(self.pageYOffset)=='number') {
sdown=self.pageYOffset;
sleft=self.pageXOffset;
}
else if (document.body && (document.body.scrollTop || document.body.scrollLeft)) {
sdown=document.body.scrollTop;
sleft=document.body.scrollLeft;
}
else if (document.documentElement && (document.documentElement.scrollTop || document.documentElement.scrollLeft)) {
sleft=document.documentElement.scrollLeft;
sdown=document.documentElement.scrollTop;
}
else {
sdown=0;
sleft=0;
}
}
function createDiv(height, width) {
var div=document.createElement("div");
div.style.position="absolute";
div.style.height=height;
div.style.width=width;
div.style.overflow="hidden";
div.style.backgroundColor="transparent";
return (div);
}
// ]]>
</script>
</BODY>
</HTML>
```