ハックツ生誕祭 ~ ハックちゅうからの招待状 ~

https://hackz-birthday.vercel.app/

Nuxt.js

TypeScript

Firebase

Firestore

株式会社ハックツの3年目をお祝いするためのアプリ

あいでん

推しアイデア

パーティーに参加してくれた人(クリアした人)を、さもパーティーに参加してるかのように表現したところ

作った背景

せっかくの記念日なので、みんなに楽しんでもらうため

推し技術

無駄にこだわったCSS Animation

プロジェクト詳細

▷ 実装内容紹介

せっかくなので本WEBアプリケーションの一部の実装方法をご紹介しようと思います。

▼ 現在のパーティー参加者数の取得

常時右下に表示している『現在のパーティー参加者』は FirebaseonSnapshotという技術を活用しています。

これを活用することでリアルタイム同期をすることができます。 (使用しているFirebaseのバージョンは 9.6.1 です)

import { defineComponent, ref } from "@nuxtjs/composition-api"; import { collection, onSnapshot, getFirestore } from "@firebase/firestore"; export default defineComponent({ setup() { const currentParticipants = ref<number>(0); // 本当の実装では、initializeApp()してるところでexportしてる const db = getFirestore(); // クリアしたユーザー数を取得 const unSub = onSnapshot(collection(db, "users"), (res) => { currentParticipants.value = res.docs.length; }); return { currentParticipants }; } });

▼ メールアニメーション

本WEBアプリケーションの初回訪問時に必ず表示されるメールのアイコンと、そのアニメーション📩

ライブラリ等は一切使わず、CSSのみで実装しています。 .vueファイルにそのまま貼り付けてもらえると、その挙動を確認できると思います!!!

isOpentrueになることで、moveクラスが付与され、メールが開くアニメーションが実装されます。 また、メールがバウンドするアニメーションは@keyframes poyopoyoで実装しています。

MailIcon.html
<button @click="toggleIsOpen" class="letter"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 215.6 140.41" class="letter__svg" > <defs> <style> .letter-cls-1 { fill: #a9a9a9; } .letter-cls-2 { fill: white; } .letter-cls-3 { fill: #6dbab4; } .letter-cls-4 { fill: #fabe00; } </style> </defs> <g> <g> <g> <path class="letter-cls-1" d="M204.93,7.66c-6.14,5-12.41,10.23-18.89,15.21-13.54,10.42-27.75,20.05-40.6,31.25-8.07,7.05-17.54,12-25.27,19.27-3.2,3-6.82,5.19-10.32,7.69-5.19,3.72-8.26.62-12.18-2.1-4.59-3.17-9-6.65-14.43-8.66C79,68.76,75.33,65.58,71.48,63,62.74,57.18,53.87,51.5,45.37,45.34S28.69,32,19.9,25.92C15.67,23,13.29,18.49,9.09,15.74c-.77-.5-1.09-1.69-1.61-2.56,1.95-.46,3.94-1.43,5.85-1.31,20.58,1.32,40.72-3.44,61.13-4.24,13.24-.53,26.52,0,39.77-.24,13-.19,26-.82,39-.92,9.68-.09,19.36.33,29,.57C190.06,7.22,197.81,7.46,204.93,7.66Z" /> <rect class="letter-cls-2" x="25.65" y="20.41" width="157" height="95" rx="4" /> <path class="letter-cls-3" d="M206.82,120c.72-9.79,1-18.56,2.09-27.25,1.49-12.13.43-24.31,1.36-36.42,1-13.4-.44-26.59-2.27-39.79-.12-.87-.3-1.74-.45-2.62a20.4,20.4,0,0,0-2.9,1.51c-7.74,5.83-15.41,11.75-23.17,17.55-10.32,7.72-20.73,15.32-31,23.07-.89.67-3.21,1.42-1.1,3.37,3.65,3.38,7.21,6.85,10.87,10.21A200.09,200.09,0,0,1,175.3,85.74c6,6.87,13.17,12.73,19.27,19.54,3.83,4.27,6.62,9.48,9.93,14.23a7.91,7.91,0,0,0,1.8,1.47ZM213.13,6.63c.82,20.94,3.46,41.83,2.08,62.85-.85,13.06-1.93,26.1-2.48,39.17-.32,7.56.25,15.15.31,22.73,0,4.13-3.83,4.25-6.22,4.77-14,3.08-28.23,2.85-42.51,2.31-12.36-.46-24.82.87-37.13,0-15.48-1.06-30.85,1.45-46.24.37-14-1-27.86.91-41.79,1.5-9.76.41-19.64-1.76-29.47-2.77a5.24,5.24,0,0,0-1.53,0c-4.71.89-7.21-.12-7.18-5.84,0-9.24-.45-18.45.16-27.73.74-11.35.33-22.77.3-34.16,0-8.27-.12-16.54-.37-24.8-.31-10.2-1.3-20.4-1-30.57C.18,9.92,3.74,6.63,6.76,6.23a237.16,237.16,0,0,0,38.66-.56c16-1.58,32-3.85,48.17-3.43,13.06.35,26.12.53,39.18.46,11.78-.06,23.55-.82,35.32-.8,14.14,0,28.26.49,42.39-.88C213.83.69,213.05,4.7,213.13,6.63ZM192.82,133.14c10.67-1,11.32-1.86,7.89-8.12-6.14-11.2-14.52-20.74-23.32-29.65-9.61-9.74-17.49-21.16-28.56-29.44-1.77-1.32-4.57-3.68-5.6-3.13a92.28,92.28,0,0,0-12.51,8.56q-9.9,7.59-19.56,15.48c-3.5,2.85-7,3.33-10.8.83C93.94,83.5,87.7,79,81,75.34c-4-2.15-7.63-7.27-13.36-3-10.24,7.6-18.12,17.62-27.28,26.33C30,108.5,21.28,120.07,10.05,129.1a2.8,2.8,0,0,0-.87,1.73c0,.32.61,1,1,1,10,1.12,20,2.71,30,3.06,7.11.26,14.27-1.57,21.43-1.74,9.78-.24,19.58.36,29.37.31,10.2-.05,20.45-1.13,30.6-.51,11.68.72,23.25-.69,34.89-.36,9.72.28,19.43,1.85,29.18.7C188,133.27,190.43,133.37,192.82,133.14ZM186,22.87c6.48-5,12.75-10.25,18.89-15.21-7.12-.2-14.87-.44-22.63-.62-9.67-.24-19.35-.66-29-.57-13,.1-26,.73-39,.92C101,7.58,87.7,7.1,74.46,7.63c-20.41.8-40.55,5.56-61.13,4.24-1.91-.12-3.9.85-5.85,1.31.52.87.84,2.06,1.61,2.56,4.2,2.75,6.58,7.27,10.81,10.18,8.79,6,16.82,13.16,25.47,19.42S62.74,57.18,71.48,63c3.85,2.57,7.52,5.75,11.76,7.31,5.46,2,9.84,5.49,14.43,8.66,3.92,2.72,7,5.82,12.18,2.1,3.5-2.5,7.12-4.67,10.32-7.69,7.73-7.27,17.2-12.22,25.27-19.27C158.29,42.92,172.5,33.29,186,22.87ZM58.45,72.35c1.64-2,12.78-3,3.37-9.49-9-6.23-18.35-12-26.86-18.84-5.17-4.14-10.74-7.68-15.79-11.94-4.48-3.77-8.89-7.63-14.42-12.39,2.63,35.77,1.74,70.38.9,105.47,1.13-.55,1.45-.67,1.74-.84,11-6.77,17.84-17.67,26.86-26.41A317.22,317.22,0,0,0,58.45,72.35Z" /> <path class="letter-cls-4" d="M204.5,119.51c-3.31-4.75-6.1-10-9.93-14.23-6.1-6.81-13.26-12.67-19.27-19.54A200.09,200.09,0,0,0,160.23,69.6c-3.66-3.36-7.22-6.83-10.87-10.21-2.11-2,.21-2.7,1.1-3.37,10.29-7.75,20.7-15.35,31-23.07,7.76-5.8,15.43-11.72,23.17-17.55a20.4,20.4,0,0,1,2.9-1.51c.15.88.33,1.75.45,2.62,1.83,13.2,3.29,26.39,2.27,39.79-.93,12.11.13,24.29-1.36,36.42-1.06,8.69-1.37,17.46-2.09,27.25l-.52,1A7.91,7.91,0,0,1,204.5,119.51Z" /> <path class="letter-cls-4" d="M200.71,125c3.43,6.26,2.78,7.1-7.89,8.12-2.39.23-4.8.13-7.2.18-9.75,1.15-19.46-.42-29.18-.7-11.64-.33-23.21,1.08-34.89.36-10.15-.62-20.4.46-30.6.51-9.79,0-19.59-.55-29.37-.31-7.16.17-14.32,2-21.43,1.74-10-.35-20-1.94-30-3.06-.4,0-1-.71-1-1a2.8,2.8,0,0,1,.87-1.73c11.23-9,19.91-20.6,30.3-30.46,9.16-8.71,17-18.73,27.28-26.33,5.73-4.24,9.4.88,13.36,3,6.71,3.64,13,8.16,19.37,12.33,3.83,2.5,7.3,2,10.8-.83q9.66-7.89,19.56-15.48a92.28,92.28,0,0,1,12.51-8.56c1-.55,3.83,1.81,5.6,3.13,11.07,8.28,18.95,19.7,28.56,29.44C186.19,104.28,194.57,113.82,200.71,125Z" /> <path class="letter-cls-4" d="M61.82,62.86c9.41,6.52-1.73,7.5-3.37,9.49a317.22,317.22,0,0,1-24.2,25.56c-9,8.74-15.91,19.64-26.86,26.41-.29.17-.61.29-1.74.84.84-35.09,1.73-69.7-.9-105.47,5.53,4.76,9.94,8.62,14.42,12.39,5,4.26,10.62,7.8,15.79,11.94C43.47,50.85,52.82,56.63,61.82,62.86Z" /> </g> // ここで制御してるよ <g class="letter__seal" :class="{ move: isOpen }"> <path class="letter-cls-4" d="M204.93,6.66c-6.14,5-12.41,10.23-18.89,15.21-13.54,10.42-27.75,20.05-40.6,31.25-8.07,7.05-17.54,12-25.27,19.27-3.2,3-6.82,5.19-10.32,7.69-5.19,3.72-8.26.62-12.18-2.1-4.59-3.17-9-6.65-14.43-8.66C79,67.76,75.33,64.58,71.48,62,62.74,56.18,53.87,50.5,45.37,44.34S28.69,31,19.9,24.92C15.67,22,13.29,17.49,9.09,14.74c-.77-.5-1.09-1.69-1.61-2.56,1.95-.46,3.94-1.43,5.85-1.31,20.58,1.32,40.72-3.44,61.13-4.24,13.24-.53,26.52,0,39.77-.24,13-.19,26-.82,39-.92,9.68-.09,19.36.33,29,.57C190.06,6.22,197.81,6.46,204.93,6.66Z" /> <path class="letter-cls-3" d="M7.06,20.68c4.48,3.87,8.27,7.17,12.11,10.4,5,4.26,10.62,7.8,15.79,11.94,8.51,6.83,17.86,12.61,26.86,18.84,1.57,1.09,10,5.52,12.46,8.27,2.41.92,4.52,3,6.71,4.21,6.71,3.64,13,8.16,19.37,12.33,3.83,2.5,7.3,2,10.8-.83q9.66-7.89,19.56-15.48a92.28,92.28,0,0,1,12.51-8.56.89.89,0,0,1,.53-.07A19,19,0,0,1,148.51,57c0-1,1.31-1.5,2-2,10.29-7.75,20.7-15.35,31-23.07,7.76-5.8,15.43-11.72,23.17-17.55a20.4,20.4,0,0,1,2.9-1.51c0,.19,5.63-6,5.58-7.26-.08-1.93.7-5.94-2.65-5.61-14.13,1.37-28.25.9-42.39.88-11.77,0-23.54.74-35.32.8-13.06.07-26.12-.11-39.18-.46C77.41.82,61.43,3.09,45.42,4.67a237.16,237.16,0,0,1-38.66.56c-3,.4-6.58,3.69-6.71,8.22v.46C2.22,16.39,4.86,18.24,7.06,20.68Zm6.27-9.81c20.58,1.32,40.72-3.44,61.13-4.24,13.24-.53,26.52,0,39.77-.24,13-.19,26-.82,39-.92,9.68-.09,19.36.33,29,.57,7.76.18,15.51.42,22.63.62-6.14,5-12.41,10.23-18.89,15.21-13.54,10.42-27.75,20.05-40.6,31.25-8.07,7.05-17.54,12-25.27,19.27-3.2,3-6.82,5.19-10.32,7.69-5.19,3.72-8.26.62-12.18-2.1-4.59-3.17-9-6.65-14.43-8.66C79,67.76,75.33,64.58,71.48,62,62.74,56.18,53.87,50.5,45.37,44.34S28.69,31,19.9,24.92C15.67,22,13.29,17.49,9.09,14.74c-.77-.5-1.09-1.69-1.61-2.56C9.43,11.72,11.42,10.75,13.33,10.87Z" /> </g> </g> </g> </svg> </button>
MailIcon.ts
import { defineComponent, ref } from "@vue/composition-api"; export default defineComponent({ setup() { const isOpen = ref<boolean>(false); const toggleIsOpen = () => { isOpen.value = true }; return { toggleIsOpen }; } });
MailIcon.scss
.letter { width: 200px; margin: auto; position: relative; display: flex; align-items: center; cursor: pointer; &__svg { overflow: initial; width: 100%; animation: poyopoyo 2s ease-out 1000ms infinite; @keyframes poyopoyo { 0%, 40%, 60%, 80% { transform: scale(1); } 50%, 70% { transform: scale(0.95); } } } &__seal { transition: all 0.6s; transform-origin: 0 6%; } } .move { transform: rotateX(180deg) rotateZ(2deg); }

あいでん

@iden071411