Updated animations (#11)

Co-authored-by: Alex Stan <alex.stan.2010@proton.me>
Co-committed-by: Alex Stan <alex.stan.2010@proton.me>
This commit is contained in:
Alex Stan 2024-06-02 11:47:22 +03:00 committed by Alex Stan
parent 5500da1f6d
commit 6761a2b0d0
9 changed files with 110 additions and 29 deletions

View file

@ -0,0 +1,26 @@
'use client';
import { motion } from "framer-motion"
import { useState } from "react";
const LinkButton = ({text, href, onPress}: {text: string, href: string, onPress?: () => any}) => {
const [buttonHovered, setButtonHovered] = useState( false );
const [buttonPressed, setButtonPressed] = useState( false );
return (
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
<motion.a
href={href}
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
style={{ marginBottom: '40px' }}
animate={{opacity: buttonHovered ? 0.8 : 1}}
onMouseEnter={()=>setButtonHovered(true)}
onMouseLeave={()=>setButtonHovered(false)}
onClick={()=>{setButtonPressed(true); onPress && onPress();}}
>
{text}
</motion.a>
</div>
);
}
export default LinkButton;

View file

@ -1,6 +1,8 @@
'use client';
import { Transition } from "@headlessui/react";
import { motion } from "framer-motion";
import { useState } from "react";
import LinkButton from "./link-button";
const Logo = () => {
@ -18,20 +20,24 @@ const Logo = () => {
leave="transition-opacity duration-75"
leaveFrom={logoHovered ? "opacity-70" : "opacity-100"}
leaveTo={logoHovered ? "opacity-100" : "opacity-70"}> */}
<a href="/" style={{ marginBottom: '40px', marginLeft: '40px', transition: 'opacity', opacity: logoHovered ? 0.7 : 1 , animationDuration: '0.3s'}}
<motion.a href="/" style={{ marginBottom: '40px', marginLeft: '40px'}} animate={{opacity: logoHovered ? 0.7 : 1}}
onMouseEnter={()=>setLogoHovered(true)}
onMouseLeave={()=>setLogoHovered(false)}>
<img src="/logo-no-background.png" height="auto" width="400px"/>
</a>
</motion.a>
{/* </Transition> */}
<div style={{ marginBottom: '40px', marginRight: '40px' }}></div>
<a
{/* <motion.a
href="/about-us"
className="rounded-md bg-white text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
style={{ marginBottom: '40px', marginRight: '40px', padding: '10px' }}
animate={{opacity: aboutButtonHovered ? 0.8 : 1}}
onMouseEnter={()=>setAboutButtonHovered(true)}
onMouseLeave={()=>setAboutButtonHovered(false)}
>
About us
</a>
</motion.a> */}
<LinkButton text="About us" href="/about-us"/>
</div>
)
}

View file

@ -1,8 +1,10 @@
import { ReactComponentElement, ReactNode } from "react";
import { ReactComponentElement, ReactNode, useEffect } from "react";
import { AnswerType, Question } from "../lib/question";
import { SessionState } from "../lib/session";
import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
import { Button, Transition } from "@headlessui/react";
import wait from "../lib/delay";
import { motion } from "framer-motion";
const QuestionView = ({ question, setState, state }: { question: Question, setState: any, state: SessionState }) => {
@ -16,6 +18,10 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
}))
}
useEffect( () => {
wait( 1000 );
}, [] );
const handleAnswerClick = (right: boolean) => {
setState( (prevState: SessionState) => ({
...prevState,
@ -47,14 +53,16 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
{question.text}
</h1>
</Transition>
<Transition
{/* <Transition
show={(question && state.opened[ question.id ]) ? true : false}
enter="transition-opacity duration-300"
enterFrom="opacity-0"
enterTo="opacity-90"
leave="transition-opacity duration-150"
leaveFrom="opacity-90"
leaveTo="opacity-0">
leaveTo="opacity-0"> */}
{/* <motion.div animate={{visibility: ((question && state.opened[ question.id ]) ? "visible" : "collapse") }}> */}
<motion.div animate={(question && state.opened[ question.id ]) ? {visibility: "visible", opacity: 1} : {visibility: "collapse", opacity: 0}}>
<div><p className="mt-6 text-lg leading-8 text-gray-200">
{question.answer}
</p>
@ -91,7 +99,7 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
</button>
</div>
</div>
</Transition>
</motion.div>
<Transition
show={(question && (state.opened[ question.id ])) ? false : true}
enter="transition-opacity duration-300"
@ -100,7 +108,7 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
leave="transition-opacity duration-150"
leaveFrom="opacity-90"
leaveTo="opacity-0">
<Button className="text-sm font-semibold leading-6 text-gray-200" onClick={handleOpenClick}>
<Button className="leading-6 text-black back rounded-md bg-white text-sm font-semibold text-gray-900" onClick={handleOpenClick} style = {{padding: '5px'}}>
Show answer
</Button>
</Transition>

View file

@ -1,7 +1,13 @@
import { PlayIcon } from '@heroicons/react/24/outline'
'use client';
import { motion } from 'framer-motion';
import { useState } from 'react';
import LinkButton from './link-button';
const TopicCard = ({ title, children, link }: { title: string, children: any, link: string }) => {
return(
<div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}>
const [clicked, setClicked] = useState( false );
const [hovered, setHovered] = useState( false );
return (
<motion.div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}
animate={{x: hovered? 10 : 0, opacity: clicked ? 0.5 : 1 }} onMouseEnter={()=>setHovered(true)} onMouseLeave={()=>setHovered(false)}>
<svg
viewBox="0 0 1024 1024"
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
@ -25,16 +31,17 @@ const TopicCard = ({ title, children, link }: { title: string, children: any, li
className="-mt-2 p-2 lg:mt-0 lg:max-w-md lg:flex-shrink-0 lg:flex lg:flex-col lg:justify-center"
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
<a
{/* <a
href={link}
className="rounded-md text-sm font-semibold border text-gray-200 shadow-sm hover:text-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
style={{ paddingLeft: '50px', paddingRight: '50px', paddingTop: '20px', paddingBottom: '20px'}}
>
Begin
</a>
</a> */}
<LinkButton text="Begin" href={link} onPress={()=>setClicked(true)}/>
</div>
</div>
</motion.div>
)
}

5
app/lib/delay.ts Normal file
View file

@ -0,0 +1,5 @@
const wait = (time: number): Promise<void> => {
return new Promise( res => setTimeout( res, time ) );
}
export default wait;

View file

@ -1,12 +1,15 @@
import { Fragment } from 'react'
'use client';
import { Fragment, useState } from 'react'
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import Logo from './components/logo';
import { motion } from 'framer-motion';
import LinkButton from './components/link-button';
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Example() {
const [buttonPressed, setButtonPressed] = useState( false );
return (
<div>
{/*Background visual elements*/}
@ -33,10 +36,13 @@ export default function Example() {
<Logo/>
<Transition
appear={true}
show={true}
show={!buttonPressed}
enter="transition-opacity duration-500"
enterFrom="opacity-0"
enterTo="opacity-80">
enterTo="opacity-80"
leave="transition-opacity duration-150"
leaveFrom="opacity-80"
leaveTo="opacity-0">
<div className="mx-auto max-w-7xl py-24 sm:px-6 sm:py-32 lg:px-8">
<div className="relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
<svg
@ -62,14 +68,18 @@ export default function Example() {
Get questions from a multitude of different subjects that include cybersecurity, geography and C++
</p>
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
<a
{/* <motion.a
href="sessionconfig"
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
style={{ marginBottom: '40px' }}
animate={{opacity: buttonHovered ? 0.8 : 1}}
onMouseEnter={()=>setButtonHovered(true)}
onMouseLeave={()=>setButtonHovered(false)}
onClick={()=>setButtonPressed(true)}
>
Get started
</a>
</motion.a> */}
<LinkButton text="Get started" href="/sessionconfig" onPress={()=>setButtonPressed(true)}/>
</div>
</div>
</div>

View file

@ -81,13 +81,6 @@ export default function Example() {
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
aria-hidden="true"
>
<div
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
/>
</div>
</div>
)

25
package-lock.json generated
View file

@ -10,6 +10,7 @@
"dependencies": {
"@headlessui/react": "^2.0.4",
"@heroicons/react": "^2.1.3",
"framer-motion": "^11.2.10",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18",
@ -2245,6 +2246,30 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/framer-motion": {
"version": "11.2.10",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.10.tgz",
"integrity": "sha512-/gr3PLZUVFCc86a9MqCUboVrALscrdluzTb3yew+2/qKBU8CX6nzs918/SRBRCqaPbx0TZP10CB6yFgK2C5cYQ==",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",

View file

@ -11,6 +11,7 @@
"dependencies": {
"@headlessui/react": "^2.0.4",
"@heroicons/react": "^2.1.3",
"framer-motion": "^11.2.10",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18",