From f835143ca4b74e24ca6104c283d8987602e73783 Mon Sep 17 00:00:00 2001 From: nisamuhmed Date: Wed, 20 Mar 2024 23:10:27 -0800 Subject: [PATCH] Deploy --- .github/workflows/main.yaml | 23 +++ index.html | 69 +++++++++ main.js | 125 ++++++++++++++++ style.css | 219 +++++++++++++++++++++++++++ style.scss | 289 ++++++++++++++++++++++++++++++++++++ 5 files changed, 725 insertions(+) create mode 100644 .github/workflows/main.yaml create mode 100644 index.html create mode 100644 main.js create mode 100644 style.css create mode 100644 style.scss diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..3ce96c8 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,23 @@ +name: Deployment + +on: + push: + branches: + - main + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-2 + + - name: Deploy to S3 bucket + run: aws s3 sync . s3://neon-clock --delete diff --git a/index.html b/index.html new file mode 100644 index 0000000..1d57491 --- /dev/null +++ b/index.html @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + +
+ + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..9ee97a0 --- /dev/null +++ b/main.js @@ -0,0 +1,125 @@ +const bars = [ + ['end', 'top'], + ['side', 'top', 'left'], + ['side', 'top', 'right'], + ['middle'], + ['side', 'bottom', 'left'], + ['side', 'bottom', 'right'], + ['end', 'bottom'] +]; + +const $main = document.querySelector('main'); + +const addDigits = number => { + const initGroup = (number, padding = 2) => { + const $group = document.createElement('div'); + $group.classList.add('group'); + + const digits = [...`${number}`.padStart(padding, 0)].map(digit => { + const $digit = document.createElement('figure'); + $digit.classList.add('digit'); + $digit.setAttribute('data-digit', digit); + bars.forEach(classes => { + const $span = document.createElement('span'); + $span.classList.add(...classes); + $digit.append($span); + }); + return $digit; + }); + + $group.append(...digits); + + return { + element: $group, + set number(val) { + number = val; + [...`${number}`.padStart(padding, 0).substring(`${number}`.length - 2)].forEach((digit, i) => { + digits[i].setAttribute('data-digit', digit); + }); + }, + + get number() { + return number; + } + } + } + + const $digits = document.createElement('div'); + $digits.classList.add('digits'); + const group = initGroup(number); + const groupShadow1 = initGroup(number); + const groupShadow2 = initGroup(number); + groupShadow1.element.classList.add('shadow'); + groupShadow1.element.classList.add('shadow1'); + groupShadow2.element.classList.add('shadow'); + groupShadow2.element.classList.add('shadow2'); + $digits.append(group.element); + $digits.append(groupShadow1.element); + $digits.append(groupShadow2.element); + $main.append($digits); + + return { + set number(val) { + number = val; + group.number = val; + groupShadow1.number = val; + groupShadow2.number = val; + }, + get number() { + return number; + } + } +} + +const addColon = () => { + const $colonGroup = document.createElement('div'); + $colonGroup.classList.add('colon-group'); + const $colon = document.createElement('figure'); + $colon.append(document.createElement('span')); + const $colonShadow1 = document.createElement('figure'); + $colonShadow1.append(document.createElement('span')); + const $colonShadow2 = document.createElement('figure'); + $colonShadow2.append(document.createElement('span')); + $colon.classList.add('colon'); + $colonShadow1.classList.add('colon', 'shadow', 'shadow1'); + $colonShadow2.classList.add('colon', 'shadow', 'shadow2'); + $colonGroup.append($colon); + $colonGroup.append($colonShadow1); + $colonGroup.append($colonShadow2); + $main.append($colonGroup); +} + +const init = () => { + let now = new Date(); + let hours = now.getHours(); + let minutes = now.getMinutes(); + let seconds = now.getSeconds(); + + const numberHour = addDigits(hours); + addColon(); + const numberMinute = addDigits(minutes); + addColon(); + const numberSecond = addDigits(seconds); + + const update = () => { + now = new Date(); + let newSeconds = now.getSeconds(); + if (seconds !== newSeconds) { + hours = now.getHours(); + minutes = now.getMinutes(); + seconds = newSeconds; + numberHour.number = hours; + numberMinute.number = minutes; + numberSecond.number = seconds; + } + + requestAnimationFrame(update); + } + update(); +} + +if (/^(?:(?!chrome|android)[\s\S])*(?:safari|iPad|iPhone|iPod)/i.test(navigator.userAgent)) { + document.body.classList.add('safari'); +} + +init(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..b4c3a0b --- /dev/null +++ b/style.css @@ -0,0 +1,219 @@ +/* author: https://codepen.io/wheatup/pen/JjzdMbK +wheatup + */ +html { + font-size: 0.75vmin; +} + +body { + background-image: linear-gradient(to bottom, #131313, #000); + perspective: 45rem; + transform-style: preserve-3d; +} + +main { + --color: #1fa3ef; + display: flex; + gap: 1rem; + width: 100%; + height: 100%; + position: relative; + align-items: center; + justify-content: center; + animation-composition: add; + animation: camera-rotate 40s ease-in-out forwards infinite, camera-pan 30s linear forwards infinite; + transform: rotateY(var(--angle)); + translate: 0rem 1rem 10rem; + transform-style: preserve-3d; +} +main .digits { + transform-style: preserve-3d; +} +main .digits .group { + display: flex; + gap: 2rem; +} +main .digits .group .digit { + position: relative; + height: 16rem; + aspect-ratio: 1/2; + filter: drop-shadow(0px 0px 4px var(--color)) drop-shadow(0px 0px 10px var(--color)); +} +main .digits .group .digit span { + --act: 0; + --signX: 1; + --signY: 1; + position: absolute; + background-color: white; + transition: all 0.3s cubic-bezier(0.17, 0.67, 0.5, 1.15); + opacity: calc(0.03 + 0.97 * var(--act)); + animation-composition: add; + scale: var(--signX) var(--signY); +} +main .digits .group .digit span.end { + clip-path: polygon(15% 0%, 7.5% 20%, 25% 100%, 75% 100%, 92.5% 20%, 85% 0%); + width: 100%; + height: 10%; +} +main .digits .group .digit span.end.top { + top: 0; +} +main .digits .group .digit span.end.bottom { + top: initial; + bottom: 0; + --signY: -1; +} +main .digits .group .digit span.side { + clip-path: polygon(0% 15%, 20% 7.5%, 100% 22.5%, 100% 85%, 20% 95%, 0% 90%); + height: 50%; + width: 20%; +} +main .digits .group .digit span.side.left { + top: 0; + left: 0; +} +main .digits .group .digit span.side.left.bottom { + top: initial; + bottom: 0; + --signY: -1; +} +main .digits .group .digit span.side.right { + top: 0; + left: initial; + right: 0; + --signX: -1; +} +main .digits .group .digit span.side.right.bottom { + top: initial; + --signY: -1; + bottom: 0; +} +main .digits .group .digit span.middle { + clip-path: polygon(22.5% 0%, 6.5% 50%, 22.5% 100%, 77.5% 100%, 93.5% 50%, 77.5% 0%); + top: 45%; + height: 10%; + width: 100%; +} +main .digits .group .digit[data-digit="0"] :not(.middle) { + --act: 1; +} +main .digits .group .digit[data-digit="1"] .right { + --act: 1; +} +main .digits .group .digit[data-digit="2"] :not(.top.left, .bottom.right) { + --act: 1; +} +main .digits .group .digit[data-digit="3"] :not(.left) { + --act: 1; +} +main .digits .group .digit[data-digit="4"] :not(.end, .bottom.left) { + --act: 1; +} +main .digits .group .digit[data-digit="5"] :not(.top.right, .bottom.left) { + --act: 1; +} +main .digits .group .digit[data-digit="6"] :not(.top.right) { + --act: 1; +} +main .digits .group .digit[data-digit="7"] .top, main .digits .group .digit[data-digit="7"] .right { + --act: 1; +} +main .digits .group .digit[data-digit="8"] > * { + --act: 1; +} +main .digits .group .digit[data-digit="9"] :not(.bottom.left) { + --act: 1; +} +main .colon-group { + transform-style: preserve-3d; +} +main .colon-group .colon span { + display: flex; + height: 16rem; + flex-direction: column; + justify-content: space-evenly; + width: 4rem; + align-items: center; + filter: drop-shadow(0px 0px 4px var(--color)) drop-shadow(0px 0px 10px var(--color)); +} +main .colon-group .colon span::before, main .colon-group .colon span::after { + content: ""; + display: block; + width: 2rem; + aspect-ratio: 1/1; + background-color: white; + border-radius: 2rem; +} +main .shadow { + top: 0; + position: absolute; + transform-origin: bottom center; + transform: translateY(1rem) translateZ(2rem) rotateX(-90.1deg); +} +main .shadow .digit span { + opacity: var(--act); +} +main .shadow.shadow1 { + opacity: 0.5; + filter: drop-shadow(0px 0px 4px var(--color)) drop-shadow(0px 0px 10px var(--color)) blur(4rem); +} +main .shadow.shadow1 > span, main .shadow.shadow1 .digit { + mask-image: linear-gradient(to bottom, white, rgba(0, 0, 0, 0.5)); +} +main .shadow.shadow2 { + opacity: 0.4; + filter: drop-shadow(0px 0px 4px var(--color)) drop-shadow(0px 0px 10px var(--color)) blur(4px); +} +main .shadow.shadow2 > span, main .shadow.shadow2 .digit { + opacity: var(--act); + mask-image: linear-gradient(to top, black, rgba(0, 0, 0, 0.1) 60%, rgba(0, 0, 0, 0)); +} + +@keyframes camera-rotate { + 0% { + transform: rotateY(-10deg); + } + 50% { + transform: rotateY(10deg); + } + 100% { + transform: rotateY(-10deg); + } +} +@keyframes camera-pan { + 0% { + transform: translate(0rem, 0rem); + } + 20% { + transform: translate(1rem, 2rem); + } + 40% { + transform: translate(-2rem, 2.5rem); + } + 50% { + transform: translate(-1.5rem, 1.5rem); + } + 70% { + transform: translate(-0.5rem, -0.5rem); + } + 90% { + transform: translate(1.5rem, -1rem); + } + 100% { + transform: translate(0rem, 0rem); + } +} +.safari .digit span { + transition: none !important; +} +.safari .digit span::before { + content: ""; + position: absolute; + display: block; + width: 400%; + height: 400%; + top: 0; + left: 0; + transform: translate(-50%, -50%); +} + diff --git a/style.scss b/style.scss new file mode 100644 index 0000000..f4d1ce9 --- /dev/null +++ b/style.scss @@ -0,0 +1,289 @@ +html { + font-size: 0.75vmin; +} + +body { + background-image: linear-gradient(to bottom, #131313, #000); + perspective: 45rem; + transform-style: preserve-3d; +} + +main { + --color: #1fa3ef; + display: flex; + gap: 1rem; + width: 100%; + height: 100%; + position: relative; + align-items: center; + justify-content: center; + animation-composition: add; + animation: camera-rotate 40s ease-in-out forwards infinite, + camera-pan 30s linear forwards infinite; + transform: rotateY(var(--angle)); + translate: 0rem 1rem 10rem; + transform-style: preserve-3d; + + .digits { + transform-style: preserve-3d; + .group { + display: flex; + gap: 2rem; + + .digit { + position: relative; + height: 16rem; + aspect-ratio: 1/2; + filter: drop-shadow(0px 0px 4px var(--color)) drop-shadow(0px 0px 10px var(--color)); + + span { + --act: 0; + --signX: 1; + --signY: 1; + position: absolute; + background-color: white; + transition: all .3s cubic-bezier(.17,.67,.5,1.15); + opacity: calc(0.03 + 0.97 * var(--act)); + animation-composition: add; + scale: var(--signX) var(--signY); + + &.end { + clip-path: polygon(15% 0%, 7.5% 20%, 25% 100%, 75% 100%, 92.5% 20%, 85% 0%); + width: 100%; + height: 10%; + + &.top { + top: 0; + } + + &.bottom { + top: initial; + bottom: 0; + --signY: -1; + } + } + + &.side { + clip-path: polygon(0% 15%, 20% 7.5%, 100% 22.5%, 100% 85%, 20% 95%, 0% 90%); + height: 50%; + width: 20%; + + &.left { + top: 0; + left: 0; + + &.bottom { + top: initial; + bottom: 0; + --signY: -1; + } + } + + &.right { + top: 0; + left: initial; + right: 0; + --signX: -1; + + &.bottom { + top: initial; + --signY: -1; + bottom: 0; + } + } + } + + &.middle { + clip-path: polygon(22.5% 0%, 6.5% 50%, 22.5% 100%, 77.5% 100%, 93.5% 50%, 77.5% 0%); + top: 45%; + height: 10%; + width: 100%; + } + } + + &[data-digit="0"] { + :not(.middle) { + --act: 1; + } + } + + &[data-digit="1"] { + .right { + --act: 1; + } + } + + &[data-digit="2"] { + :not(.top.left, .bottom.right) { + --act: 1; + } + } + + &[data-digit="3"] { + :not(.left) { + --act: 1; + } + } + + &[data-digit="4"] { + :not(.end, .bottom.left) { + --act: 1; + } + } + + &[data-digit="5"] { + :not(.top.right, .bottom.left) { + --act: 1; + } + } + + &[data-digit="6"] { + :not(.top.right) { + --act: 1; + } + } + + &[data-digit="7"] { + .top, .right { + --act: 1; + } + } + + &[data-digit="8"] { + > * { + --act: 1; + } + } + + &[data-digit="9"] { + :not(.bottom.left) { + --act: 1; + } + } + } + } + } + + .colon-group { + transform-style: preserve-3d; + .colon { + span { + display: flex; + height: 16rem; + flex-direction: column; + justify-content: space-evenly; + width: 4rem; + align-items: center; + filter: drop-shadow(0px 0px 4px var(--color)) drop-shadow(0px 0px 10px var(--color)); + + &::before, &::after { + content: ''; + display: block; + width: 2rem; + aspect-ratio: 1/1; + background-color: white; + border-radius: 2rem; + } + } + } + } + + .shadow { + top: 0; + position: absolute; + transform-origin: bottom center; + transform: translateY(1rem) translateZ(2rem) rotateX(-90.1deg); // 90deg won't be visible on safari + + .digit { + span { + opacity: var(--act); + } + } + + &.shadow1 { + opacity: 0.5; + filter: drop-shadow(0px 0px 4px var(--color)) + drop-shadow(0px 0px 10px var(--color)) + blur(4rem); + + > span, .digit { + mask-image: linear-gradient(to bottom, white, rgba(0,0,0,0.5)); + } + } + + &.shadow2 { + opacity: 0.4; + filter: drop-shadow(0px 0px 4px var(--color)) + drop-shadow(0px 0px 10px var(--color)) + blur(4px); + + > span, .digit { + opacity: var(--act); + mask-image: linear-gradient(to top, black, rgba(0,0,0,0.1) 60%, rgba(0,0,0,0)); + } + } + } +} + +@keyframes camera-rotate { + 0% { + transform: rotateY(-10deg); + } + + 50% { + transform: rotateY(10deg); + } + + 100% { + transform: rotateY(-10deg); + } +} + +@keyframes camera-pan { + 0% { + transform: translate(0rem, 0rem); + } + + 20% { + transform: translate(1rem, 2rem); + } + + 40% { + transform: translate(-2rem, 2.5rem); + } + + 50% { + transform: translate(-1.5rem, 1.5rem); + } + + 70% { + transform: translate(-0.5rem, -0.5rem); + } + + 90% { + transform: translate(1.5rem, -1rem); + } + + 100% { + transform: translate(0rem, 0rem); + } +} + +.safari { + .digit span { + // 3d transform with opacity animation breaks filters + transition: none !important; + + &::before { + content: ''; + position: absolute; + display: block; + // prevent the bounding box to be cut off during content change + width: 400%; + height: 400%; + top: 0; + left: 0; + transform: translate(-50%, -50%); + } + } +}