diff --git a/cs/script.js b/cs/script.js new file mode 100644 index 0000000..91f24ce --- /dev/null +++ b/cs/script.js @@ -0,0 +1,232 @@ +var settings = { + particles: { + length: 500, // maximum amount of particles + duration: 2, // particle duration in sec + velocity: 100, // particle velocity in pixels/sec + effect: -0.6, // play with this for a nice effect + size: 28, // particle size in pixels + }, +}; + +(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) } } +}()); + +/* + * Point class + */ +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; +})(); + +/* + * Particle class + */ +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; +})(); + +/* + * ParticlePool class + */ +var ParticlePool = (function () { + var particles, + firstActive = 0, + firstFree = 0, + duration = settings.particles.duration; + + function ParticlePool(length) { + // create and populate particle pool + 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); + + // handle circular queue + firstFree++; + if (firstFree == particles.length) firstFree = 0; + if (firstActive == firstFree) firstActive++; + if (firstActive == particles.length) firstActive = 0; + }; + ParticlePool.prototype.update = function (deltaTime) { + var i; + + // update active particles + 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); + } + + // remove inactive particles + while (particles[firstActive].age >= duration && firstActive != firstFree) { + firstActive++; + if (firstActive == particles.length) firstActive = 0; + } + + + }; + ParticlePool.prototype.draw = function (context, image) { + // draw active particles + 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; +})(); + +/* + * Putting it all together + */ +(function (canvas) { + var context = canvas.getContext('2d'), + particles = new ParticlePool(settings.particles.length), + 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 = '#c88ffe'; + context.fillStyle = '#ffc0cb'; + 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('lovexf')); \ No newline at end of file diff --git a/cs/style.css b/cs/style.css new file mode 100644 index 0000000..d1348dd --- /dev/null +++ b/cs/style.css @@ -0,0 +1,66 @@ +* { + padding: 0; + margin: 0; +} + +body { + background: url(https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dec470b0-fed6-46e5-be53-ec85fadc01be/b3997a29-3506-4fa5-9a54-57b967b1990a.webp) no-repeat center center; + background-size: cover; + height: 100vh; +} + +body::after { + content: ''; + display: block; + position: absolute; + width: 100%; + height: 100vh; + -webkit-background-color: rgba(0, 0, 0, .5); + background-color: rgba(0, 0, 0, .5); +} + +audio, +.love_you { + position: absolute; + left: 50%; + top: 1.5rem; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + z-index: 1809185784; +} + +.love_you { + top: 45%; + font-family: Georgia, 'Times New Roman', Times, serif; + font-size: 1.3rem; + font-weight: bold; + color: #ffc0cb; + letter-spacing: .05rem; + -moz-animation: love-xf 1.2s alternate ease-in-out; + -webkit-animation: love-xf 1.2s alternate ease-in-out; + animation: love-xf 1.2s alternate ease-in-out; + user-select: none; + -webkit-text-shadow: 0 0 8px #fff; + text-shadow: 0 0 8px #fff; +} + +#lovexf { + position: absolute; + width: 100%; + height: 100%; + z-index: 520; +} + +@keyframes love-xf { + 0% { + opacity: 0.08; + filter: blur(8px); + letter-spacing: .1rem; + } + + 100% { + opacity: 1; + filter: blur(0); + letter-spacing: .05rem; + } +} \ No newline at end of file