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 visitedFlagStorage = sessionStorage
const App = ({ store }) => { const App = ({ store }) => {
const [modal, setModal] = useState(!visitedFlagStorage.getItem('visited'))
return <Provider store={store}> return <Provider store={store}>
<NavBar /> <NavBar />
<Tree /> <Tree />
<ToolTip /> <ToolTip />
{modal && < Help {...{ setModal }} />}
</Provider> </Provider>
} }

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef, useCallback, useReducer } from "react" 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 }) => { export const Clip = ({ setClip, clip }) => {
@ -7,28 +7,34 @@ export const Clip = ({ setClip, clip }) => {
if (window.innerWidth / 1.6 > window.innerHeight) { if (window.innerWidth / 1.6 > window.innerHeight) {
width = window.innerHeight * 1.6 width = window.innerHeight * 1.6
} else { } 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 const top = (window.innerHeight - (width / 1.6) - 32) / 2
// console.log(width, width/1.6, window.innerHeight, top) // console.log(width, width/1.6, window.innerHeight, top)
return ( return (
<div className='absolute left-0 right-0 m-auto bg-gray-700 flex flex-col <div className='absolute left-0 right-0 m-auto flex flex-col
border-2 border-gray-500 rounded-2xl overflow-hidden overflow-hidden
' '
style={{ style={{
width, width,
top, 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'> <div className='text-gray-50'>
{clip[1]} {clip[1]}
</div> </div>
<MdCancel className="absolute cursor-pointer text-gray-50 hover:text-gray-400 right-2"
onClick={() => setClip(null)}
/>
</div> </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> </div>
) )
} }

View File

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

View File

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

View File

@ -12,7 +12,8 @@ import { Dialog } from './dialog'
import { Modal } from './modal' import { Modal } from './modal'
import { STLExport, saveFile, openFile } from './fileHelpers' import { STLExport, saveFile, openFile } from './fileHelpers'
import { QuickStart } from './quickStart'; import { QuickStart } from './quickStart';
import { Help } from './help'
const visitedFlagStorage = sessionStorage
const buttonIdx = { const buttonIdx = {
'line': 1, 'line': 1,
'arc': 2, 'arc': 2,
@ -170,9 +171,10 @@ export const NavBar = () => {
const [_, forceUpdate] = useReducer(x => x + 1, 0); const [_, forceUpdate] = useReducer(x => x + 1, 0);
const [splash, setSplash] = useState(!visitedFlagStorage.getItem('visited'))
const [modal, setModal] = useState(false) 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-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'> <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( {(sketchActive ? sketchModeButtons : partModeButtons).map(
([Icon, fcn, txt], idx) => ( ([Icon, fcn, txt], idx) => (
Icon !== undefined ? Icon !== undefined ?
<Icon className={`cursor-pointer fill-current text-gray-200 w-auto h-full p-3.5 <Icon className={`cursor-pointer fill-current w-auto h-full p-3.5
${idx == buttonIdx[mode] ? 'bg-green-600' : 'hover:bg-gray-600 bg-transparent'}`} tooltip={txt} ${idx == buttonIdx[mode] ? 'bg-green-800' : 'hover:bg-gray-600 bg-transparent'}`} tooltip={txt}
onClick={fcn} key={idx} onClick={fcn} key={idx}
/> : /> :
<div className="w-12 h-full"></div> <div className="w-12 h-full"></div>
@ -197,23 +199,27 @@ export const NavBar = () => {
</div> </div>
<div className='w-auto h-full flex-1 justify-end flex-shrink-1 hidden md:flex'> <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) setModal(true)
} }
} /> } />
<a href='https://github.com/twpride/three.cad' className='h-full w=auto'> <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> <FaGithub className="text-gray-200 cursor-pointer hover:bg-gray-600 bg-transparent w-auto h-full p-3.5"></FaGithub>
</a> </a>
<a href='https://www.linkedin.com/in/howard-hwang-b3000335' className='h-full w=auto'> <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> <FaLinkedin className="text-gray-200 cursor-pointer hover:bg-gray-600 bg-transparent w-auto h-full p-3.5"></FaLinkedin>
</a> </a>
</div> </div>
{ {
modal && <Modal {...{setModal, id: 'navbar'}}> splash && <Modal {...{ setModal: setSplash, clickOut: false}}>
<QuickStartWrapper> <Help {...{ setModal: setSplash, setQs: setModal }} />
<QuickStart {...{setModal}}/> </Modal>
</QuickStartWrapper> }
{
modal && <Modal {...{ setModal, id: 'navbar' }}>
<QuickStart {...{ setModal }} />
</Modal> </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 { Clip } from './clip'
import { Modal } from './modal' 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 { FaRegPlayCircle, FaEdit, FaCubes } from 'react-icons/fa'
import * as Icon from "./icons"; import * as Icon from "./icons";
@ -50,36 +50,48 @@ const clipArr = [
['basic-workflow.mp4', 'Basic model creation workflow'], ['basic-workflow.mp4', 'Basic model creation workflow'],
['load-file-and-edit.mp4', 'Loading and editing models'], ['load-file-and-edit.mp4', 'Loading and editing models'],
['export-to-3dprint.mp4', 'Exporting model for 3D printing'], ['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(); const utf8decoder = new TextDecoder();
export const QuickStart = ({setModal}) => { export const QuickStart = ({ setModal }) => {
const dispatch = useDispatch() const dispatch = useDispatch()
const [clip, setClip] = useState(null) 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'> const updateSize = () => {
<div className="bg-transparent w-full h-full setRect(Math.min(Math.min(window.innerHeight * 0.8, window.innerWidth * 0.7), 800))
text-sm lg:text-base xl:text-lg }
flex flex-col items-center overflow-y-auto overflow-x-hidden text-gray-50"> useEffect(() => {
<div className='text-center w-full text-lg lg:text-xl xl:text-2xl my-2 font-bold'> window.addEventListener('resize', updateSize)
Quick Start return () => {
</div> 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 Demos
</div> </div>
<div className='mb-4 cursor-pointer'> <div className='mb-4 cursor-pointer w-min'>
{ {
clipArr.map((ele, idx) => { clipArr.map((ele, idx) => {
const isGz = ele[0].match(/\.[0-9a-z]+$/i)[0] == '.gz' 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 () => { onClick={async () => {
if (isGz) { if (isGz) {
const state = sce.loadState( const state = sce.loadState(
@ -97,7 +109,7 @@ export const QuickStart = ({setModal}) => {
) )
setModal(false) setModal(false)
dispatch({ type: 'restore-state', state, fileName: ele[0] }) dispatch({ type: 'restore-state', state, fileName: ele[0].replace(/\.[^/.]+$/, "") })
sce.render() sce.render()
} else { } else {
@ -109,9 +121,9 @@ export const QuickStart = ({setModal}) => {
key={idx} key={idx}
> >
{isGz ? {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]} {ele[1]}
</div> </div>
@ -130,7 +142,7 @@ export const QuickStart = ({setModal}) => {
{ {
navArr.map((row, i) => ( navArr.map((row, i) => (
typeof row === 'string' ? 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} {row}
</div> </div>
: :
@ -161,18 +173,27 @@ export const QuickStart = ({setModal}) => {
} }
</div> </div>
{
clip && <Modal setModal={setClip} id={'qs'}>
<Clip {...{ setClip, clip }} />
</Modal>
}
</div> </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> </div>
} }