Created
December 5, 2021 16:41
-
-
Save aero012/a8df57376000cc9dea91d064b63cbebb to your computer and use it in GitHub Desktop.
Falling Sakura leaves
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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