3333.note {text-align : center;font-size : .88rem ;opacity : .72 ;margin-top : 18px }
3434.toast {position : fixed;left : 50% ;bottom : 24px ;transform : translateX (-50% );background : var (--green );color : var (--cream );padding : 10px 16px ;border-radius : 999px ;font-weight : 800 ;opacity : 0 ;pointer-events : none;transition : opacity .18s }
3535.toast .show {opacity : 1 }
36+ /* ============================================================
37+ RESULT SHUFFLE ANIMATION
38+ ============================================================ */
39+
40+ .value .shuffling {
41+ font-style : italic;
42+ opacity : 0.72 ;
43+ }
44+
45+ .label .is-rolling ,
46+ .primary .is-rolling {
47+ pointer-events : none;
48+ opacity : 0.78 ;
49+ }
3650@media (max-width : 700px ){
3751 .page {width : min (100% - 12px , 980px );padding-top : 24px }
3852 .row {grid-template-columns : minmax (105px , 130px ) minmax (0 , 1fr ) 38px 38px }
@@ -1386,7 +1400,65 @@ <h1>Titt's Questionable<br>Character Design Brief</h1>
13861400 }
13871401 return randomItem ( DATA [ key ] ) ;
13881402}
1403+ /* ============================================================
1404+ VISUAL SHUFFLE EFFECT
1405+ ============================================================ */
1406+
1407+ function shuffleField ( key , duration = 650 ) {
1408+ return new Promise ( ( resolve ) => {
1409+ const rowButton = document . querySelector (
1410+ `button.label[data-key="${ key } "]`
1411+ ) ;
1412+
1413+ const valueEl = rowButton
1414+ ? rowButton . parentElement . querySelector ( ".value" )
1415+ : null ;
1416+
1417+ if ( ! valueEl ) {
1418+ state [ key ] . value = generateField ( key ) ;
1419+ render ( ) ;
1420+ resolve ( ) ;
1421+ return ;
1422+ }
1423+
1424+ rowButton . classList . add ( "is-rolling" ) ;
1425+ valueEl . classList . add ( "shuffling" ) ;
1426+
1427+ const started = performance . now ( ) ;
1428+
1429+ const tick = ( ) => {
1430+
1431+ /* Show a temporary random result */
1432+ valueEl . textContent = generateField ( key ) ;
1433+
1434+ const elapsed = performance . now ( ) - started ;
1435+
1436+ /* Stop shuffling and choose the final result */
1437+ if ( elapsed >= duration ) {
1438+ state [ key ] . value = generateField ( key ) ;
13891439
1440+ valueEl . textContent = state [ key ] . value ;
1441+
1442+ valueEl . classList . remove ( "shuffling" ) ;
1443+ rowButton . classList . remove ( "is-rolling" ) ;
1444+
1445+ resolve ( ) ;
1446+ return ;
1447+ }
1448+
1449+ /*
1450+ * Starts quickly and gradually slows down,
1451+ * giving it the "landing on a result" effect.
1452+ */
1453+ const progress = elapsed / duration ;
1454+ const delay = 45 + ( progress * 110 ) ;
1455+
1456+ setTimeout ( tick , delay ) ;
1457+ } ;
1458+
1459+ tick ( ) ;
1460+ } ) ;
1461+ }
13901462function render ( ) {
13911463 const wrap = document . getElementById ( "generator" ) ;
13921464 wrap . innerHTML = "" ;
@@ -1402,19 +1474,30 @@ <h1>Titt's Questionable<br>Character Design Brief</h1>
14021474 }
14031475}
14041476
1405- function generateAll ( ) {
1477+ async function generateAll ( ) {
1478+ const button = document . getElementById ( "generateAll" ) ;
1479+
1480+ button . classList . add ( "is-rolling" ) ;
1481+
1482+ const jobs = [ ] ;
1483+
14061484 for ( const [ key ] of fields ) {
1407- if ( ! state [ key ] . locked ) state [ key ] . value = generateField ( key ) ;
1485+ if ( ! state [ key ] . locked ) {
1486+ jobs . push ( shuffleField ( key , 700 ) ) ;
1487+ }
14081488 }
1409- render ( ) ;
1489+
1490+ await Promise . all ( jobs ) ;
1491+
1492+ button . classList . remove ( "is-rolling" ) ;
14101493}
14111494
14121495document . getElementById ( "generator" ) . addEventListener ( "click" , e => {
14131496 const btn = e . target . closest ( "button[data-key]" ) ;
14141497 if ( ! btn ) return ;
14151498 const key = btn . dataset . key ;
14161499 const action = btn . dataset . action ;
1417- if ( action === "roll" ) state [ key ] . value = generateField ( key ) ;
1500+ if ( action === "roll" ) { shuffleField ( key , 700 ) ; return ; }
14181501 if ( action === "lock" ) state [ key ] . locked = ! state [ key ] . locked ;
14191502 if ( action === "clear" ) state [ key ] . value = "" ;
14201503 render ( ) ;
0 commit comments