Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	| from typing import Union | |
| import streamlit as st | |
| import inspect | |
| from importlib import import_module | |
| from pathlib import Path | |
| from typing import Any, Callable, Optional, TypeVar, Union, overload | |
| try: | |
| from streamlit.runtime.metrics_util import gather_metrics as _gather_metrics | |
| except ImportError: | |
| def _gather_metrics(name, func): # type: ignore | |
| return func | |
| F = TypeVar("F", bound=Callable[..., Any]) | |
| # Typing overloads here are actually required so that you can correctly (= with correct typing) use the decorator in different ways: | |
| # 1) as a decorator without parameters @extra | |
| # 2) as a decorator with parameters (@extra(foo="bar") but this also refers to empty parameters @extra() | |
| # 3) as a function: extra(my_function) | |
| def extra( | |
| func: F, | |
| ) -> F: | |
| ... | |
| def extra( | |
| func: None = None, | |
| ) -> Callable[[F], F]: | |
| ... | |
| def extra( | |
| func: Optional[F] = None, | |
| ) -> Union[Callable[[F], F], F]: | |
| if func: | |
| filename = inspect.stack()[1].filename | |
| submodule = Path(filename).parent.name | |
| extra_name = "streamlit_extras." + submodule | |
| module = import_module(extra_name) | |
| if hasattr(module, "__funcs__"): | |
| module.__funcs__ += [func] # type: ignore | |
| else: | |
| module.__funcs__ = [func] # type: ignore | |
| profiling_name = f"{submodule}.{func.__name__}" | |
| try: | |
| return _gather_metrics(name=profiling_name, func=func) | |
| except TypeError: | |
| # Don't fail on streamlit==1.13.0, which only expects a callable | |
| pass | |
| def wrapper(f: F) -> F: | |
| return f | |
| return wrapper | |
| def proportional_rain( | |
| emoji1: str, | |
| count1: int, | |
| emoji2: str, | |
| count2: int, | |
| font_size: int = 64, | |
| falling_speed: int = 5, | |
| animation_length: Union[int, str] = "infinite" | |
| ): | |
| """ | |
| Creates a CSS animation where input emojis fall from top to bottom of the screen. | |
| The proportion of emojis is based on the provided counts. | |
| """ | |
| if isinstance(animation_length, int): | |
| animation_length = f"{animation_length}" | |
| # CSS Code ... | |
| st.write( | |
| f""" | |
| <style> | |
| body {{ | |
| background: gray; | |
| }} | |
| .emoji {{ | |
| color: #777; | |
| font-size: {font_size}px; | |
| font-family: Arial; | |
| // text-shadow: 0 0 5px #000; | |
| }} | |
| ///*delete for no hover-effect*/ | |
| //.emoji:hover {{ | |
| // font-size: 60px; | |
| // text-shadow: 5px 5px 5px white; | |
| //}} | |
| @-webkit-keyframes emojis-fall {{ | |
| 0% {{ | |
| top: -10%; | |
| }} | |
| 100% {{ | |
| top: 100%; | |
| }} | |
| }} | |
| @-webkit-keyframes emojis-shake {{ | |
| 0% {{ | |
| -webkit-transform: translateX(0px); | |
| transform: translateX(0px); | |
| }} | |
| 50% {{ | |
| -webkit-transform: translateX(20px); | |
| transform: translateX(20px); | |
| }} | |
| 100% {{ | |
| -webkit-transform: translateX(0px); | |
| transform: translateX(0px); | |
| }} | |
| }} | |
| @keyframes emojis-fall {{ | |
| 0% {{ | |
| top: -10%; | |
| }} | |
| 100% {{ | |
| top: 100%; | |
| }} | |
| }} | |
| @keyframes emojis-shake {{ | |
| 0% {{ | |
| transform: translateX(0px); | |
| }} | |
| 25% {{ | |
| transform: translateX(15px); | |
| }} | |
| 50% {{ | |
| transform: translateX(-15px); | |
| }} | |
| 100% {{ | |
| transform: translateX(0px); | |
| }} | |
| }} | |
| .emoji {{ | |
| position: fixed; | |
| top: -10%; | |
| z-index: 99999; | |
| -webkit-user-select: none; | |
| -moz-user-select: none; | |
| -ms-user-select: none; | |
| user-select: none; | |
| cursor: default; | |
| -webkit-animation-name: emojis-fall, emojis-shake; | |
| -webkit-animation-duration: 5s, 3s; | |
| -webkit-animation-timing-function: linear, ease-in-out; | |
| -webkit-animation-iteration-count: {animation_length}, {animation_length}; // overall length | |
| -webkit-animation-play-state: running, running; | |
| animation-name: emojis-fall, emojis-shake; | |
| animation-duration: {falling_speed}s, 3s; // fall speed | |
| animation-timing-function: linear, ease-in-out; | |
| animation-iteration-count: {animation_length}, {animation_length}; // overall length | |
| animation-play-state: running, running; | |
| }} | |
| .emoji:nth-of-type(0) {{ | |
| left: 1%; | |
| -webkit-animation-delay: 0s, 0s; | |
| animation-delay: 0s, 0s; | |
| }} | |
| .emoji:nth-of-type(1) {{ | |
| left: 10%; | |
| -webkit-animation-delay: 1s, 1s; | |
| animation-delay: 1s, 1s; | |
| }} | |
| .emoji:nth-of-type(2) {{ | |
| left: 20%; | |
| -webkit-animation-delay: 6s, 0.5s; | |
| animation-delay: 6s, 0.5s; | |
| }} | |
| .emoji:nth-of-type(3) {{ | |
| left: 30%; | |
| -webkit-animation-delay: 4s, 2s; | |
| animation-delay: 4s, 2s; | |
| }} | |
| .emoji:nth-of-type(4) {{ | |
| left: 40%; | |
| -webkit-animation-delay: 2s, 2s; | |
| animation-delay: 2s, 2s; | |
| }} | |
| .emoji:nth-of-type(5) {{ | |
| left: 50%; | |
| -webkit-animation-delay: 8s, 3s; | |
| animation-delay: 8s, 3s; | |
| }} | |
| .emoji:nth-of-type(6) {{ | |
| left: 60%; | |
| -webkit-animation-delay: 6s, 2s; | |
| animation-delay: 6s, 2s; | |
| }} | |
| .emoji:nth-of-type(7) {{ | |
| left: 70%; | |
| -webkit-animation-delay: 2.5s, 1s; | |
| animation-delay: 2.5s, 1s; | |
| }} | |
| .emoji:nth-of-type(8) {{ | |
| left: 80%; | |
| -webkit-animation-delay: 1s, 0s; | |
| animation-delay: 1s, 0s; | |
| }} | |
| .emoji:nth-of-type(9) {{ | |
| left: 90%; | |
| -webkit-animation-delay: 3s, 1.5s; | |
| animation-delay: 3s, 1.5s; | |
| }} | |
| </style> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # Create emoji strings based on counts | |
| emoji_str1 = "".join([f'<div class="emoji">{emoji1}</div>' for _ in range(count1)]) | |
| emoji_str2 = "".join([f'<div class="emoji">{emoji2}</div>' for _ in range(count2)]) | |
| st.write( | |
| f""" | |
| <!--get emojis from https://getemoji.com--> | |
| <div class="emojis"> | |
| {emoji_str1} | |
| {emoji_str2} | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) |