iosにありがちなモーダルの遷移とプッシュの遷移のサンプル
デモ
https://nakadoribooks.github.io/modal_and_push/
使うもの
create-react-app
styled-components
コード
- styleの定義
- Reactの実装
スタイル
アニメーションはだいたいこのくらい
duration: 600
ease: 'cubic-bezier(.17,.71,0,1)'
styles.js
importReactfrom'react';importstyledfrom'styled-components';constsizes={headerHeight:72}// だいたいこのくらいconstanimation={page:{duration:600,ease:'cubic-bezier(.17,.71,0,1)'},}animation.page.transition=`transform ${animation.page.duration}ms ${animation.page.ease}, opacity ${animation.page.duration}ms ${animation.page.ease}`constcolors={primary:"#3880ff",secondary:"#0cd1e8",border:"#eeeeee",white:"#ffffff",black:"#000000",}constHeader=styled.div`
width: 100%;
padding:0px 20px;
position:absolute;
height: ${sizes.headerHeight}px;
border-bottom:1px solid ${colors.border};
background-color:${colors.white};
padding-top:1px;
justify-content:space-between;
display:flex;
align-items:center;
box-shadow: 0 2px 2px rgba(200,200,200,.1);
h1{
font-size:20px;
}
`constHeaderButton=styled.a`
display:inline-block;
height:40px;
font-size:14px;
line-height:38px;
border: 1px solid ${colors.primary};
border-radius:5px;
color:${colors.primary};
padding:0px 20px;
min-width: 100px;
text-align:center;
cursor:pointer;
:hover{
background-color:${colors.primary};
color:${colors.white};
}
`constHeaderSpace=styled.div`
width:100px;
`constRootView=styled.div``constPageWrapper=styled.div`
position:fixed;
top:0px;left:0px;right:0px;bottom:0px;
`// 後ろにある半透明の黒constBehindLayer=styled.div`
background-color:${colors.black};
opacity: 0;
position:absolute;
top:0px;left:0px;right:0px;bottom:0px;
transition: opacity ${animation.page.duration}ms ease;
${(props)=>props.showed&&`
opacity:0.3;
`}
`constPageContainer=styled.div`
height:100%;
position:relative;
top:0px;
transition: ${animation.page.transition};
`constRootContainer=styled(PageContainer)`
/* 上にモーダルきた時に少しちっさくなるやつ */
${props=>props.modaling&&`
transform: scale(0.99)
`}
/* プッシュされた時につこし左に行くやつ */
${props=>props.pushing&&`
transform: translateX(-10vh)
`}
`constModalContainer=styled(PageContainer)`
transform: translateY(100vh);
${props=>props.showed&&`
transform: translateY(0vh);
`}
`constPushContainer=styled(PageContainer)`
transform: translateX(50vh);
opacity: 0;
${props=>props.showed&&`
opacity: 1.0;
transform: translateX(0vh);
`}
`constPageContents=styled.div`
height:calc(100vh - ${sizes.headerHeight}px);
position:relative;
top:${sizes.headerHeight}px;
overflow-y:scroll;
background-color:${colors.white};
`constLinkButton=styled.p`
text-align:center;
a{
display:inline-block;
padding:10px 20px;
border-radius: 10px;
border: 1px solid ${colors.border};
cursor:pointer;
transition: background-color 300ms ease, transform 300ms ease;
:hover{
opacity: 0.7;
}
}
/* フォーカス残るよう */
${props=>props.focus&&`
a{
background-color:${colors.border};
transofrm:scale(0.99);
}
`}
`constCenterContainer=styled.div`
width:100%;
height:100%;
display: flex;
justify-content: center;
align-items: center;
`export{animation,Header,HeaderButton,HeaderSpace,RootView,PageWrapper,BehindLayer,RootContainer,ModalContainer,PushContainer,PageContents,LinkButton,CenterContainer}
React 実装
遷移先は didMountで少し遅らせてアニメーション表示
setTimeout(()=>{this.setState({showed:true})},10)
遷移元は アニメーション中のフラグとmodal/pushを表示する用のフラグ二つづつもつ。
const {modaling, pushing, showingModal, showingPush} = this.state
App.js
importReactfrom'react';import{animation,Header,HeaderButton,HeaderSpace,RootView,PageWrapper,BehindLayer,RootContainer,ModalContainer,PushContainer,PageContents,LinkButton,CenterContainer}from'./styles'classModalPageextendsReact.Component{state={showed:false}componentDidMount(){// アニメーションで表示setTimeout(()=>{this.setState({showed:true})},10)}render=()=>{const{showed}=this.statereturn<PageWrapper><BehindLayershowed={showed}/>
<ModalContainershowed={showed}><Header><HeaderButtononClick={this.handleClickClose}>閉じる</HeaderButton>
<h1>モーダル</h1>
<HeaderSpace/></Header>
<PageContents><CenterContainer><h1>モーダル</h1>
</CenterContainer>
</PageContents>
</ModalContainer>
</PageWrapper>
}handleClickClose=(event)=>{this.setState({showed:false})this.props.onClose()}}classPushPageextendsReact.Component{state={showed:false}componentDidMount(){// アニメーションで表示setTimeout(()=>{this.setState({showed:true})},10)}render=()=>{const{showed}=this.statereturn<PageWrapper><BehindLayershowed={showed}/>
<PushContainershowed={showed}><Header><HeaderButtononClick={this.handleClickClose}>戻る</HeaderButton>
<h1>プッシュ</h1>
<HeaderSpace/></Header>
<PageContents><CenterContainer><h1>プッシュ</h1>
</CenterContainer>
</PageContents>
</PushContainer>
</PageWrapper>
}handleClickClose=(event)=>{this.setState({showed:false})this.props.onBack()}}classAppextendsReact.Component{state={pushing:false,modaling:false,showingModal:false,showingPush:false}render=()=>{const{modaling,pushing,showingModal,showingPush}=this.statereturn<RootView><PageWrapper><RootContainermodaling={modaling}pushing={pushing}><Header><div/><h1>ページ遷移サンプル</h1><div /></Header>
<PageContents><CenterContainer><div>{/* 戻ってきた時に分かりやすいようにフォーカスを残しておく */}<LinkButtonfocus={showingModal}><aonClick={this.handleClickModal}>モーダル</a></LinkButton><LinkButtonfocus={showingPush}><aonClick={this.handleClickPush}>プッシュ</a></LinkButton></div>
</CenterContainer>
</PageContents>
</RootContainer>
</PageWrapper>
{showingModal&&<ModalPageonClose={this.handleCloseModal}/>}
{showingPush&&<PushPageonBack={this.handleBackPush}/>}
</RootView>
}handleClickModal=(event)=>{this.setState({modaling:true,showingModal:true})}handleClickPush=(event)=>{this.setState({pushing:true,showingPush:true})}handleCloseModal=(event)=>{this.setState({modaling:false})setTimeout(()=>{this.setState({showingModal:false})},animation.page.duration)}handleBackPush=(event)=>{this.setState({pushing:false})setTimeout(()=>{this.setState({showingPush:false})},animation.page.duration)}}exportdefaultApp;