finish splash and quick start

master
howard 2021-05-03 21:02:46 -07:00
parent a3338c5e33
commit 8d63c87eb9
13 changed files with 130 additions and 106 deletions

Binary file not shown.

Binary file not shown.

BIN
dist/3dprint.mp4 vendored Normal file

Binary file not shown.

BIN
dist/extrude.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
dist/sketch.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 407 KiB

View File

@ -25,12 +25,10 @@ if (process.env.NODE_ENV === 'production') {
const visitedFlagStorage = sessionStorage
const App = ({ store }) => {
const [modal, setModal] = useState(!visitedFlagStorage.getItem('visited'))
return <Provider store={store}>
<NavBar />
<Tree />
<ToolTip />
{modal && < Help {...{ setModal }} />}
</Provider>
}

View File

@ -1,34 +1,40 @@
import React, { useState, useEffect, useRef, useCallback, useReducer } from "react"
import { MdCancel} from 'react-icons/md'
import { MdCancel } from 'react-icons/md'
export const Clip = ({ setClip, clip }) => {
let width;
if (window.innerWidth / 1.6 > window.innerHeight) {
width = window.innerHeight * 1.6
width = window.innerHeight * 1.6
} else {
width = window.innerWidth
width = window.innerWidth
}
width = Math.min(width* 0.9, 1024)
width = Math.min(width * 0.9, 1024)
const top = (window.innerHeight - (width / 1.6) - 32) / 2
// console.log(width, width/1.6, window.innerHeight, top)
return (
<div className='absolute left-0 right-0 m-auto bg-gray-700 flex flex-col
border-2 border-gray-500 rounded-2xl overflow-hidden
<div className='absolute left-0 right-0 m-auto flex flex-col
overflow-hidden
'
style={{
width,
top,
}}
>
<div className='text-2xl flex justify-center items-center'>
<div className='text-xl flex justify-center items-center bg-green-900 '>
<div className='text-gray-50'>
{clip[1]}
</div>
<MdCancel className="absolute cursor-pointer text-gray-50 hover:text-gray-400 right-2"
onClick={() => setClip(null)}
/>
</div>
<video src={clip[0]} width='100%' controls muted type="video/mp4" />
<video
className='border-2 border-gray-500'
src={clip[0]} width='100%' controls muted type="video/mp4" />
</div>
)
}

View File

@ -48,7 +48,7 @@ function reducer(state, action) {
case 'drag':
const dragLeft = state.dragLeft - action.move
if (dragLeft < 0 || dragLeft > state.rect * arr.length) {
if (dragLeft < 0 || dragLeft > state.rect * arr.length - 1) {
return state
} else {
return {
@ -77,7 +77,7 @@ const arr = [
['Sketch out your idea in a 2D outline.', 'sketch.png'],
['Transform the sketched shape into a 3D solid.', 'extrude.png'],
['Use additional sketches to sculpt or extend the model.', 'sculpt.gif'],
['Export your design to a 3D printer and turn into reality.', ''],
['Export your design to a 3D printer and turn into reality.', '3dprint.mp4'],
]
@ -87,7 +87,7 @@ const arr = [
export const Help = ({ setModal }) => {
export const Help = ({ setModal, setQs }) => {
@ -150,38 +150,53 @@ export const Help = ({ setModal }) => {
<div className='bg-transparent h-full flex select-none'
style={{
width: state.rect * (arr.length + 1),
width: state.rect * (arr.length),
transform: `translateX(${state.dragging ? -state.dragLeft - 4 : -state.pg * state.rect - 4}px)`,
transition: state.dragging ? null : elastic
}}
>
{
arr.map(
(e, idx) => <div className='flex flex-col items-center'
style={{ width: state.rect, height: '100%' }} key={idx}
>
<img className="bg-gray-800"
src={e[1]}
style={{
width: state.rect * 0.8,
height: state.rect * 0.8,
}}
></img>
(e, idx) => {
const isVideo = e[1].match(/\.[0-9a-z]+$/i)[0] == '.mp4'
return <div className='flex flex-col items-center'
style={{ width: state.rect, height: '100%' }} key={idx}
>
{
isVideo ?
<video src={e[1]}
style={{
width: state.rect * 0.8,
height: state.rect * 0.8,
}}
autoPlay loop
muted type="video/mp4" />
:
<img
src={e[1]}
style={{
width: state.rect * 0.8,
height: state.rect * 0.8,
}}
></img>
}
<div className='my-auto text-center text-gray-50 text-sm sm:text-base md:text-xl'>
{e[0]}
<div className='my-auto text-center text-gray-50 text-sm sm:text-base md:text-xl'>
{e[0]}
</div>
</div>
</div>
}
)
}
<div className='flex flex-col items-center'
{/* <div className='flex flex-col items-center'
style={{ width: state.rect, height: '100%' }}
>
<QuickStart {...{ setModal }} />
</div>
</div> */}
</div>
@ -192,7 +207,10 @@ export const Help = ({ setModal }) => {
// style={{
// position:'absolute'
// bottom: 0.1 * state.rect}}
onClick={() => setModal(false)}
onClick={() => {
setModal(false)
setQs(true)
}}
>
Get Started
</div>
@ -208,14 +226,14 @@ export const Help = ({ setModal }) => {
<div className='cursor-pointer select-none absolute w-12 h-12 top-0 bottom-0 my-auto -right-24 fill-current bg-gray-100 hover:bg-gray-300 rounded-full'
onClick={() => carouselDispatch({ type: "move", del: 1 })}
style={{
visibility: state.pg == arr.length ? 'hidden' : 'visible'
visibility: state.pg == arr.length - 1 ? 'hidden' : 'visible'
}}
>
<MdArrowForward className="w-full h-full text-gray-700 p-3" />
</div>
<div className="flex -bottom-8 absolute flex justify-center items-center">
{Array(arr.length + 1).fill().map((ele, idx) => (
{Array(arr.length).fill().map((ele, idx) => (
<div key={idx} className={`h-2 w-2 mx-1 rounded-full ${idx == state.pg ? 'bg-gray-50' : 'bg-gray-500'}`}></div>
))}
</div>

View File

@ -12,12 +12,11 @@ export class Modal extends React.Component {
this.handleClickout = this.handleClickout.bind(this)
this.clickOut = props.clickOut == undefined ? true : props.clickOut
}
handleClickout(e) {
if (modalRoot.lastChild != this.el ) return
console.log(this.id, e.composedPath())
if (modalRoot.lastChild != this.el) return
if (!e.composedPath().includes(this.el)) {
@ -28,12 +27,13 @@ export class Modal extends React.Component {
componentDidMount() {
modalRoot.appendChild(this.el);
document.addEventListener( // handles click outside buttona & dropdown
'click', this.handleClickout
,
{ capture: true } // capture phase to allow for stopPropogation on others
)
if (this.clickOut) {
document.addEventListener( // handles click outside buttona & dropdown
'click', this.handleClickout
,
{ capture: true } // capture phase to allow for stopPropogation on others
)
}
}
componentWillUnmount() {

View File

@ -12,7 +12,8 @@ import { Dialog } from './dialog'
import { Modal } from './modal'
import { STLExport, saveFile, openFile } from './fileHelpers'
import { QuickStart } from './quickStart';
import { Help } from './help'
const visitedFlagStorage = sessionStorage
const buttonIdx = {
'line': 1,
'arc': 2,
@ -170,9 +171,10 @@ export const NavBar = () => {
const [_, forceUpdate] = useReducer(x => x + 1, 0);
const [splash, setSplash] = useState(!visitedFlagStorage.getItem('visited'))
const [modal, setModal] = useState(false)
return <div className='topNav flex justify-center bg-gray-800'>
return <div className='topNav flex justify-center bg-gray-800 text-gray-200 '>
<div className='w-auto h-full flex-1 flex justify-end lg:justify-between'>
<div className='w-100 h-full font-mono text-lg text-gray-200 select-none hidden lg:flex mr-8 items-center'>
@ -187,8 +189,8 @@ export const NavBar = () => {
{(sketchActive ? sketchModeButtons : partModeButtons).map(
([Icon, fcn, txt], idx) => (
Icon !== undefined ?
<Icon className={`cursor-pointer fill-current text-gray-200 w-auto h-full p-3.5
${idx == buttonIdx[mode] ? 'bg-green-600' : 'hover:bg-gray-600 bg-transparent'}`} tooltip={txt}
<Icon className={`cursor-pointer fill-current w-auto h-full p-3.5
${idx == buttonIdx[mode] ? 'bg-green-800' : 'hover:bg-gray-600 bg-transparent'}`} tooltip={txt}
onClick={fcn} key={idx}
/> :
<div className="w-12 h-full"></div>
@ -197,23 +199,27 @@ export const NavBar = () => {
</div>
<div className='w-auto h-full flex-1 justify-end flex-shrink-1 hidden md:flex'>
<MdHelpOutline className="btn-green w-auto h-full p-3" onClick={() => {
<MdHelpOutline className={`cursor-pointer fill-current w-auto h-full p-3
${modal ? 'bg-green-800' : 'hover:bg-gray-600 bg-transparent'}`} onClick={() => {
setModal(true)
}
} />
<a href='https://github.com/twpride/three.cad' className='h-full w=auto'>
<FaGithub className="btn-green w-auto h-full p-3.5"></FaGithub>
<a href='https://github.com/twpride/three.cad' className='h-full w-auto'>
<FaGithub className="text-gray-200 cursor-pointer hover:bg-gray-600 bg-transparent w-auto h-full p-3.5"></FaGithub>
</a>
<a href='https://www.linkedin.com/in/howard-hwang-b3000335' className='h-full w=auto'>
<FaLinkedin className="btn-green w-auto h-full p-3.5"></FaLinkedin>
<a href='https://www.linkedin.com/in/howard-hwang-b3000335' className='h-full w-auto'>
<FaLinkedin className="text-gray-200 cursor-pointer hover:bg-gray-600 bg-transparent w-auto h-full p-3.5"></FaLinkedin>
</a>
</div>
{
modal && <Modal {...{setModal, id: 'navbar'}}>
<QuickStartWrapper>
<QuickStart {...{setModal}}/>
</QuickStartWrapper>
splash && <Modal {...{ setModal: setSplash, clickOut: false}}>
<Help {...{ setModal: setSplash, setQs: setModal }} />
</Modal>
}
{
modal && <Modal {...{ setModal, id: 'navbar' }}>
<QuickStart {...{ setModal }} />
</Modal>
}
@ -222,29 +228,4 @@ export const NavBar = () => {
export const QuickStartWrapper = ({ children }) => {
const [rect, setRect] = useState(Math.min(Math.min(window.innerHeight * 0.8, window.innerWidth * 0.7), 800))
const updateSize = () => {
setRect(Math.min(Math.min(window.innerHeight * 0.8, window.innerWidth * 0.7), 800))
}
useEffect(() => {
window.addEventListener('resize', updateSize)
return () => {
window.removeEventListener('resize', updateSize)
}
}, [])
return <div className="absolute left-0 right-0 mx-auto bg-gray-700 rounded-xl flex flex-col items-center border-gray-500 border-2 overflow-hidden"
style={{
width: rect,
height: 1.1 * rect,
top: (window.innerHeight - 1.1 * rect) / 3,
}}
>
{children}
</div>
}

View File

@ -4,7 +4,7 @@ import { useDispatch, useSelector } from "react-redux"
import { Clip } from './clip'
import { Modal } from './modal'
import { MdZoomIn, MdSave, MdFolder, MdInsertDriveFile, MdHelpOutline } from 'react-icons/md'
import { MdZoomIn, MdSave, MdFolder, MdInsertDriveFile, MdCancel } from 'react-icons/md'
import { FaRegPlayCircle, FaEdit, FaCubes } from 'react-icons/fa'
import * as Icon from "./icons";
@ -50,36 +50,48 @@ const clipArr = [
['basic-workflow.mp4', 'Basic model creation workflow'],
['load-file-and-edit.mp4', 'Loading and editing models'],
['export-to-3dprint.mp4', 'Exporting model for 3D printing'],
['headphones-stand.json.gz', 'Headphones Stand Model'],
['headphone-stand.json.gz', 'Headphone Stand Model'],
]
const utf8decoder = new TextDecoder();
export const QuickStart = ({setModal}) => {
export const QuickStart = ({ setModal }) => {
const dispatch = useDispatch()
const [clip, setClip] = useState(null)
const [rect, setRect] = useState(Math.min(Math.min(window.innerHeight * 0.8, window.innerWidth * 0.7), 800))
return <div className='bg-transparent w-full h-full flex justify-center'>
<div className="bg-transparent w-full h-full
text-sm lg:text-base xl:text-lg
flex flex-col items-center overflow-y-auto overflow-x-hidden text-gray-50">
<div className='text-center w-full text-lg lg:text-xl xl:text-2xl my-2 font-bold'>
Quick Start
</div>
const updateSize = () => {
setRect(Math.min(Math.min(window.innerHeight * 0.8, window.innerWidth * 0.7), 800))
}
useEffect(() => {
window.addEventListener('resize', updateSize)
return () => {
window.removeEventListener('resize', updateSize)
}
}, [])
return <div className="absolute left-0 right-0 mx-auto bg-gray-700 flex flex-col items-center
text-sm lg:text-base xl:text-lg text-gray-50"
style={{
width: rect,
height: 1.1 * rect,
top: (window.innerHeight - 1.1 * rect) / 2,
}}
>
<div className='w-full h-full bg-transparent overflow-y-auto overflow-x-hidden flex flex-col items-center'>
<div className='text-center text-base lg:text-lg xl:text-xl mb-2'>
<div className='text-center text-base lg:text-lg xl:text-xl mb-2 font-bold'>
Demos
</div>
<div className='mb-4 cursor-pointer'>
<div className='mb-4 cursor-pointer w-min'>
{
clipArr.map((ele, idx) => {
const isGz = ele[0].match(/\.[0-9a-z]+$/i)[0] == '.gz'
return <div className="flex h-12 mx-2 rounded-lg items-center hover:text-green-500"
return <div className="flex h-12 mx-2 items-center hover:text-green-500 whitespace-nowrap"
onClick={async () => {
if (isGz) {
const state = sce.loadState(
@ -95,9 +107,9 @@ export const QuickStart = ({setModal}) => {
).decompress()
)
)
setModal(false)
dispatch({ type: 'restore-state', state, fileName: ele[0] })
dispatch({ type: 'restore-state', state, fileName: ele[0].replace(/\.[^/.]+$/, "") })
sce.render()
} else {
@ -109,9 +121,9 @@ export const QuickStart = ({setModal}) => {
key={idx}
>
{isGz ?
<FaCubes size={'2.5em'} style={{ padding: '0.625em' }} />
<FaCubes size={'2.5em'} className='flex-shrink-0' style={{ padding: '0.625em' }} />
:
<FaRegPlayCircle size={'2.5em'} style={{ padding: '0.625em' }} />
<FaRegPlayCircle size={'2.5em'} className='flex-shrink-0' style={{ padding: '0.625em' }} />
}
{ele[1]}
</div>
@ -130,7 +142,7 @@ export const QuickStart = ({setModal}) => {
{
navArr.map((row, i) => (
typeof row === 'string' ?
<div className='col-span-2 flex justify-center text-base lg:text-lg xl:text-xl mb-2' key={i}>
<div className='col-span-2 flex justify-center text-base lg:text-lg xl:text-xl mb-2 font-bold' key={i}>
{row}
</div>
:
@ -161,18 +173,27 @@ export const QuickStart = ({setModal}) => {
}
</div>
{
clip && <Modal setModal={setClip} id={'qs'}>
<Clip {...{ setClip, clip }} />
</Modal>
}
</div>
<div className='absolute -top-7 w-full text-xl flex justify-center items-center bg-green-800'>
<div className='text-gray-50'>
Quick Start
</div>
<MdCancel className="absolute cursor-pointer text-gray-50 hover:text-gray-400 right-2"
onClick={() => setModal(null)}
/>
</div>
{
clip && <Modal setModal={setClip} id={'qs'}>
<Clip {...{ setClip, clip }} />
</Modal>
}
</div>
}