Skip to content

Instantly share code, notes, and snippets.

@aero012
Created December 5, 2021 16:41
Show Gist options
  • Save aero012/a8df57376000cc9dea91d064b63cbebb to your computer and use it in GitHub Desktop.
Save aero012/a8df57376000cc9dea91d064b63cbebb to your computer and use it in GitHub Desktop.
Falling Sakura leaves
<!DOCTYPE html>
<!--
All credit goes to http://jsfiddle.net/aKr8D/21/
-->
<html>
<head>
<title>Falling Sakura leaves</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
@-webkit-keyframes fall {
0% {
opacity: 0.9;
top: 0
}
100% {
opacity: 0.2;
top: 100%
}
}
@keyframes fall {
0% {
opacity: 0.9;
top: 0
}
100% {
opacity: 0.2;
top: 100%
}
}
@-webkit-keyframes blow-soft-left {
0% {
margin-left: 0
}
100% {
margin-left: -50%
}
}
@keyframes blow-soft-left {
0% {
margin-left: 0
}
100% {
margin-left: -50%
}
}
@-webkit-keyframes blow-medium-left {
0% {
margin-left: 0
}
100% {
margin-left: -100%
}
}
@keyframes blow-medium-left {
0% {
margin-left: 0
}
100% {
margin-left: -100%
}
}
@-webkit-keyframes blow-soft-right {
0% {
margin-left: 0
}
100% {
margin-left: 50%
}
}
@keyframes blow-soft-right {
0% {
margin-left: 0
}
100% {
margin-left: 50%
}
}
@-webkit-keyframes blow-medium-right {
0% {
margin-left: 0
}
100% {
margin-left: 100%
}
}
@keyframes blow-medium-right {
0% {
margin-left: 0
}
100% {
margin-left: 100%
}
}
@-webkit-keyframes sway-0 {
0% {
-webkit-transform: rotate(-5deg)
}
40% {
-webkit-transform: rotate(28deg)
}
100% {
-webkit-transform: rotate(3deg)
}
}
@keyframes sway-0 {
0% {
-ms-transform: rotate(-5deg);
transform: rotate(-5deg)
}
40% {
-ms-transform: rotate(28deg);
transform: rotate(28deg)
}
100% {
-ms-transform: rotate(3deg);
transform: rotate(3deg)
}
}
@-webkit-keyframes sway-1 {
0% {
-webkit-transform: rotate(10deg)
}
40% {
-webkit-transform: rotate(43deg)
}
100% {
-webkit-transform: rotate(15deg)
}
}
@keyframes sway-1 {
0% {
-ms-transform: rotate(10deg);
transform: rotate(10deg)
}
40% {
-ms-transform: rotate(43deg);
transform: rotate(43deg)
}
100% {
-ms-transform: rotate(15deg);
transform: rotate(15deg)
}
}
@-webkit-keyframes sway-2 {
0% {
-webkit-transform: rotate(15deg)
}
40% {
-webkit-transform: rotate(56deg)
}
100% {
-webkit-transform: rotate(22deg)
}
}
@keyframes sway-2 {
0% {
-ms-transform: rotate(15deg);
transform: rotate(15deg)
}
40% {
-ms-transform: rotate(56deg);
transform: rotate(56deg)
}
100% {
-ms-transform: rotate(22deg);
transform: rotate(22deg)
}
}
@-webkit-keyframes sway-3 {
0% {
-webkit-transform: rotate(25deg)
}
40% {
-webkit-transform: rotate(74deg)
}
100% {
-webkit-transform: rotate(37deg)
}
}
@keyframes sway-3 {
0% {
-ms-transform: rotate(25deg);
transform: rotate(25deg)
}
40% {
-ms-transform: rotate(74deg);
transform: rotate(74deg)
}
100% {
-ms-transform: rotate(37deg);
transform: rotate(37deg)
}
}
@-webkit-keyframes sway-4 {
0% {
-webkit-transform: rotate(40deg)
}
40% {
-webkit-transform: rotate(68deg)
}
100% {
-webkit-transform: rotate(25deg)
}
}
@keyframes sway-4 {
0% {
-ms-transform: rotate(40deg);
transform: rotate(40deg)
}
40% {
-ms-transform: rotate(68deg);
transform: rotate(68deg)
}
100% {
-ms-transform: rotate(25deg);
transform: rotate(25deg)
}
}
@-webkit-keyframes sway-5 {
0% {
-webkit-transform: rotate(50deg)
}
40% {
-webkit-transform: rotate(78deg)
}
100% {
-webkit-transform: rotate(40deg)
}
}
@keyframes sway-5 {
0% {
-ms-transform: rotate(50deg);
transform: rotate(50deg)
}
40% {
-ms-transform: rotate(78deg);
transform: rotate(78deg)
}
100% {
-ms-transform: rotate(40deg);
transform: rotate(40deg)
}
}
@-webkit-keyframes sway-6 {
0% {
-webkit-transform: rotate(65deg)
}
40% {
-webkit-transform: rotate(92deg)
}
100% {
-webkit-transform: rotate(58deg)
}
}
@keyframes sway-6 {
0% {
-ms-transform: rotate(65deg);
transform: rotate(65deg)
}
40% {
-ms-transform: rotate(92deg);
transform: rotate(92deg)
}
100% {
-ms-transform: rotate(58deg);
transform: rotate(58deg)
}
}
@-webkit-keyframes sway-7 {
0% {
-webkit-transform: rotate(72deg)
}
40% {
-webkit-transform: rotate(118deg)
}
100% {
-webkit-transform: rotate(68deg)
}
}
@keyframes sway-7 {
0% {
-ms-transform: rotate(72deg);
transform: rotate(72deg)
}
40% {
-ms-transform: rotate(118deg);
transform: rotate(118deg)
}
100% {
-ms-transform: rotate(68deg);
transform: rotate(68deg)
}
}
@-webkit-keyframes sway-8 {
0% {
-webkit-transform: rotate(94deg)
}
40% {
-webkit-transform: rotate(136deg)
}
100% {
-webkit-transform: rotate(82deg)
}
}
@keyframes sway-8 {
0% {
-ms-transform: rotate(94deg);
transform: rotate(94deg)
}
40% {
-ms-transform: rotate(136deg);
transform: rotate(136deg)
}
100% {
-ms-transform: rotate(82deg);
transform: rotate(82deg)
}
}
.sakura {
background: -webkit-linear-gradient(120deg, rgba(255, 183, 197, 0.9), rgba(255, 197, 208, 0.9));
background: linear-gradient(120deg, rgba(255, 183, 197, 0.9), rgba(255, 197, 208, 0.9));
pointer-events: none;
position: absolute
}
</style>
</head>
<body>
<script type="text/javascript">
// Plugin code
(function ($) {
/** Polyfills and prerequisites **/
// requestAnimationFrame Polyfill
var lastTime = 0;
var vendors = ['webkit', 'o', 'ms', 'moz', ''];
var vendorCount = vendors.length;
for (var x = 0; x < vendorCount && ! window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if ( ! window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if ( ! window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
// Prefixed event check
$.fn.prefixedEvent = function(type, callback) {
for (var x = 0; x < vendorCount; ++x) {
if ( ! vendors[x]) {
type = type.toLowerCase();
}
el = (this instanceof jQuery ? this[0] : this);
el.addEventListener(vendors[x] + type, callback, false);
}
return this;
};
// Test if element is in viewport
function elementInViewport(el) {
if (el instanceof jQuery) {
el = el[0];
}
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)
);
}
// 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;
}
/** Actual plugin code **/
$.fn.sakura = function (event, options) {
// Target element
var target = this.selector == "" ? $('body') : this;
// Defaults for the option object, which gets extended below
var defaults = {
blowAnimations: ['blow-soft-left', 'blow-medium-left', 'blow-soft-right', 'blow-medium-right'],
className: 'sakura',
fallSpeed: 1,
maxSize: 14,
minSize: 10,
newOn: 300,
swayAnimations: ['sway-0', 'sway-1', 'sway-2', 'sway-3', 'sway-4', 'sway-5', 'sway-6', 'sway-7', 'sway-8']
};
var options = $.extend({}, defaults, options);
// Default or start event
if (typeof event === 'undefined' || event === 'start') {
// Set the overflow-x CSS property on the target element to prevent horizontal scrollbars
target.css({ 'overflow-x': 'hidden' });
// Function that inserts new petals into the document
var petalCreator = function () {
if (target.data('sakura-anim-id')) {
setTimeout(function () {
requestAnimationFrame(petalCreator);
}, options.newOn);
}
// Get one random animation of each type and randomize fall time of the petals
var blowAnimation = randomArrayElem(options.blowAnimations);
var swayAnimation = randomArrayElem(options.swayAnimations);
var fallTime = ((document.documentElement.clientHeight * 0.007) + Math.round(Math.random() * 5)) * options.fallSpeed;
// Build animation
var animations =
'fall ' + fallTime + 's linear 0s 1' + ', ' +
blowAnimation + ' ' + (((fallTime > 30 ? fallTime : 30) - 20) + randomInt(0, 20)) + 's linear 0s infinite' + ', ' +
swayAnimation + ' ' + randomInt(2, 4) + 's linear 0s infinite';
// Create petal and randomize size
var petal = $('<div class="' + options.className + '" />');
var height = randomInt(options.minSize, options.maxSize);
var width = height - Math.floor(randomInt(0, options.minSize) / 3);
// Apply Event Listener to remove petals that reach the bottom of the page
petal.prefixedEvent('AnimationEnd', function () {
if ( ! elementInViewport(this)) {
$(this).remove();
}
})
// Apply Event Listener to remove petals that finish their horizontal float animation
.prefixedEvent('AnimationIteration', function (ev) {
if (
(
$.inArray(ev.animationName, options.blowAnimations) != -1 ||
$.inArray(ev.animationName, options.swayAnimations) != -1
) &&
! elementInViewport(this)
) {
$(this).remove();
}
})
.css({
'-webkit-animation': animations,
animation: animations,
'border-radius': randomInt(options.maxSize, (options.maxSize + Math.floor(Math.random() * 10))) + 'px ' + randomInt(1, Math.floor(width / 4)) + 'px',
height: height + 'px',
left: (Math.random() * document.documentElement.clientWidth - 100) + 'px',
'margin-top': (-(Math.floor(Math.random() * 20) + 15)) + 'px',
width: width + 'px'
});
target.append(petal);
};
// Finally: Start adding petals
target.data('sakura-anim-id', requestAnimationFrame(petalCreator));
}
// Stop event, which stops the animation loop and removes all current blossoms
else if (event === 'stop') {
// Cancel animation
var animId = target.data('sakura-anim-id');
if (animId) {
cancelAnimationFrame(animId);
target.data('sakura-anim-id', null);
}
// Remove all current blossoms
setTimeout(function() {
$('.' + options.className).remove();
}, (options.newOn + 50));
}
};
}(jQuery));
$(document).ready(function() {
$('body').sakura();
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment