Animate when boosting (puffing up)
This commit is contained in:
@@ -4,6 +4,24 @@
|
||||
}
|
||||
|
||||
/* Keyframes */
|
||||
@keyframes bubble-up {
|
||||
0% { transform: translate(0, 0); }
|
||||
25% { transform: translate(0.5em, -2em); }
|
||||
50% { transform: translate(0, -4em); opacity: 0.66; }
|
||||
75% { transform: translate(0.5em, -6em); opacity: 0.33; }
|
||||
100% { transform: translate(0, -8em); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes float-up-left {
|
||||
0% { transform: translate(10px, 20px); }
|
||||
100% { transform: translate(0, 0); }
|
||||
}
|
||||
|
||||
@keyframes float-down-right {
|
||||
0% { transform: translate(-10px, -20px); }
|
||||
100% { transform: translate(0, 0); }
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0% { transform: translateX(-2rem); }
|
||||
25% { transform: translateX(2rem); }
|
||||
@@ -17,21 +35,3 @@
|
||||
50% { transform: translateX(-0.2rem); }
|
||||
75% { transform: translateX(0.2rem); }
|
||||
}
|
||||
|
||||
@keyframes splat {
|
||||
0% { transform: scale(150%); opacity: 0; }
|
||||
29% { transform: scale(150%); opacity: 0; }
|
||||
30% { transform: scale(150%); opacity: 0.5; }
|
||||
80% { transform: scale(90%); opacity: 1; }
|
||||
100% { transform: scale(100%); }
|
||||
}
|
||||
|
||||
@keyframes float-up-left {
|
||||
0% { transform: translate(10px, 20px); }
|
||||
100% { transform: translate(0, 0); }
|
||||
}
|
||||
|
||||
@keyframes float-down-right {
|
||||
0% { transform: translate(-10px, -20px); }
|
||||
100% { transform: translate(0, 0); }
|
||||
}
|
||||
|
||||
@@ -76,17 +76,26 @@
|
||||
color: var(--splat-color);
|
||||
}
|
||||
|
||||
&.splat__boosts {
|
||||
&:is(.splat__boosts) {
|
||||
aspect-ratio: 1;
|
||||
inset: 50cqi -1cqi auto auto;
|
||||
display: grid;
|
||||
min-inline-size: 6ch;
|
||||
padding: 0;
|
||||
place-items: center;
|
||||
inset: 55cqi -2cqi auto auto;
|
||||
|
||||
a {
|
||||
grid-area: 1/1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.windshield .splat:hover & {
|
||||
transform: translate(0, 1rem) !important;
|
||||
}
|
||||
|
||||
&.boosting {
|
||||
animation: bubble-up 500ms normal forwards ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.splat__category {
|
||||
@@ -110,7 +119,7 @@
|
||||
inset: 2cqi 2cqi auto auto;
|
||||
}
|
||||
|
||||
&.splat__meta {
|
||||
&:where(.splat__meta) {
|
||||
background-color: var(--color-bg);
|
||||
block-size: auto;
|
||||
color: var(--splat-color);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import { nextFrame } from "helpers/timing_helpers"
|
||||
|
||||
export default class extends Controller {
|
||||
static classes = [ "play" ]
|
||||
|
||||
async play() {
|
||||
await nextFrame()
|
||||
this.element.classList.remove(this.playClass)
|
||||
this.#forceReflow()
|
||||
this.element.classList.add(this.playClass)
|
||||
}
|
||||
|
||||
#forceReflow() {
|
||||
this.element.offsetWidth
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class extends Controller {
|
||||
static classes = [ "toggle" ]
|
||||
|
||||
toggle() {
|
||||
this.element.classList.toggle(this.toggleClass)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export function throttle(fn, delay = 1000) {
|
||||
let timeoutId = null
|
||||
|
||||
return (...args) => {
|
||||
if (!timeoutId) {
|
||||
fn(...args)
|
||||
timeoutId = setTimeout(() => timeoutId = null, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function debounce(fn, delay = 1000) {
|
||||
let timeoutId = null
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => fn.apply(this, args), delay)
|
||||
}
|
||||
}
|
||||
|
||||
export function nextEventLoopTick() {
|
||||
return delay(0)
|
||||
}
|
||||
|
||||
export function onNextEventLoopTick(callback) {
|
||||
setTimeout(callback, 0)
|
||||
}
|
||||
|
||||
export function nextFrame() {
|
||||
return new Promise(requestAnimationFrame)
|
||||
}
|
||||
|
||||
export function nextEventNamed(eventName, element = window) {
|
||||
return new Promise(resolve => element.addEventListener(eventName, resolve, { once: true }))
|
||||
}
|
||||
|
||||
export function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<%= link_to splat.boosts.any? ? "+ #{splat.boosts.size}" : "+", new_splat_boost_path(splat) %>
|
||||
<%= link_to splat.boosts.any? ? "+ #{splat.boosts.size}" : "+", new_splat_boost_path(splat), data: { action: "toggle-class#toggle" } %>
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
<% end %>
|
||||
|
||||
|
||||
<div class="splat__bubble splat__meta splat__boosts">
|
||||
<%= tag.div class: "splat__bubble splat__meta splat__boosts",
|
||||
data: { controller: "animation toggle-class", animation_play_class: "boosting", toggle_class_toggle_class: "boosting", action: "animationend->toggle-class#toggle" } do %>
|
||||
<turbo-frame id="<%= dom_id(splat, "boosts") %>" src="<%= splat_boosts_path(splat) %>" />
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -4,4 +4,6 @@ pin "application"
|
||||
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
||||
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
||||
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
||||
|
||||
pin_all_from "app/javascript/controllers", under: "controllers"
|
||||
pin_all_from "app/javascript/helpers", under: "helpers"
|
||||
|
||||
Reference in New Issue
Block a user