From 0f8383f0a3f0daf19ac51e6c2d57b6f5108662c8 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Jun 2025 18:06:18 -0400 Subject: [PATCH] added and fixed chrome. updated animations and string --- colors.json | 7 + script.js | 100 ++++---- style.css | 670 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 685 insertions(+), 92 deletions(-) diff --git a/colors.json b/colors.json index 469f8d4..42813a6 100644 --- a/colors.json +++ b/colors.json @@ -135,6 +135,13 @@ "chromeType": "silver", "image": "images/chrome-silver.png" }, + { + "name": "Chrome Space Grey", + "hex": "#a8a9a4", + "metallic": true, + "chromeType": "spacegrey", + "image": "images/chrome-spacegrey.png" + }, { "name": "Chrome Rose Gold", "hex": "#FFBF00", diff --git a/script.js b/script.js index 8316369..f1dfc74 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,5 @@ let selectedPalette = []; +let animationsEnabled = true; fetch('colors.json') .then(response => response.json()) @@ -26,8 +27,6 @@ fetch('colors.json') if (color.image) { backgroundDiv.style.backgroundImage = `url(${color.image})`; - backgroundDiv.style.backgroundSize = 'cover'; - backgroundDiv.style.backgroundPosition = 'center'; } else { backgroundDiv.style.backgroundColor = color.hex; } @@ -42,7 +41,6 @@ fetch('colors.json') swatch.appendChild(backgroundDiv); - const shineImg = document.createElement('img'); shineImg.classList.add('color-shine'); shineImg.src = "shine.svg"; @@ -92,22 +90,28 @@ function renderSelectedPalette() { const swatchWrapper = document.createElement('div'); swatchWrapper.classList.add('swatch-wrapper'); + // --- Balloon and String Container --- + const floatGroup = document.createElement('div'); + floatGroup.classList.add('balloon-float-group'); + if (animationsEnabled) { + floatGroup.classList.add('balloon-float'); + floatGroup.style.animationDuration = `${(Math.random() * 3 + 3).toFixed(2)}s`; + floatGroup.style.animationDelay = `${(Math.random() * 2).toFixed(2)}s`; + } else { + floatGroup.classList.remove('balloon-float'); + floatGroup.style.animation = 'none'; + } + + // --- Balloon --- const swatch = document.createElement('div'); - swatch.classList.add('color-swatch', 'balloon-float'); + swatch.classList.add('color-swatch'); swatch.dataset.color = color.hex; - const duration = (Math.random() * 3 + 3).toFixed(2) + 's'; - const delay = (Math.random() * 2).toFixed(2) + 's'; - swatch.style.animationDuration = duration; - swatch.style.animationDelay = delay; - const backgroundDiv = document.createElement('div'); backgroundDiv.classList.add('color-background', 'chosen'); if (color.image) { backgroundDiv.style.backgroundImage = `url(${color.image})`; - backgroundDiv.style.backgroundSize = 'cover'; - backgroundDiv.style.backgroundPosition = 'center'; } else { backgroundDiv.style.backgroundColor = color.hex; } @@ -121,42 +125,47 @@ function renderSelectedPalette() { } swatch.appendChild(backgroundDiv); - const svgString = ` - - - - `; - if (animationsEnabled) { - swatch.classList.add('balloon-float'); - const duration = (Math.random() * 3 + 3).toFixed(2) + 's'; - const delay = (Math.random() * 2).toFixed(2) + 's'; - swatch.style.animationDuration = duration; - swatch.style.animationDelay = delay; - } else { - swatch.classList.remove('balloon-float'); - swatch.style.animation = 'none'; - } - - swatch.insertAdjacentHTML('beforeend', svgString); const shineImg = document.createElement('img'); shineImg.classList.add('color-shine'); shineImg.src = "shine.svg"; shineImg.alt = ""; swatch.appendChild(shineImg); + // --- SVG String --- + const svgNS = "http://www.w3.org/2000/svg"; + const stringSVG = document.createElementNS(svgNS, "svg"); + stringSVG.setAttribute("class", "balloon-string-svg"); + stringSVG.setAttribute("width", "20"); + stringSVG.setAttribute("height", "60"); + stringSVG.setAttribute("viewBox", "0 0 20 60"); + + const path = document.createElementNS(svgNS, "path"); + path.setAttribute("d", "M10 0 C8 10, 12 20, 10 30 C8 40, 12 50, 10 60"); + path.setAttribute("stroke", "#444"); + path.setAttribute("stroke-width", "2"); + path.setAttribute("fill", "none"); + + stringSVG.appendChild(path); + + // --- Assemble Group --- + floatGroup.appendChild(swatch); + floatGroup.appendChild(stringSVG); + swatchWrapper.appendChild(floatGroup); + + // --- Name --- + const colorName = document.createElement('span'); + colorName.classList.add('color-name', 'highlighted-name'); + colorName.textContent = color.name; + swatchWrapper.appendChild(colorName); + + // --- Click to remove --- swatch.addEventListener('click', () => { selectedPalette = selectedPalette.filter(c => c.hex !== color.hex); renderSelectedPalette(); updateSwatchHighlights(); }); - const colorName = document.createElement('span'); - colorName.classList.add('color-name', 'highlighted-name'); - colorName.textContent = color.name; - - swatchWrapper.appendChild(swatch); - swatchWrapper.appendChild(colorName); paletteColorsContainer.appendChild(swatchWrapper); }); } @@ -192,15 +201,11 @@ document.getElementById('clear-palette').addEventListener('click', () => { updateSwatchHighlights(); }); - - -// Toggle balloon animation globally document.getElementById('toggle-animation').addEventListener('click', () => { animationsEnabled = !animationsEnabled; - renderSelectedPalette(); // re-render with or without animation + renderSelectedPalette(); }); -// Shuffle selected palette document.getElementById('shuffle-palette').addEventListener('click', () => { selectedPalette = shuffleArray(selectedPalette); renderSelectedPalette(); @@ -208,26 +213,9 @@ document.getElementById('shuffle-palette').addEventListener('click', () => { }); function shuffleArray(array) { - // Fisher–Yates Shuffle for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } - - -let animationsEnabled = true; - -// Handle toggle switch -document.getElementById('toggle-animation').addEventListener('change', (e) => { - animationsEnabled = e.target.checked; - renderSelectedPalette(); -}); - -// Shuffle button -document.getElementById('shuffle-palette').addEventListener('click', () => { - selectedPalette = shuffleArray(selectedPalette); - renderSelectedPalette(); - updateSwatchHighlights(); -}); diff --git a/style.css b/style.css index 4b1a4ea..d1784ff 100644 --- a/style.css +++ b/style.css @@ -151,7 +151,7 @@ background-image: url("https://www.transparenttextures.com/patterns/asfalt-light justify-content: space-around; /* width: 60px; */ margin: 0 5px; - padding: 5px; + padding: 10px; max-width: min-content; /* overflow-wrap: break-word; */ @@ -204,36 +204,59 @@ background-image: url("https://www.transparenttextures.com/patterns/asfalt-light box-shadow: 0 0 8px 2px rgba(255, 255, 255, 0.2); } */ - .color-swatch { - width: 55px; - height: 65px; - border-radius: 50%; + .color-swatch { + width: 100px; + height: 136px; position: relative; - cursor: pointer; - background: linear-gradient( - 145deg, - #bbb, /* dark shadow */ - #eee 30%, /* bright highlight */ - #ddd 50%, /* mid-tone */ - #fff 70%, /* brightest highlight */ - #bbb /* dark shadow */ - ); - box-shadow: - inset 2px 2px 6px rgba(255, 255, 255, 0.7), /* inner glossy highlight */ - 0 4px 8px rgba(0, 0, 0, 0.2); /* subtle drop shadow */ + background: #ccc; /* fallback */ transition: transform 0.3s ease; + + background: radial-gradient(circle at 30% 30%, #fff5, transparent 60%), #ddd; + + -webkit-mask-image: url('images/balloon-mask.svg'); + -webkit-mask-size: contain; + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + + mask-image: url('images/balloon-mask.svg'); + mask-size: contain; + mask-repeat: no-repeat; + mask-position: center; + + box-shadow: + inset 2px 2px 6px rgba(255, 255, 255, 0.6), + 0 4px 8px rgba(0, 0, 0, 0.2); } + + + .color-swatch::after { + content: ''; + position: absolute; + bottom: -6px; + left: 50%; + transform: translateX(-50%); + width: 10px; + height: 6px; + background: inherit; + border-radius: 50% 50% 30% 30%; + box-shadow: inset 0 0 2px rgba(0,0,0,0.2); + } + + .color-swatch:hover, .color-swatch:active { - transform: scale(1.1); + transform: scale(1.2); box-shadow: 0 0 5px rgba(0,0,0,0.2); } .color-swatch.chosen { outline: 3px solid #333; outline-offset: 1px; + width: 90px; + height: 126px; + z-index: 10; } .color-background { @@ -245,30 +268,576 @@ background-image: url("https://www.transparenttextures.com/patterns/asfalt-light } .color-shine { position: absolute; - top: 10%; + top: 30%; left: 40%; width: 20px; - height: 8px; + height: auto; + max-width: 20px; opacity: 0.5; z-index: 10; + pointer-events: none; + transform: translate(-50%, -50%); + } .color-name { - font-size: 0.8rem; + font-size: 0.85rem; text-align: center; margin-top: 5px; word-break: keep-all; /* Prevent breaking long words */ /* white-space: nowrap; Keep names on a single line */ /* overflow: hidden; /*Hide overflow text if necessary */ text-overflow: ellipsis; /* Add ... when text is too long */ - max-width: 90%; + max-width: 100%; background-color: #e8e8e8; z-index: 1; } #palette-colors .color-swatch { - width: 40px; - height: 48px; + body { + margin: 0; + padding: 10px; + box-sizing: border-box; + /* font-family: sans-serif; */ + font-family: "Autour One", serif; + background-color: #e8e8e8; + background-image: url("https://www.transparenttextures.com/patterns/asfalt-dark.png"); + /* This is mostly intended for prototyping; please download the pattern and re-host for production environments. Thank you! */ + } + #main-header { + text-align: center; + padding: 20px 10px; + background: #ffffffdd; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + border-radius: 12px; + margin-bottom: 20px; + } + + #main-header h1 { + font-size: 2rem; + margin-bottom: 5px; + } + + #main-header p { + font-size: 1rem; + color: #666; + } + + #selected-palette { + position: sticky; + top: 10px; + background: #fff; + z-index: 100; + padding: 10px; + border: 2px solid #ccc; + border-radius: 10px; + transition: box-shadow 0.3s ease; + max-width: 80%; + margin: auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + + gap: 15px; + background-color: #e8e8e8; + background-image: url("https://www.transparenttextures.com/patterns/asfalt-light.png"); + /* This is mostly intended for prototyping; please download the pattern and re-host for production environments. Thank you! */ + } + + /* Strong shadow when stuck */ + #selected-palette.stuck { + box-shadow: 16px 16px 15px rgba(0, 0, 0, 0.25); + border: 1px solid rgb(0, 0, 0); + border-radius: 7px; + } + + + + #palette-colors { + display: flex; + flex-wrap: wrap; + justify-content: center; + overflow: visible; + padding: 10px; + /* transition: padding 0.3s ease; */ + transition: max-height 0.3s ease, padding 0.3s ease; + } + + + #clear-palette { + margin-top: 20px; + padding: 8px 12px; + font-size: 0.9rem; + background: #8ae7db; + border: 1px solid #ccc; + border-radius: 15px; + cursor: pointer; + margin: auto; + + } + + #color-families { + display: flex; + flex-direction: column; + gap: 15px; + + } + + @keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + footer { + text-align: center; + font-size: 0.9rem; + color: #777; + padding: 20px; + } + + @media (max-width: 480px) { + .color-name { + font-size: 0.7rem; + } + + .swatch-wrapper { + padding: 6px; + } + + #main-header h1 { + font-size: 1.5rem; + } + } + .color-family { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding-bottom: 20px; + border-bottom: 1px solid #ccc; + animation: fadeInUp 0.5s ease; + + } + .swatch-container { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 10px 20px; /* add horizontal AND vertical gaps */ + } + + .color-family h3 { + text-align: center; + margin: 10px 0 5px; + font-size: 1rem; + font-weight: bold; + } + + + .swatch-wrapper { + display: inline-flex; + flex-direction: column; + align-items: center; + justify-content: space-around; + /* width: 60px; */ + margin: 0 5px; + padding: 10px; + max-width: min-content; + /* overflow-wrap: break-word; */ + + + } + + /* .color-swatch { + width: 55px; + height: 65px; + border-radius: 50%; + position: relative; + cursor: pointer; + transition: transform 0.2s ease; + } */ + .color-swatch::before { + content: ''; + position: absolute; + top: 10%; + left: 20%; + width: 60%; + height: 40%; + border-radius: 50% / 30%; + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.8), + rgba(255, 255, 255, 0) 70% + ); + pointer-events: none; + filter: blur(4px); + } + + .metallic-element[data-color="#FFD700"] { + background: linear-gradient(to bottom, #B88606, #79550E); + box-shadow: + inset 0 0 0 0 rgba(255, 255, 255, 0.3), + inset 0 0 0 1px rgba(0, 0, 0, 0.3), + inset 0 0 10px rgba(0, 0, 0, 0.2); + } + + /* .color-swatch::after { + content: ''; + position: absolute; + top: 2%; + left: 2%; + width: 96%; + height: 96%; + border-radius: 50%; + border: 1px solid rgba(255, 255, 255, 0.3); + pointer-events: none; + box-shadow: 0 0 8px 2px rgba(255, 255, 255, 0.2); + } + */ + .color-swatch { + width: 100px; + height: 136px; + position: relative; + background: #ccc; /* fallback */ + transition: transform 0.3s ease; + + background: radial-gradient(circle at 30% 30%, #fff5, transparent 60%), #ddd; + + -webkit-mask-image: url('images/balloon-mask.svg'); + -webkit-mask-size: contain; + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + + mask-image: url('images/balloon-mask.svg'); + mask-size: contain; + mask-repeat: no-repeat; + mask-position: center; + + box-shadow: + inset 2px 2px 6px rgba(255, 255, 255, 0.6), + 0 4px 8px rgba(0, 0, 0, 0.2); + } + + + + .color-swatch::after { + content: ''; + position: absolute; + bottom: -6px; + left: 50%; + transform: translateX(-50%); + width: 10px; + height: 6px; + background: inherit; + border-radius: 50% 50% 30% 30%; + box-shadow: inset 0 0 2px rgba(0,0,0,0.2); + } + + + + .color-swatch:hover, + .color-swatch:active { + transform: scale(1.2); + box-shadow: 0 0 5px rgba(0,0,0,0.2); + } + + .color-swatch.chosen { + outline: 3px solid #333; + outline-offset: 1px; + width: 100px; + height: 136px; + z-index: 10; + } + + .color-background { + width: 100%; + height: 100%; + border-radius: 50%; + border: 2px solid rgba(0, 0, 0, 0.1); /* Light gray border */ + box-shadow: 1 1 1; + } + .color-shine { + position: absolute; + top: 30%; + left: 40%; + width: 20px; + height: auto; + max-width: 20px; + opacity: 0.5; + z-index: 10; + pointer-events: none; + transform: translate(-50%, -50%); + + } + + .color-name { + font-size: 0.85rem; + text-align: center; + margin-top: 5px; + word-break: keep-all; /* Prevent breaking long words */ + /* white-space: nowrap; Keep names on a single line */ + /* overflow: hidden; /*Hide overflow text if necessary */ + text-overflow: ellipsis; /* Add ... when text is too long */ + max-width: 100%; + background-color: #e8e8e8; + z-index: 1; + } + + #palette-colors .color-swatch { + width: 100px; + height: 136px; + } + + /* .checkmark-overlay { + position: absolute; + top: 4px; + right: 4px; + font-size: 16px; + font-weight: bold; + color: white; + background: rgba(0, 0, 0, 0.6); + border-radius: 50%; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + z-index: 20; + pointer-events: none; + } */ + + @media (max-width: 600px) { + + .swatch-wrapper { + width: 50px; + } + + .color-name { + font-size: 0.65rem; + max-width: 40px; + word-wrap: break-word; + } + + #selected-palette h2 { + font-size: 1.1rem; + } + } + + .color-name { + transition: all 0.3s ease; + } + + .color-name.highlighted-name { + text-decoration:underline; + font-weight: bold; + color: #111; + } + + @keyframes pop { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } + } + + .color-background.pop { + animation: pop 0.25s ease; + } + + .color-background.chosen { + transform: scale(2.2); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.25); + } + .chosen{ + z-index: 4; + } + + /* Shared metallic style */ + .metallic { + position: relative; + border: 1px solid rgba(255, 255, 255, 0.3); + /* box-shadow: inset 1px 1px 4px rgba(255, 255, 255, 0.6), + 0 2px 4px rgba(0, 0, 0, 0.3); */ + overflow: hidden; + } + + /* Highlight overlay */ + .metallic1::after { + content: ''; + position: absolute; + top: 15%; + left: 15%; + width: 70%; + height: 70%; + background: radial-gradient(circle at top left, rgba(255,255,255,0.3), transparent 70%); + transform: rotate(-20deg); + pointer-events: none; + border-radius: 50%; + } + + /* Specific chrome variants */ + .chrome-gold { + background: linear-gradient(145deg, #fefcea, #b69978, #a18b67, #806748); + } + + .chrome-silver { + background: linear-gradient(145deg, #e0e0e0, #a9a9a9, #808080, #e0e0e0); + } + + .chrome-rosegold { + background: linear-gradient(145deg, #fbe3dc, #e6b7a9, #d19387, #fbe3dc); + } + + .chrome-champagne { + background: linear-gradient(145deg, #fff2cc, #f2e6b6, #d9c08e, #fff2cc); + } + + .chrome-blue { + background: linear-gradient(145deg, #d0f0ff, #4d7995, #2d576f, #d0f0ff); + } + + .chrome-purple { + background: linear-gradient(145deg, #e0ccff, #b08be1, #915bc4, #e0ccff); + } + + .chrome-green { + background: linear-gradient(145deg, #e2ffe2, #457066, #5c877d, #e2ffe2); + } + + + @keyframes balloonFloat { + 0%, 100% { transform: translateY(0) rotate(-4deg); } + 50% { transform: translateY(-25px) rotate(4deg); } + } + + .balloon-float { + animation-name: balloonFloat; + animation-timing-function: 4s ease-in-out; + animation-iteration-count: infinite; + transform-origin: bottom center; + position: relative; + } + + .color-background { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + } + /* --- SVG Balloon String --- */ + .balloon-string-svg { + position: absolute; + top: 55px; /* match .color-swatch height */ + left: 50%; + transform: translateX(-50%); + width: 20px; + height: 60px; + z-index: 0; + pointer-events: none; + overflow: visible; + transform-origin: top center; + animation: svgWiggle 2s ease-in-out infinite; + } + + + .balloon-float-group { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + animation-name: balloonFloat; + animation-duration: 4s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + transform-origin: bottom center; + } + + .wiggle-path { + stroke: #727171; + stroke-width: 2; + fill: none; + stroke-linecap: round; + stroke-dasharray: 1, 2; + } + + @keyframes svgWiggle { + 0%, 100% { + transform: translateX(-50%) rotate(0deg); + } + 25% { + transform: translateX(-50%) rotate(2deg); + } + 50% { + transform: translateX(-50%) rotate(-2.5deg); + } + 75% { + transform: translateX(-50%) rotate(1.5deg); + } + } + + #palette-controls { + position: absolute; + top: 10px; + right: 10px; + display: flex; + align-items: center; + gap: 10px; + z-index: 200; + } + + /* Toggle Switch Styling */ + .switch { + position: relative; + display: inline-block; + width: 40px; + height: 20px; + } + .switch input { + opacity: 0; + width: 0; + height: 0; + } + .slider { + position: absolute; + cursor: pointer; + top: 0; left: 0; + right: 0; bottom: 0; + background-color: #ccc; + transition: 0.4s; + border-radius: 20px; + } + .slider:before { + position: absolute; + content: ""; + height: 14px; + width: 14px; + left: 3px; + bottom: 3px; + background-color: white; + transition: 0.4s; + border-radius: 50%; + } + input:checked + .slider { + background-color: #4caf50; + } + input:checked + .slider:before { + transform: translateX(20px); + } + + /* Shuffle Button */ + #shuffle-palette { + font-size: 16px; + padding: 4px 8px; + border: none; + background: #f0f0f0; + border-radius: 6px; + cursor: pointer; + } + */ } @@ -298,6 +867,8 @@ background-image: url("https://www.transparenttextures.com/patterns/asfalt-light .color-name { font-size: 0.65rem; + max-width: 40px; + word-wrap: break-word; } #selected-palette h2 { @@ -326,9 +897,12 @@ background-image: url("https://www.transparenttextures.com/patterns/asfalt-light } .color-background.chosen { - /* transform: scale(1.1); */ + transform: scale(2.2); box-shadow: 0 0 10px rgba(0, 0, 0, 0.25); } + .chosen{ + z-index: 4; + } /* Shared metallic style */ .metallic { @@ -401,32 +975,56 @@ background-image: url("https://www.transparenttextures.com/patterns/asfalt-light background-position: center; background-repeat: no-repeat; } - - .balloon-string { - width: 6px; - height: 45px; + /* --- SVG Balloon String --- */ + .balloon-string-svg { position: absolute; - bottom: -45px; + top: 109px; /* match .color-swatch height */ left: 50%; transform: translateX(-50%); - animation: stringWiggle 2s ease-in-out infinite; + width: 20px; + height: 60px; + z-index: -4; + pointer-events: none; + overflow: visible; + transform-origin: top center; + animation: svgWiggle 2s ease-in-out infinite; } - @keyframes stringWiggle { + + .balloon-float-group { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + animation-name: balloonFloat; + animation-duration: 4s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + transform-origin: bottom center; + } + + .wiggle-path { + stroke: #727171; + stroke-width: 2; + fill: none; + stroke-linecap: round; + stroke-dasharray: 1, 2; + } + + @keyframes svgWiggle { 0%, 100% { transform: translateX(-50%) rotate(0deg); } 25% { - transform: translateX(-50%) rotate(1.5deg); + transform: translateX(-50%) rotate(2deg); } 50% { - transform: translateX(-50%) rotate(-1.5deg); + transform: translateX(-50%) rotate(-2.5deg); } 75% { - transform: translateX(-50%) rotate(1deg); + transform: translateX(-50%) rotate(1.5deg); } } - #palette-controls { position: absolute;