|
1 | | -// Project Registry |
| 1 | +// Project Registry |
2 | 2 | // Each project's HTML and logic lives in its own file under js/projects/ |
3 | 3 |
|
4 | 4 |
|
@@ -339,229 +339,7 @@ function initDiceRolling() { |
339 | 339 | // ============================================ |
340 | 340 | // COIN FLIP |
341 | 341 | // ============================================ |
342 | | -function getCoinFlipHTML() { |
343 | | - return ` |
344 | | - <div class="project-content"> |
345 | | - <h2>🪙 Coin Flip</h2> |
346 | | - <div class="coin-container"> |
347 | | - <div class="coin-scene"> |
348 | | - <div class="coin" id="coin"> |
349 | | - <div class="coin-face heads">👑</div> |
350 | | - <div class="coin-edge"></div> |
351 | | - <div class="coin-face tails">🦅</div> |
352 | | - </div> |
353 | | - <div class="coin-shadow"></div> |
354 | | - </div> |
355 | | - |
356 | | - <div class="coin-result" id="coinResult">Click to Flip!</div> |
357 | | - |
358 | | - <button class="btn-flip" id="flipCoin">Flip Coin</button> |
359 | | - |
360 | | - <div class="coin-stats"> |
361 | | - <div class="stat-item"> |
362 | | - <span>Heads:</span> |
363 | | - <span id="headsCount">0</span> |
364 | | - </div> |
365 | | - <div class="stat-item"> |
366 | | - <span>Tails:</span> |
367 | | - <span id="tailsCount">0</span> |
368 | | - </div> |
369 | | - </div> |
370 | | - </div> |
371 | | - </div> |
372 | | - |
373 | | - <style> |
374 | | - .coin-container { |
375 | | - text-align: center; |
376 | | - padding: 3rem 2rem; |
377 | | - } |
378 | | -
|
379 | | - .coin-scene { |
380 | | - position: relative; |
381 | | - width: 170px; |
382 | | - height: 205px; |
383 | | - perspective: 900px; |
384 | | - margin: 1rem auto 2rem; |
385 | | - transform-origin: center bottom; |
386 | | - } |
387 | | - |
388 | | - .coin { |
389 | | - --flip-x: 0deg; |
390 | | - --flip-y: 0deg; |
391 | | - width: 170px; |
392 | | - height: 170px; |
393 | | - position: relative; |
394 | | - transform-style: preserve-3d; |
395 | | - transform: rotateX(var(--flip-x)) rotateY(var(--flip-y)); |
396 | | - transition: transform 1.6s cubic-bezier(0.2, 0.8, 0.15, 1); |
397 | | - } |
398 | | - |
399 | | - .coin-face { |
400 | | - position: absolute; |
401 | | - width: 100%; |
402 | | - height: 100%; |
403 | | - backface-visibility: hidden; |
404 | | - display: flex; |
405 | | - align-items: center; |
406 | | - justify-content: center; |
407 | | - font-size: 6rem; |
408 | | - background: radial-gradient(circle at 35% 30%, #fff5a6, #ffd54f 45%, #e1a600 100%); |
409 | | - border-radius: 50%; |
410 | | - border: 5px solid #d79e00; |
411 | | - box-shadow: inset -6px -8px 10px rgba(0, 0, 0, 0.22), inset 6px 8px 12px rgba(255, 255, 255, 0.45), 0 12px 25px rgba(0, 0, 0, 0.35); |
412 | | - } |
413 | | -
|
414 | | - .heads { transform: translateZ(8px); } |
415 | | - |
416 | | - .tails { |
417 | | - transform: rotateY(180deg) translateZ(8px); |
418 | | - } |
419 | | -
|
420 | | - .coin-edge { |
421 | | - position: absolute; |
422 | | - width: 100%; |
423 | | - height: 100%; |
424 | | - border-radius: 50%; |
425 | | - border: 8px solid #b8860b; |
426 | | - transform: translateZ(0); |
427 | | - box-shadow: inset 0 0 0 2px rgba(255, 232, 145, 0.35); |
428 | | - } |
429 | | -
|
430 | | - .coin-shadow { |
431 | | - position: absolute; |
432 | | - left: 50%; |
433 | | - bottom: 6px; |
434 | | - width: 110px; |
435 | | - height: 16px; |
436 | | - transform: translateX(-50%); |
437 | | - border-radius: 50%; |
438 | | - background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0.08) 72%, transparent 100%); |
439 | | - } |
440 | | -
|
441 | | - .coin-scene.rolling .coin-shadow { |
442 | | - animation: coinShadowFloat 1.6s ease; |
443 | | - } |
444 | | -
|
445 | | - .coin-scene.impact { |
446 | | - animation: coinLanding 0.45s ease-out; |
447 | | - } |
448 | | -
|
449 | | - .coin-scene.impact .coin-shadow { |
450 | | - animation: coinShadowImpact 0.45s ease-out; |
451 | | - } |
452 | | - |
453 | | - .coin-result { |
454 | | - font-size: 2rem; |
455 | | - font-weight: bold; |
456 | | - margin: 2rem 0; |
457 | | - min-height: 3rem; |
458 | | - } |
459 | | - |
460 | | - .btn-flip { |
461 | | - background: linear-gradient(135deg, #ffd700, #ffed4e); |
462 | | - color: #333; |
463 | | - border: none; |
464 | | - padding: 1rem 3rem; |
465 | | - border-radius: 50px; |
466 | | - font-size: 1.3rem; |
467 | | - font-weight: bold; |
468 | | - cursor: pointer; |
469 | | - transition: var(--transition); |
470 | | - } |
471 | | - |
472 | | - .btn-flip:hover { |
473 | | - transform: scale(1.05); |
474 | | - box-shadow: 0 5px 20px rgba(255, 215, 0, 0.4); |
475 | | - } |
476 | | - |
477 | | - .coin-stats { |
478 | | - display: flex; |
479 | | - gap: 3rem; |
480 | | - justify-content: center; |
481 | | - margin-top: 2rem; |
482 | | - font-size: 1.2rem; |
483 | | - } |
484 | | - |
485 | | - .stat-item { |
486 | | - display: flex; |
487 | | - gap: 1rem; |
488 | | - align-items: center; |
489 | | - } |
490 | | -
|
491 | | - @keyframes coinLanding { |
492 | | - 0% { transform: translateY(-12px); } |
493 | | - 55% { transform: translateY(5px); } |
494 | | - 82% { transform: translateY(-2px); } |
495 | | - 100% { transform: translateY(0); } |
496 | | - } |
497 | | -
|
498 | | - @keyframes coinShadowFloat { |
499 | | - 0%, 100% { transform: translateX(-50%) scale(1); opacity: 0.4; } |
500 | | - 45% { transform: translateX(-50%) scale(0.68); opacity: 0.2; } |
501 | | - } |
502 | | -
|
503 | | - @keyframes coinShadowImpact { |
504 | | - 0% { transform: translateX(-50%) scale(0.72); opacity: 0.2; } |
505 | | - 55% { transform: translateX(-50%) scale(1.12); opacity: 0.44; } |
506 | | - 100% { transform: translateX(-50%) scale(1); opacity: 0.4; } |
507 | | - } |
508 | | - </style> |
509 | | - `; |
510 | | -} |
511 | | - |
512 | | -function initCoinFlip() { |
513 | | - const coin = document.getElementById('coin'); |
514 | | - const coinScene = coin.closest('.coin-scene'); |
515 | | - const flipBtn = document.getElementById('flipCoin'); |
516 | | - const result = document.getElementById('coinResult'); |
517 | | - const headsCountEl = document.getElementById('headsCount'); |
518 | | - const tailsCountEl = document.getElementById('tailsCount'); |
519 | | - let headsCount = 0; |
520 | | - let tailsCount = 0; |
521 | | - let spinCount = 0; |
522 | | - |
523 | | - function setCoinFace(isHeads, seed) { |
524 | | - const targetY = isHeads ? 0 : 180; |
525 | | - const flipX = 360 * (4 + (seed % 3)); |
526 | | - const flipY = 360 * (3 + (seed % 2)) + targetY; |
527 | | - coin.style.setProperty('--flip-x', `${flipX}deg`); |
528 | | - coin.style.setProperty('--flip-y', `${flipY}deg`); |
529 | | - } |
530 | | - |
531 | | - function triggerCoinLanding() { |
532 | | - coinScene.classList.remove('impact'); |
533 | | - void coinScene.offsetWidth; |
534 | | - coinScene.classList.add('impact'); |
535 | | - setTimeout(() => { |
536 | | - coinScene.classList.remove('impact'); |
537 | | - }, 460); |
538 | | - } |
539 | | - |
540 | | - flipBtn.addEventListener('click', () => { |
541 | | - flipBtn.disabled = true; |
542 | | - result.textContent = 'Flipping...'; |
543 | | - coinScene.classList.add('rolling'); |
544 | | - |
545 | | - const isHeads = Math.random() < 0.5; |
546 | | - spinCount += 1; |
547 | | - setCoinFace(isHeads, spinCount); |
548 | | - |
549 | | - setTimeout(() => { |
550 | | - coinScene.classList.remove('rolling'); |
551 | | - triggerCoinLanding(); |
552 | | - if (isHeads) { |
553 | | - result.textContent = '👑 Heads!'; |
554 | | - headsCount++; |
555 | | - headsCountEl.textContent = headsCount; |
556 | | - } else { |
557 | | - result.textContent = '🦅 Tails!'; |
558 | | - tailsCount++; |
559 | | - tailsCountEl.textContent = tailsCount; |
560 | | - } |
561 | | - flipBtn.disabled = false; |
562 | | - }, 1600); |
563 | | - }); |
564 | | -} |
| 342 | +// (Coin Flip HTML & Logic loaded from modular file js/projects/coin-flip.js) |
565 | 343 |
|
566 | 344 | // Continue with more projects in next message... |
567 | 345 | // Additional Project Implementations |
|
0 commit comments