# Landscape-full-screen (React Hook) :::info index.html (不重要,主要邏輯在下方 index.js) ```htmlmixed= // 宣告一個 root 標籤 <div id="root"></div> ``` ::: #### useEffect 你可以先粗略理解為 render 結束後的 callback #### useState 你可以先粗略理解為 class 底下的 property :::success index.js ```jsx= import React, { useEffect, useState, useRef } from 'react'; // 渲染方法 import { render } from 'react-dom'; import './style.css'; const isPortrait = () => (window.innerWidth < window.innerHeight); const App = () => { // 設定 showSwipeUp(boolean) 預設值為 false const [ showSwipeUp, setShowSwipeUp ] = useState(false); // game-content 設定在 css,hide 設定在 css -> display: none const gameContentClasses = showSwipeUp ? 'game-content hide' : 'game-content'; // swipe-up 設定在 css,show 設定在 css -> display: block const swipeUpClasses = showSwipeUp ? 'swipe-up show' : 'swipe-up'; // 渲染完的 callback useEffect(() => { // 比較短的為高 const screenHeight = Math.min(window.screen.width, window.screen.height); // 檢查 toolbar 是否顯示中 const onCheckSwipeUp = () => { const show = screenHeight - window.innerHeight > 40; // 設定 showSwipeUp = show setShowSwipeUp(show); } // 100 毫秒檢查一次 toolbar 是否顯示中 setInterval(onCheckSwipeUp, 100); // 下次 effect 執行前,清除 onCheckSwipeUp Interval,避免重複呼叫 Interval return () => { clearIntrerval(onCheckSwipeUp); } // 此參數決定 effect 調用時機 // 預設是每次渲染後都會執行 // [] 空陣列,表示只在此組件被載入時執行一次 }, []); // 渲染完的 callback useEffect(() => { const onResize = () => { // keep scroll to botoom at portrait mode. // 當在 landscape 時須設定為 0, 才能讓 swipe up div 在正確位置出現 const offsetY = isPortrait() ? 9999 : 0; window.requestAnimationFrame(() => { window.scrollTo(0, offsetY); }); } onResize(); // 監聽視窗尺寸變動事件 window.addEventListener('resize', onResize); // 下次 effect 執行前,清除 listener,避免重複監聽事件 return (() => { window.removeEventListener('resize', resize); }); // 此參數決定 effect 調用時機 // 預設是每次渲染後都會執行 // [] 空陣列,表示只在此組件被載入時執行一次 }, []); // 渲染結構 return ( <div> // 渲染 game-content(不重要) <GameCanvas className={gameContentClasses}/> // 渲染 swipe up 字樣 <div className={swipeUpClasses}> <span>swipe up</span> </div> </div> ); } // 渲染 App 至 root 這個節點(root 被定義在 index.html) render(<App />, document.getElementById('root')); ``` ::: :::info GameCanvas.js ```jsx= import React, { useEffect, useRef } from 'react'; const GameCanvas = ({ className }) => { const canvasRef = useRef(null); useEffect(() => { const onResize = () => { const canvas = canvasRef.current; const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; const ctx = canvas.getContext('2d'); ctx.scale(dpr, dpr); ctx.font = '48px serif'; ctx.fillText('Hello world', 10, 50); } onResize(); window.addEventListener('resize', onResize); return (() => { window.removeEventListener('resize', resize); }) }, []); return ( <canvas ref={canvasRef} className={className}></canvas> ) } export default GameCanvas; ``` ::: ### 幫助理解 useState ```jsx= import React, { useState } from 'react'; function Example() { // 宣告一個新的 state 變數,我們稱作為「count」。 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` 等價於 ```javascript= class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } } ``` 相關文章 [Tricky Solution](https://medium.com/@humanhighway/tips-about-how-to-make-a-landscape-fullscreen-webpage-in-ios-safari-2d6d0e94d324) [React useState](https://zh-hant.reactjs.org/docs/hooks-state.html) [React useEffect](https://zh-hant.reactjs.org/docs/hooks-effect.html) [React useRef](https://zh-hant.reactjs.org/docs/hooks-reference.html) [resize event](https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event) ###### tags: `Egret`