Music / sakura.js
johnsmith007's picture
d
b455d0c verified
var Sakura = function Sakura(selector, options) {
var _this = this;
if (typeof selector === 'undefined') {
throw new Error('No selector present. Define an element.');
}
this.el = document.querySelector(selector); // Defaults for the option object, which gets extended below.
var defaults = {
className: 'sakura',
// Classname of the petal. This corresponds with the css.
fallSpeed: 1,
// Speed factor in which the petal falls.
maxSize: 14,
// The maximum size of the petal.
minSize: 10,
// The minimum size of the petal.
delay: 300,
// Delay between petals.
gradientColorStart: 'rgba(255, 183, 197, 0.9)',
// Gradient color start (rgba).
gradientColorEnd: 'rgba(255, 197, 208, 0.9)',
// Gradient color end (rgba).
gradientColorDegree: 120 // Gradient degree angle.
}; // Merge defaults with user options.
var extend = function extend(originalObj, newObj) {
Object.keys(originalObj).forEach(function (key) {
if (newObj && Object.prototype.hasOwnProperty.call(newObj, key)) {
var origin = originalObj;
origin[key] = newObj[key];
}
});
return originalObj;
};
this.settings = extend(defaults, options); // Hide horizontal scrollbars on the target element.
this.el.style.overflowX = 'hidden'; // Random array element
function randomArrayElem(arr) {
return arr[Math.floor(Math.random() * arr.length)];
} // Random integer
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
} // Check for animation events.
var prefixes = ['webkit', 'moz', 'MS', 'o', ''];
function PrefixedEvent(element, type, callback) {
for (var p = 0; p < prefixes.length; p += 1) {
var animType = type;
if (!prefixes[p]) {
animType = type.toLowerCase();
}
element.addEventListener(prefixes[p] + animType, callback, false);
}
} // Check if the element is in the viewport.
function elementInViewport(el) {
var rect = el.getBoundingClientRect();
return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
}
this.createPetal = function () {
if (_this.el.dataset.sakuraAnimId) {
setTimeout(function () {
window.requestAnimationFrame(_this.createPetal);
}, _this.settings.delay);
} // Name the animations. These have to match the animations in the CSS file.
var animationNames = {
blowAnimations: ['blow-soft-left', 'blow-medium-left', 'blow-soft-right', 'blow-medium-right'],
swayAnimations: ['sway-0', 'sway-1', 'sway-2', 'sway-3', 'sway-4', 'sway-5', 'sway-6', 'sway-7', 'sway-8']
}; // Get one random animation of each type and randomize fall time of the petals
var blowAnimation = randomArrayElem(animationNames.blowAnimations);
var swayAnimation = randomArrayElem(animationNames.swayAnimations);
var fallTime = (document.documentElement.clientHeight * 0.007 + Math.round(Math.random() * 5)) * _this.settings.fallSpeed; // Create animations
var animationsArr = ["fall ".concat(fallTime, "s linear 0s 1"), "".concat(blowAnimation, " ").concat((fallTime > 30 ? fallTime : 30) - 20 + randomInt(0, 20), "s linear 0s infinite"), "".concat(swayAnimation, " ").concat(randomInt(2, 4), "s linear 0s infinite")];
var animations = animationsArr.join(', '); // Create petal and give it a random size.
var petal = document.createElement('div');
petal.classList.add(_this.settings.className);
var height = randomInt(_this.settings.minSize, _this.settings.maxSize);
var width = height - Math.floor(randomInt(0, _this.settings.minSize) / 3);
petal.style.background = "linear-gradient(".concat(_this.settings.gradientColorDegree, "deg, ").concat(_this.settings.gradientColorStart, ", ").concat(_this.settings.gradientColorEnd, ")");
petal.style.webkitAnimation = animations;
petal.style.animation = animations;
petal.style.borderRadius = "".concat(randomInt(_this.settings.maxSize, _this.settings.maxSize + Math.floor(Math.random() * 10)), "px ").concat(randomInt(1, Math.floor(width / 4)), "px");
petal.style.height = "".concat(height, "px");
petal.style.left = "".concat(Math.random() * document.documentElement.clientWidth - 100, "px");
petal.style.marginTop = "".concat(-(Math.floor(Math.random() * 20) + 15), "px");
petal.style.width = "".concat(width, "px"); // Remove petals of which the animation ended.
PrefixedEvent(petal, 'AnimationEnd', function () {
if (!elementInViewport(petal)) {
petal.remove();
}
}); // Remove petals that float out of the viewport.
PrefixedEvent(petal, 'AnimationIteration', function () {
if (!elementInViewport(petal)) {
petal.remove();
}
}); // Add the petal to the target element.
_this.el.appendChild(petal);
};
this.el.setAttribute('data-sakura-anim-id', window.requestAnimationFrame(this.createPetal));
};
Sakura.prototype.start = function () {
var animId = this.el.dataset.sakuraAnimId;
if (!animId) {
this.el.setAttribute('data-sakura-anim-id', window.requestAnimationFrame(this.createPetal));
} else {
throw new Error('Sakura is already running.');
}
};
Sakura.prototype.stop = function () {
var _this2 = this;
var graceful = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var animId = this.el.dataset.sakuraAnimId;
if (animId) {
window.cancelAnimationFrame(animId);
this.el.setAttribute('data-sakura-anim-id', '');
} // Remove all current blossoms at once.
// You can also set 'graceful' to true to stop new petals from being created.
// This way the petals won't be removed abruptly.
if (!graceful) {
setTimeout(function () {
var petals = document.getElementsByClassName(_this2.settings.className);
while (petals.length > 0) {
petals[0].parentNode.removeChild(petals[0]);
}
}, this.settings.delay + 50);
}
};