/*
* File Name / blowingHeart.js
* Created Date / Aug 24, 2020
* Aurhor / Toshiya Marukubo
*/
/*
Common Tool.
*/
class Tool {
// random number.
static randomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// random color rgb.
static randomColorRGB() {
return (
"rgb(" +
this.randomNumber(0, 255) +
", " +
this.randomNumber(0, 255) +
", " +
this.randomNumber(0, 255) +
")"
);
}
// random color hsl.
static randomColorHSL(hue, saturation, lightness) {
return (
"hsl(" +
hue +
", " +
saturation +
"%, " +
lightness +
"%)"
);
}
// gradient color.
static gradientColor(ctx, cr, cg, cb, ca, x, y, r) {
const col = cr + "," + cg + "," + cb;
const g = ctx.createRadialGradient(x, y, 0, x, y, r);
g.addColorStop(0, "rgba(" + col + ", " + (ca * 1) + ")");
g.addColorStop(0.5, "rgba(" + col + ", " + (ca * 0.5) + ")");
g.addColorStop(1, "rgba(" + col + ", " + (ca * 0) + ")");
return g;
}
}
/*
When want to use Angle and radian.
*/
class Angle {
constructor(a) {
this.a = a;
this.rad = (this.a * Math.PI) / 180;
}
incDec(num) {
this.a += num;
this.rad = (this.a * Math.PI) / 180;
}
}
/*
variable for canvas.
*/
let canvas;
let offCanvas;
class Canvas {
constructor(bool) {
// create canvas.
this.canvas = document.createElement("canvas");
// if on screen.
if (bool === true) {
this.canvas.style.position = 'relative';
this.canvas.style.display = 'block';
this.canvas.style.top = 0;
this.canvas.style.left = 0;
document.getElementsByTagName("body")[0].appendChild(this.canvas);
}
this.ctx = this.canvas.getContext("2d");
this.width = this.canvas.width = window.innerWidth;
this.height = this.canvas.height = window.innerHeight;
// size.
this.width < 768 ? this.heartSize = 180 : this.heartSize = 250;
// mouse infomation.
this.mouseX = null;
this.mouseY = null;
// sprite array and quantity.
this.hearts = [];
this.offHeartNum = 1;
this.offHearts = [];
// offscreen data.
this.data = null;
}
onInit() {
let index = 0;
for (let i = 0; i < this.height; i += 12) {
for (let j = 0; j < this.width; j += 12) {
let oI = (j + i * this.width) * 4 + 3;
if (this.data[oI] > 0) {
index++;
const h = new Heart(canvas.ctx, j + Tool.randomNumber(-3, 3), i + Tool.randomNumber(-3, 3), Tool.randomNumber(6, 12), index);
canvas.hearts.push(h);
}
}
}
}
offInit() {
for (let i = 0; i < this.offHeartNum; i++) {
const s = new Heart(this.ctx, this.width / 2, this.height / 2.3, this.heartSize);
this.offHearts.push(s);
}
for (let i = 0; i < this.offHearts.length; i++) {
this.offHearts[i].offRender(i);
}
// data
this.data = this.ctx.getImageData(0, 0, this.width, this.height).data;
// on screen init.
this.onInit();
}
render() {
this.ctx.clearRect(0, 0, this.width, this.height);
for (let i = 0; i < this.hearts.length; i++) {
this.hearts[i].render(i);
}
}
resize() {
this.offHearts = [];
this.hearts = [];
this.width = this.canvas.width = window.innerWidth;
this.height = this.canvas.height = window.innerHeight;
this.width < 768 ? this.heartSize = 180 : this.heartSize = 250;
}
}
class Heart {
constructor(ctx, x, y, r, i) {
this.ctx = ctx;
this.init(x, y, r, i);
}
init(x, y, r, i) {
this.x = x;
this.xi = x;
this.y = y;
this.yi = y;
this.r = r;
this.i = i * 0.5 + 200;
this.l = this.i;
this.c = Tool.randomColorHSL(Tool.randomNumber(-5, 5), 80, 60);
this.a = new Angle(Tool.randomNumber(0, 360));
this.v = {
x: Math.random(),
y: -Math.random()
};
this.ga = Math.random();
}
draw() {
const ctx = this.ctx;
ctx.save();
ctx.globalCompositeOperation = 'lighter';
ctx.globalAlpha = this.ga;
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.moveTo(this.x, this.y + this.r);
ctx.bezierCurveTo(
this.x - this.r - this.r / 5,
this.y + this.r / 1.5,
this.x - this.r,
this.y - this.r,
this.x,
this.y - this.r / 5
);
ctx.bezierCurveTo(
this.x + this.r,
this.y - this.r,
this.x + this.r + this.r / 5,
this.y + this.r / 1.5,
this.x,
this.y + this.r
);
ctx.closePath();
ctx.fill();
ctx.restore();
}
updateParams() {
this.a.incDec(1);
Math.sin(this.a.rad) < 0 ? this.r = -Math.sin(this.a.rad) * 20 : this.r = Math.sin(this.a.rad) * 20;
}
updatePosition() {
this.l -= 1;
if (this.l < 0) {
this.v.y -= 0.01;
this.v.x += 0.02;
this.y += this.v.y;
this.x += this.v.x;
}
}
wrapPosition() {
if (this.x > canvas.width * 1.5) {
this.init(this.xi, this.yi, Tool.randomNumber(6, 12), this.i);
}
}
render() {
this.wrapPosition();
this.updateParams();
this.updatePosition();
this.draw();
}
offRender(i) {
this.draw();
}
}
(function () {
"use strict";
window.addEventListener("load", function () {
offCanvas = new Canvas(false);
canvas = new Canvas(true);
offCanvas.offInit();
function render() {
window.requestAnimationFrame(function () {
canvas.render();
render();
});
}
render();
// event
window.addEventListener("resize", function () {
canvas.resize();
offCanvas.resize();
offCanvas.offInit();
}, false);
});
})();
* {
margin: 0;
padding: 0;
}
html,
body {
overflow: hidden;
}
body {
position: relative;
background: #000;
}
console