[{"data":1,"prerenderedAt":1270},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fscreen-orientation-api":316,"post-surround-\u002Fposts\u002Fscreen-orientation-api":1263},[4,12,21,29,37,47,57,67,77,85,96,104,112,121,129,137,147,155,164,173,181,188,195,207,215,224,236,241,247,254,260,266,272,279,286,291,298,304,310],{"path":5,"title":6,"description":7,"category":8,"tags":9},"\u002Fposts\u002Fgit-commands-cheatsheet","Git 常用指令與終端機筆記","2018 年整理的 Git 學習筆記，包含基本終端機指令、Git 操作、分支、reset、SourceTree、Vim 與一些常用設定。","notes",[10,11],"Git","版本控制",{"path":13,"title":14,"description":15,"category":16,"tags":17},"\u002Fposts\u002Fjs-prototype-inheritance","JavaScript 物件導向：建構函式、prototype 與繼承三步驟","整理 ES5 時代用建構函式加 prototype 手刻物件導向的完整流程——封裝屬性與方法、共用函式為什麼要掛在 prototype 上、以及繼承的三個步驟（call 借用母類別建構子、Object.create 接上原型鏈、修正 constructor 指向），並附上物件縮寫與計算屬性這兩個順便學到的 ES6 寫法。","frontend",[18,19,20],"JavaScript","物件導向","prototype",{"path":22,"title":23,"description":24,"category":16,"tags":25},"\u002Fposts\u002Fflexbox-notes","Flexbox 對齊與彈性尺寸筆記","記錄 Flexbox 的主軸與交錯軸對齊、flex-direction、flex-wrap，以及 flex-grow、flex-shrink、flex-basis 等常用屬性。",[26,27,28],"CSS","Flexbox","版面",{"path":30,"title":31,"description":32,"category":16,"tags":33},"\u002Fposts\u002Fscreen-orientation-api","screen.orientation：現在判斷與鎖定螢幕方向的做法","整理現在 JavaScript 判斷螢幕方向的方式，包括 screen.orientation、change 事件與 lock()，並說明 RWD 使用 matchMedia 或 CSS orientation 會更適合。",[18,34,35,36],"RWD","螢幕方向","PWA",{"path":38,"title":39,"description":40,"category":16,"tags":41},"\u002Fposts\u002Fpinia-vuex-state-management","Pinia \u002F Vuex 狀態管理及差異","紀錄 Vuex、Pinia 的狀態管理概念、差異與用法。",[42,43,44,45,46],"Pinia","Vuex","Vue3","狀態管理","Nuxt",{"path":48,"title":49,"description":50,"category":8,"tags":51},"\u002Fposts\u002Fpyenv-python-version-management","Python 多版本管理 pyenv","紀錄 macOS 與 Windows 使用 pyenv 管理 Python 版本的方式。",[52,53,54,55,56],"Python","pyenv","macOS","Windows","版本管理",{"path":58,"title":59,"description":60,"category":8,"tags":61},"\u002Fposts\u002Fnpm-publish-vue2-component","npm 發布流程","紀錄 Vue 2 元件的 npm 發布、dist-tag 與打包流程。",[62,63,64,65,66],"npm","Vue2","Vue CLI","套件發布","MIT",{"path":68,"title":69,"description":70,"category":8,"tags":71},"\u002Fposts\u002Fmobile-debugging-today","現在怎麼 debug 手機網頁：從裝一堆工具到幾乎不用裝","對照 2022 年那套 scoop、iTunes、remotedebug-ios-webkit-adapter 的繁瑣設定，整理現在偵錯手機網頁的做法：Android 無線偵錯、iOS 的實際選項、on-device console、Playwright 裝置模擬，以及本機 HTTPS 的處理。",[72,73,74,75,76],"debug","iOS","Android","Chrome DevTools","Playwright",{"path":78,"title":79,"description":80,"category":16,"tags":81},"\u002Fposts\u002Fplaywright-vs-cypress","E2E 測試現在選什麼：Playwright 與 Cypress 的實際差異","對照幾年前用 Cypress 的做法，說明 Playwright 為什麼變成多數新專案的預設選擇：真正的多瀏覽器支援、免費的平行執行、trace viewer 除錯，以及兩者在架構上的根本差異與各自仍然適合的場景。",[76,82,83,84],"Cypress","E2E","測試",{"path":86,"title":87,"description":88,"category":89,"tags":90},"\u002Fposts\u002Fjs-encoding-today","現在處理編碼：TextEncoder、Uint8Array 的 Base64 方法與 URLSearchParams","整理 JavaScript 現代編碼方式，使用 TextEncoder、TextDecoder 與 Uint8Array 處理 UTF-8 與 Base64，並說明 Base64URL、URLSearchParams 與 encodeURIComponent 的差異。","research",[91,92,93,94,95,18],"編碼","TextEncoder","Base64","URLSearchParams","Uint8Array",{"path":97,"title":98,"description":99,"category":16,"tags":100},"\u002Fposts\u002Fdart-sass-migration","Dart Sass 遷移筆記：node-sass 之後怎麼裝 Sass","node-sass 與 LibSass 已停止維護，現在改用 Dart Sass。整理 webpack 與 Vite 的安裝方式，以及為什麼不再像 node-sass 一樣容易遇到 Node.js 版本與原生編譯問題。",[101,102,103],"Sass","Webpack","Vite",{"path":105,"title":106,"description":107,"category":16,"tags":108},"\u002Fposts\u002Fdayjs-vue3-computed","day.js 搭配 Vue 3 的日期格式化","整理 Vue 3 日期格式化的常見做法，使用 day.js 取代舊專案常見的 moment.js，並以 method 或 computed 取代 Vue 2 filter，也補充原生 Intl.DateTimeFormat 的使用方式。",[44,109,110,111],"day.js","日期","Intl",{"path":113,"title":114,"description":115,"category":16,"tags":116},"\u002Fposts\u002Fes-modules-today","ES Module 現在的樣子：Node ESM、exports 與 import maps","整理現代 JavaScript ES Module 的常見用法，包括 Node.js 原生 ESM、package.json exports、瀏覽器 import maps、JSON import attributes，以及 Vite 動態載入的注意事項。",[117,118,119,120,103,18],"ES Module","Node.js","import maps","exports",{"path":122,"title":123,"description":124,"category":89,"tags":125},"\u002Fposts\u002Fstructured-clone","structuredClone()：終於有內建的深層拷貝","整理 JavaScript structuredClone() 的深層拷貝用法，說明 Date、Map、Set、循環參考的處理方式，以及 Function、class 與 Vue reactive Proxy 的注意事項。",[18,126,127,128],"structuredClone","深拷貝","Vue",{"path":130,"title":131,"description":132,"category":16,"tags":133},"\u002Fposts\u002Fdynamic-import","JavaScript 動態載入：import() 與 Vue 非同步元件","整理靜態 import、動態 import() 與 Vue defineAsyncComponent 的使用方式，並簡單補充 Vite import.meta.glob 的用途。",[18,134,135,103,136],"ES Modules","Vue 3","Code Splitting",{"path":138,"title":139,"description":140,"category":16,"tags":141},"\u002Fposts\u002Fscreen-wake-lock-api","Screen Wake Lock API：不用套件也能讓頁面保持亮著","整理 Screen Wake Lock API 的基本用法，說明 HTTPS、頁面可見性、切換分頁後重新取得 wake lock，以及 Vue／Nuxt 計時器使用時的注意事項。",[142,128,143,144,145,146],"Wake Lock","Nuxt4","行動裝置","計時器","Web API",{"path":148,"title":149,"description":150,"category":16,"tags":151},"\u002Fposts\u002Fhtml-meta-boilerplate-today","現在的 HTML meta 標籤怎麼寫","整理現在一個新頁面該有的 meta 標籤骨架——viewport 補上 viewport-fit=cover 因應瀏海與圓角螢幕、color-scheme 讓表單控制項跟著深色模式換色、theme-color 決定行動瀏覽器工具列顏色，以及基本的 SEO 與 Open Graph 標籤。",[152,153,154],"HTML","meta","SEO",{"path":156,"title":157,"description":158,"category":8,"tags":159},"\u002Fposts\u002Fdocker-node-postgres","把 Node + PostgreSQL 的 backend 裝進 Docker：Dockerfile、.dockerignore 與 compose","從「本機裝了就跑」到打包成容器的通用做法——Dockerfile 每一行的理由與 layer cache 為什麼決定了指令順序、.dockerignore 漏掉 node_modules 對原生模組是災難級的問題、容器之間為什麼不能用 localhost 互相找到對方，以及怎麼驗證資料真的寫進了 volume。",[160,161,118,162,163],"Docker","Docker Compose","PostgreSQL","Express",{"path":165,"title":166,"description":167,"category":16,"tags":168},"\u002Fposts\u002Fnuxt-server-proxy","Nuxt 3 \u002F 4 用 Server API 做代理與轉發","整理 Nuxt 3 與 Nuxt 4 使用 server\u002Fapi 轉發第三方 API 的方式，可以避免瀏覽器 CORS 問題、隱藏 API Key，也能在伺服器端加入快取。",[169,143,170,171,172],"Nuxt3","CORS","Nitro","API",{"path":174,"title":175,"description":176,"category":16,"tags":177},"\u002Fposts\u002Fnuxt4-project-setup","Nuxt 4 建立專案：和 Nuxt 3 有哪些不同","整理 Nuxt 4 建立專案的基本流程與目錄結構，並對照 Nuxt 3 的差異，包括 app\u002F 目錄、路徑別名、TypeScript，以及目前常用的 ESLint 與 Tailwind CSS 4 設定。",[143,44,178,179,180],"TypeScript","ESLint","Tailwind CSS 4",{"path":182,"title":183,"description":184,"category":16,"tags":185},"\u002Fposts\u002Fnuxt3-route-middleware","Nuxt 3 \u002F 4 的 Route Middleware：進頁面前先做判斷","整理 Nuxt 3 與 Nuxt 4 的 Route Middleware，用 defineNuxtRouteMiddleware 在進入頁面前檢查登入狀態，搭配 navigateTo 重新導向，並說明一般 middleware、global middleware 與 definePageMeta 的用法。",[169,143,186,187],"Middleware","驗證",{"path":189,"title":190,"description":191,"category":16,"tags":192},"\u002Fposts\u002Fnuxt-route-rules-redirect","Nuxt 3 \u002F 4 的 routeRules：重新導向與頁面設定","整理 Nuxt 3 與 Nuxt 4 的 routeRules 用法，可以依照不同網址設定重新導向、預渲染、SSR 與快取，並簡單對照 Nuxt 2 的舊做法。",[169,143,193,194],"routeRules","重新導向",{"path":196,"title":197,"description":198,"category":16,"tags":199},"\u002Fposts\u002Fios-iframe-google-login-blank-page","iOS iframe 串接 Google 登入出現空白頁：Popup 與頁面回收問題排查紀錄","記錄 iframe 內串接 Google 第三方登入時，部分 iOS 裝置因背景頁面遭到回收並重新載入，導致 Popup 登入流程中斷，以及最後透過 Redirect 分流與登入狀態接回機制處理的過程。",[73,200,201,202,203,204,205,206],"Safari","iframe","Google Login","GIS","OAuth","Popup","Redirect",{"path":208,"title":209,"description":210,"category":16,"tags":211},"\u002Fposts\u002Fios-login-tab-return-behavior","iOS 第三方登入後回不到原頁？分頁開啟關係與回頁行為實測","記錄 iOS Safari 與 Chrome 在多分頁第三方登入流程中，登入頁關閉後不一定回到原頁的實測，以及分頁開啟關係、window.close 與 window.focus 的限制。",[73,200,212,202,204,201,213,214],"Chrome","window.close","window.focus",{"path":216,"title":217,"description":218,"category":16,"tags":219},"\u002Fposts\u002Fgoogle-app-about-blank-placeholder","Google App 內建瀏覽器：about:blank 佔位頁被關閉的排查紀錄","記錄 iOS Google App 內建瀏覽器在非同步開啟外部頁面時，about:blank 佔位分頁被自動關閉，導致第三方登入或外部導轉失敗的排查與解法。",[73,220,221,222,204,223],"Google App","window.open","about:blank","WebView",{"path":225,"title":226,"description":227,"category":228,"tags":229},"\u002Fposts\u002Fclaude-code-codex-cli-init-basics","AI 相關紀錄 v1｜Claude Code \u002F Codex CLI 初始化與基本概念","從 Claude Code 與 Codex CLI 開始，整理 CLI、Coding Agent、Context、\u002Finit 與 CLAUDE.md／AGENTS.md 這幾個一開始最容易搞混的概念，以及在既有專案中的安裝與啟動方式。","ai",[230,231,232,233,234,235],"AI","Claude Code","Codex CLI","Coding Agent","CLAUDE.md","AGENTS.md",{"path":237,"title":238,"description":239,"category":228,"tags":240},"\u002Fposts\u002Fclaude-agents-md-project-guidelines","AI 相關紀錄 v2｜CLAUDE.md \u002F AGENTS.md 怎麼寫？專案規範與實際範例","整理 CLAUDE.md 與 AGENTS.md 的用途、適合記錄的專案規範，以及可直接參考的設定範例。",[230,231,232,234,235],{"path":242,"title":243,"description":244,"category":228,"tags":245},"\u002Fposts\u002Fclaude-codex-config-locations","AI 相關紀錄 v3｜Claude Code `.claude\u002F` \u002F Codex CLI 設定位置：全域與專案設定基本概要介紹","整理 Claude Code 與 Codex CLI 的使用者層級、專案層級設定位置，以及 rules、skills、agents 等目錄的用途。",[230,231,232,246],"設定檔",{"path":248,"title":249,"description":250,"category":228,"tags":251},"\u002Fposts\u002Fclaude-settings-codex-config","AI 相關紀錄 v4｜Claude Code settings \u002F Codex config 基本設定","整理 Claude Code settings.json 與 Codex config.toml 的設定層級、權限控制和常見基本設定。",[230,231,232,252,253],"settings.json","config.toml",{"path":255,"title":256,"description":257,"category":228,"tags":258},"\u002Fposts\u002Fclaude-codex-rules-structure","AI 相關紀錄 v5｜Rules：專案規則多了之後怎麼拆？","整理 Claude Code Rules 與 Codex AGENTS.md 的拆分方式，說明何時該拆、怎麼依目錄管理專案規則。",[230,231,232,259,235],"Rules",{"path":261,"title":262,"description":263,"category":228,"tags":264},"\u002Fposts\u002Fclaude-codex-slash-commands","AI 相關紀錄 v6｜Commands \u002F Slash Commands：CLI 裡的 `\u002F` 指令是什麼？","介紹 Claude Code 與 Codex CLI 的 Slash Commands，整理常用內建指令、自訂指令與 Skills 的差異。",[230,231,232,265],"Slash Commands",{"path":267,"title":268,"description":269,"category":228,"tags":270},"\u002Fposts\u002Fclaude-codex-skills","AI 相關紀錄 v7｜Skills：把重複工作流程整理起來","介紹 Claude Code 與 Codex Skills，整理適合保存成 Skill 的重複流程、基本結構與使用方式。",[230,231,232,271],"Skills",{"path":273,"title":274,"description":275,"category":228,"tags":276},"\u002Fposts\u002Fclaude-codex-agents-subagents","AI 相關紀錄 v8｜Agent \u002F Subagent：什麼時候真的需要分工？","整理 Agent、Subagent 與 Custom Agent 的用途，說明何時適合分工，以及它們和 Rules、Skills 的差異。",[230,231,232,277,278],"Agent","Subagent",{"path":280,"title":281,"description":282,"category":228,"tags":283},"\u002Fposts\u002Fmcp-basics","AI 相關紀錄 v9｜MCP：讓 AI 使用其他工具與資料","介紹 MCP 如何讓 AI 使用外部工具與資料，並以 Pencil MCP 說明實際用途、權限風險及與 Skill 的差異。",[230,284,231,232,285],"MCP","Pencil",{"path":287,"title":288,"description":289,"category":228,"tags":290},"\u002Fposts\u002Fai-coding-agent-concepts-summary","AI 相關紀錄 v10｜基本概念小結論","用一篇快速整理 CLAUDE.md、AGENTS.md、設定檔、Rules、Commands、Skills、Agent 與 MCP 的角色和差異。",[230,231,232,284,271,277],{"path":292,"title":293,"description":294,"category":228,"tags":295},"\u002Fposts\u002Fclaude-code-codex-model-selection","Claude Code 與 Codex：不同模型怎麼選？","比較 Claude Code 與 Codex 不同模型的速度、能力與成本取捨，依工作難度整理實用的選擇方向。",[230,231,296,297],"Codex","模型選擇",{"path":299,"title":300,"description":301,"category":228,"tags":302},"\u002Fposts\u002Fclaude-model-selection","Claude 模型簡單怎麼選？","整理 Claude 各模型在速度、能力與使用情境上的差異，提供從輕量工作到複雜任務的選擇方向。",[230,231,303,297],"Claude",{"path":305,"title":306,"description":307,"category":228,"tags":308},"\u002Fposts\u002Fcodex-model-selection","Codex 模型簡單怎麼選？","整理 Codex 各模型在速度、能力與使用情境上的差異，提供從簡單修改到大型端到端任務的選擇方向。",[230,296,309,297],"OpenAI",{"path":311,"title":312,"description":313,"category":228,"tags":314},"\u002Fposts\u002Fclaude-code-codex-cli-command-cheatsheet","Claude Code \u002F Codex CLI 常用指令速查表","整理 Claude Code 與 Codex CLI 的常用斜線指令、快捷操作與進階功能，方便需要時快速查找。",[230,231,232,265,315],"速查表",{"id":317,"title":31,"body":318,"category":16,"description":32,"draft":1252,"extension":1253,"image":1254,"meta":1255,"navigation":637,"path":30,"publishedAt":1256,"relatedExperiments":1257,"seo":1258,"stem":1259,"tags":1260,"updatedAt":1261,"__hash__":1262},"posts\u002Fposts\u002F20230301.screen-orientation-api.md",{"type":319,"value":320,"toc":1241},"minimark",[321,325,343,346,363,371,375,401,407,415,418,466,472,491,494,501,563,574,578,581,587,593,596,664,667,701,704,710,713,719,777,779,799,802,821,824,834,837,900,903,916,919,926,929,932,946,949,953,956,959,965,968,988,991,994,1101,1104,1107,1130,1133,1136,1158,1161,1176,1179,1197,1200,1207,1210,1237],[322,323,324],"p",{},"早年判斷與鎖定螢幕方向時，常看到：",[326,327,332],"pre",{"className":328,"code":329,"language":330,"meta":331,"style":331},"language-js shiki shiki-themes github-light github-dark-default","window.orientation\n","js","",[333,334,335],"code",{"__ignoreMap":331},[336,337,340],"span",{"class":338,"line":339},"line",1,[336,341,329],{"class":342},"szmH1",[322,344,345],{},"或各家瀏覽器自己實作的：",[326,347,349],{"className":328,"code":348,"language":330,"meta":331,"style":331},"screen.lockOrientation()\n",[333,350,351],{"__ignoreMap":331},[336,352,353,356,360],{"class":338,"line":339},[336,354,355],{"class":342},"screen.",[336,357,359],{"class":358},"sVjZL","lockOrientation",[336,361,362],{"class":342},"()\n",[322,364,365,366,370],{},"現在如果真的需要知道裝置目前的螢幕方向，可以使用標準的 ",[367,368,369],"strong",{},"Screen Orientation API","。",[372,373,374],"h2",{"id":374},"取得目前方向",[326,376,378],{"className":328,"code":377,"language":330,"meta":331,"style":331},"console.log(screen.orientation.type)\nconsole.log(screen.orientation.angle)\n",[333,379,380,391],{"__ignoreMap":331},[336,381,382,385,388],{"class":338,"line":339},[336,383,384],{"class":342},"console.",[336,386,387],{"class":358},"log",[336,389,390],{"class":342},"(screen.orientation.type)\n",[336,392,394,396,398],{"class":338,"line":393},2,[336,395,384],{"class":342},[336,397,387],{"class":358},[336,399,400],{"class":342},"(screen.orientation.angle)\n",[322,402,403,406],{},[333,404,405],{},"type"," 可能會得到：",[326,408,413],{"className":409,"code":411,"language":412,"meta":331},[410],"language-text","portrait-primary\nportrait-secondary\nlandscape-primary\nlandscape-secondary\n","text",[333,414,411],{"__ignoreMap":331},[322,416,417],{},"例如：",[326,419,421],{"className":328,"code":420,"language":330,"meta":331,"style":331},"if (screen.orientation.type.startsWith('portrait')) {\n  console.log('目前是直向')\n}\n",[333,422,423,445,460],{"__ignoreMap":331},[336,424,425,429,432,435,438,442],{"class":338,"line":339},[336,426,428],{"class":427},"sDgYj","if",[336,430,431],{"class":342}," (screen.orientation.type.",[336,433,434],{"class":358},"startsWith",[336,436,437],{"class":342},"(",[336,439,441],{"class":440},"sNbzA","'portrait'",[336,443,444],{"class":342},")) {\n",[336,446,447,450,452,454,457],{"class":338,"line":393},[336,448,449],{"class":342},"  console.",[336,451,387],{"class":358},[336,453,437],{"class":342},[336,455,456],{"class":440},"'目前是直向'",[336,458,459],{"class":342},")\n",[336,461,463],{"class":338,"line":462},3,[336,464,465],{"class":342},"}\n",[322,467,468,471],{},[333,469,470],{},"angle"," 則可以取得目前旋轉的角度：",[326,473,475],{"className":328,"code":474,"language":330,"meta":331,"style":331},"console.log(screen.orientation.angle)\n\u002F\u002F 0、90、180、270\n",[333,476,477,485],{"__ignoreMap":331},[336,478,479,481,483],{"class":338,"line":339},[336,480,384],{"class":342},[336,482,387],{"class":358},[336,484,400],{"class":342},[336,486,487],{"class":338,"line":393},[336,488,490],{"class":489},"sPyeA","\u002F\u002F 0、90、180、270\n",[372,492,493],{"id":493},"監聽方向改變",[322,495,496,497,500],{},"如果使用者旋轉手機，可以監聽 ",[333,498,499],{},"change","：",[326,502,504],{"className":328,"code":503,"language":330,"meta":331,"style":331},"screen.orientation.addEventListener('change', () => {\n  console.log(\n    '目前方向：',\n    screen.orientation.type\n  )\n})\n",[333,505,506,528,537,545,551,557],{"__ignoreMap":331},[336,507,508,511,514,516,519,522,525],{"class":338,"line":339},[336,509,510],{"class":342},"screen.orientation.",[336,512,513],{"class":358},"addEventListener",[336,515,437],{"class":342},[336,517,518],{"class":440},"'change'",[336,520,521],{"class":342},", () ",[336,523,524],{"class":427},"=>",[336,526,527],{"class":342}," {\n",[336,529,530,532,534],{"class":338,"line":393},[336,531,449],{"class":342},[336,533,387],{"class":358},[336,535,536],{"class":342},"(\n",[336,538,539,542],{"class":338,"line":462},[336,540,541],{"class":440},"    '目前方向：'",[336,543,544],{"class":342},",\n",[336,546,548],{"class":338,"line":547},4,[336,549,550],{"class":342},"    screen.orientation.type\n",[336,552,554],{"class":338,"line":553},5,[336,555,556],{"class":342},"  )\n",[336,558,560],{"class":338,"line":559},6,[336,561,562],{"class":342},"})\n",[322,564,565,566,569,570,573],{},"這比以前自己監聽 ",[333,567,568],{},"resize"," 或判斷 ",[333,571,572],{},"window.orientation"," 清楚很多。",[372,575,577],{"id":576},"如果只是做-rwd","如果只是做 RWD",[322,579,580],{},"如果只是想知道：",[582,583,584],"blockquote",{},[322,585,586],{},"現在畫面是直向還是橫向？",[322,588,589,590,370],{},"其實不一定需要 ",[333,591,592],{},"screen.orientation",[322,594,595],{},"CSS 直接使用：",[326,597,601],{"className":598,"code":599,"language":600,"meta":331,"style":331},"language-css shiki shiki-themes github-light github-dark-default","@media (orientation: portrait) {\n  \u002F* 直向 *\u002F\n}\n\n@media (orientation: landscape) {\n  \u002F* 橫向 *\u002F\n}\n","css",[333,602,603,624,629,633,639,654,659],{"__ignoreMap":331},[336,604,605,608,611,615,618,621],{"class":338,"line":339},[336,606,607],{"class":427},"@media",[336,609,610],{"class":342}," (",[336,612,614],{"class":613},"sPgVT","orientation",[336,616,617],{"class":342},": ",[336,619,620],{"class":613},"portrait",[336,622,623],{"class":342},") {\n",[336,625,626],{"class":338,"line":393},[336,627,628],{"class":489},"  \u002F* 直向 *\u002F\n",[336,630,631],{"class":338,"line":462},[336,632,465],{"class":342},[336,634,635],{"class":338,"line":547},[336,636,638],{"emptyLinePlaceholder":637},true,"\n",[336,640,641,643,645,647,649,652],{"class":338,"line":553},[336,642,607],{"class":427},[336,644,610],{"class":342},[336,646,614],{"class":613},[336,648,617],{"class":342},[336,650,651],{"class":613},"landscape",[336,653,623],{"class":342},[336,655,656],{"class":338,"line":559},[336,657,658],{"class":489},"  \u002F* 橫向 *\u002F\n",[336,660,662],{"class":338,"line":661},7,[336,663,465],{"class":342},[322,665,666],{},"JavaScript 也可以：",[326,668,670],{"className":328,"code":669,"language":330,"meta":331,"style":331},"const isPortrait = window.matchMedia(\n  '(orientation: portrait)'\n).matches\n",[333,671,672,691,696],{"__ignoreMap":331},[336,673,674,677,680,683,686,689],{"class":338,"line":339},[336,675,676],{"class":427},"const",[336,678,679],{"class":613}," isPortrait",[336,681,682],{"class":427}," =",[336,684,685],{"class":342}," window.",[336,687,688],{"class":358},"matchMedia",[336,690,536],{"class":342},[336,692,693],{"class":338,"line":393},[336,694,695],{"class":440},"  '(orientation: portrait)'\n",[336,697,698],{"class":338,"line":462},[336,699,700],{"class":342},").matches\n",[322,702,703],{},"可以簡單區分：",[326,705,708],{"className":706,"code":707,"language":412,"meta":331},[410],"RWD \u002F 版面切換\n→ CSS @media\n→ matchMedia()\n\n需要取得裝置方向、角度\n→ screen.orientation\n",[333,709,707],{"__ignoreMap":331},[372,711,712],{"id":712},"鎖定螢幕方向",[322,714,715,716,500],{},"Screen Orientation API 也提供 ",[333,717,718],{},"lock()",[326,720,722],{"className":328,"code":721,"language":330,"meta":331,"style":331},"try {\n  await screen.orientation.lock('landscape')\n} catch (error) {\n  console.log('無法鎖定螢幕方向')\n}\n",[333,723,724,731,749,760,773],{"__ignoreMap":331},[336,725,726,729],{"class":338,"line":339},[336,727,728],{"class":427},"try",[336,730,527],{"class":342},[336,732,733,736,739,742,744,747],{"class":338,"line":393},[336,734,735],{"class":427},"  await",[336,737,738],{"class":342}," screen.orientation.",[336,740,741],{"class":358},"lock",[336,743,437],{"class":342},[336,745,746],{"class":440},"'landscape'",[336,748,459],{"class":342},[336,750,751,754,757],{"class":338,"line":462},[336,752,753],{"class":342},"} ",[336,755,756],{"class":427},"catch",[336,758,759],{"class":342}," (error) {\n",[336,761,762,764,766,768,771],{"class":338,"line":547},[336,763,449],{"class":342},[336,765,387],{"class":358},[336,767,437],{"class":342},[336,769,770],{"class":440},"'無法鎖定螢幕方向'",[336,772,459],{"class":342},[336,774,775],{"class":338,"line":553},[336,776,465],{"class":342},[322,778,417],{},[326,780,782],{"className":328,"code":781,"language":330,"meta":331,"style":331},"await screen.orientation.lock('portrait')\n",[333,783,784],{"__ignoreMap":331},[336,785,786,789,791,793,795,797],{"class":338,"line":339},[336,787,788],{"class":427},"await",[336,790,738],{"class":342},[336,792,741],{"class":358},[336,794,437],{"class":342},[336,796,441],{"class":440},[336,798,459],{"class":342},[322,800,801],{},"或：",[326,803,805],{"className":328,"code":804,"language":330,"meta":331,"style":331},"await screen.orientation.lock('landscape')\n",[333,806,807],{"__ignoreMap":331},[336,808,809,811,813,815,817,819],{"class":338,"line":339},[336,810,788],{"class":427},[336,812,738],{"class":342},[336,814,741],{"class":358},[336,816,437],{"class":342},[336,818,746],{"class":440},[336,820,459],{"class":342},[322,822,823],{},"但要注意：",[582,825,826],{},[322,827,828,830,831,833],{},[333,829,718],{}," 的瀏覽器支援與使用限制，比單純讀取 ",[333,832,592],{}," 多很多。",[322,835,836],{},"有些瀏覽器需要先進入全螢幕模式，才能鎖定方向：",[326,838,840],{"className":328,"code":839,"language":330,"meta":331,"style":331},"async function enterLandscapeMode(element) {\n  await element.requestFullscreen()\n\n  await screen.orientation.lock(\n    'landscape'\n  )\n}\n",[333,841,842,861,873,877,887,892,896],{"__ignoreMap":331},[336,843,844,847,850,853,855,859],{"class":338,"line":339},[336,845,846],{"class":427},"async",[336,848,849],{"class":427}," function",[336,851,852],{"class":358}," enterLandscapeMode",[336,854,437],{"class":342},[336,856,858],{"class":857},"sjlk-","element",[336,860,623],{"class":342},[336,862,863,865,868,871],{"class":338,"line":393},[336,864,735],{"class":427},[336,866,867],{"class":342}," element.",[336,869,870],{"class":358},"requestFullscreen",[336,872,362],{"class":342},[336,874,875],{"class":338,"line":462},[336,876,638],{"emptyLinePlaceholder":637},[336,878,879,881,883,885],{"class":338,"line":547},[336,880,735],{"class":427},[336,882,738],{"class":342},[336,884,741],{"class":358},[336,886,536],{"class":342},[336,888,889],{"class":338,"line":553},[336,890,891],{"class":440},"    'landscape'\n",[336,893,894],{"class":338,"line":559},[336,895,556],{"class":342},[336,897,898],{"class":338,"line":661},[336,899,465],{"class":342},[322,901,902],{},"所以一般網頁不要假設：",[326,904,906],{"className":328,"code":905,"language":330,"meta":331,"style":331},"screen.orientation.lock()\n",[333,907,908],{"__ignoreMap":331},[336,909,910,912,914],{"class":338,"line":339},[336,911,510],{"class":342},[336,913,741],{"class":358},[336,915,362],{"class":342},[322,917,918],{},"一定能成功。",[322,920,921,922,925],{},"實際使用時要用 ",[333,923,924],{},"try...catch"," 處理失敗。",[372,927,928],{"id":928},"解除鎖定",[322,930,931],{},"如果之前成功鎖定方向，可以使用：",[326,933,935],{"className":328,"code":934,"language":330,"meta":331,"style":331},"screen.orientation.unlock()\n",[333,936,937],{"__ignoreMap":331},[336,938,939,941,944],{"class":338,"line":339},[336,940,510],{"class":342},[336,942,943],{"class":358},"unlock",[336,945,362],{"class":342},[322,947,948],{},"讓裝置恢復正常的自動旋轉行為。",[372,950,952],{"id":951},"什麼情況才需要-lock","什麼情況才需要 lock()？",[322,954,955],{},"一般網站其實很少需要強制鎖定方向。",[322,957,958],{},"比較常見的是：",[326,960,963],{"className":961,"code":962,"language":412,"meta":331},[410],"Web 遊戲\n全螢幕播放器\n電子看板\n需要固定橫向操作的工具\nPWA\n",[333,964,962],{"__ignoreMap":331},[322,966,967],{},"普通的 RWD 網頁通常只需要：",[326,969,971],{"className":598,"code":970,"language":600,"meta":331,"style":331},"@media (orientation: portrait) {}\n",[333,972,973],{"__ignoreMap":331},[336,974,975,977,979,981,983,985],{"class":338,"line":339},[336,976,607],{"class":427},[336,978,610],{"class":342},[336,980,614],{"class":613},[336,982,617],{"class":342},[336,984,620],{"class":613},[336,986,987],{"class":342},") {}\n",[322,989,990],{},"不用主動去鎖使用者的螢幕方向。",[372,992,993],{"id":993},"舊寫法與現在的差異",[995,996,997,1013],"table",{},[998,999,1000],"thead",{},[1001,1002,1003,1007,1010],"tr",{},[1004,1005,1006],"th",{},"想做的事",[1004,1008,1009],{},"舊寫法",[1004,1011,1012],{},"現在",[1014,1015,1016,1031,1045,1064,1075,1088],"tbody",{},[1001,1017,1018,1022,1026],{},[1019,1020,1021],"td",{},"取得裝置方向",[1019,1023,1024],{},[333,1025,572],{},[1019,1027,1028],{},[333,1029,1030],{},"screen.orientation.type",[1001,1032,1033,1036,1040],{},[1019,1034,1035],{},"取得旋轉角度",[1019,1037,1038],{},[333,1039,572],{},[1019,1041,1042],{},[333,1043,1044],{},"screen.orientation.angle",[1001,1046,1047,1049,1057],{},[1019,1048,493],{},[1019,1050,1051,1054,1055],{},[333,1052,1053],{},"orientationchange"," \u002F ",[333,1056,568],{},[1019,1058,1059,1061,1062],{},[333,1060,592],{}," 的 ",[333,1063,499],{},[1001,1065,1066,1069,1072],{},[1019,1067,1068],{},"RWD 判斷",[1019,1070,1071],{},"CSS media query",[1019,1073,1074],{},"一樣使用 CSS media query",[1001,1076,1077,1080,1083],{},[1019,1078,1079],{},"JavaScript 判斷版面方向",[1019,1081,1082],{},"自己計算寬高",[1019,1084,1085],{},[333,1086,1087],{},"matchMedia()",[1001,1089,1090,1093,1096],{},[1019,1091,1092],{},"鎖定方向",[1019,1094,1095],{},"各家非標準 API",[1019,1097,1098],{},[333,1099,1100],{},"screen.orientation.lock()",[372,1102,1103],{"id":1103},"小結",[322,1105,1106],{},"如果只是做 RWD：",[326,1108,1110],{"className":598,"code":1109,"language":600,"meta":331,"style":331},"@media (orientation: portrait) {\n}\n",[333,1111,1112,1126],{"__ignoreMap":331},[336,1113,1114,1116,1118,1120,1122,1124],{"class":338,"line":339},[336,1115,607],{"class":427},[336,1117,610],{"class":342},[336,1119,614],{"class":613},[336,1121,617],{"class":342},[336,1123,620],{"class":613},[336,1125,623],{"class":342},[336,1127,1128],{"class":338,"line":393},[336,1129,465],{"class":342},[322,1131,1132],{},"就夠了。",[322,1134,1135],{},"如果 JavaScript 需要知道目前畫面方向：",[326,1137,1139],{"className":328,"code":1138,"language":330,"meta":331,"style":331},"window.matchMedia(\n  '(orientation: portrait)'\n).matches\n",[333,1140,1141,1150,1154],{"__ignoreMap":331},[336,1142,1143,1146,1148],{"class":338,"line":339},[336,1144,1145],{"class":342},"window.",[336,1147,688],{"class":358},[336,1149,536],{"class":342},[336,1151,1152],{"class":338,"line":393},[336,1153,695],{"class":440},[336,1155,1156],{"class":338,"line":462},[336,1157,700],{"class":342},[322,1159,1160],{},"如果需要更完整的裝置方向資訊：",[326,1162,1164],{"className":328,"code":1163,"language":330,"meta":331,"style":331},"screen.orientation.type\nscreen.orientation.angle\n",[333,1165,1166,1171],{"__ignoreMap":331},[336,1167,1168],{"class":338,"line":339},[336,1169,1170],{"class":342},"screen.orientation.type\n",[336,1172,1173],{"class":338,"line":393},[336,1174,1175],{"class":342},"screen.orientation.angle\n",[322,1177,1178],{},"真的需要鎖定方向時才使用：",[326,1180,1181],{"className":328,"code":804,"language":330,"meta":331,"style":331},[333,1182,1183],{"__ignoreMap":331},[336,1184,1185,1187,1189,1191,1193,1195],{"class":338,"line":339},[336,1186,788],{"class":427},[336,1188,738],{"class":342},[336,1190,741],{"class":358},[336,1192,437],{"class":342},[336,1194,746],{"class":440},[336,1196,459],{"class":342},[322,1198,1199],{},"而且要記得：",[582,1201,1202],{},[322,1203,1204,1206],{},[333,1205,718],{}," 並不是一般網頁在所有瀏覽器都能直接使用，必須做好失敗處理。",[372,1208,1209],{"id":1209},"參考資料",[1211,1212,1213,1223,1230],"ul",{},[1214,1215,1216],"li",{},[1217,1218,1222],"a",{"href":1219,"rel":1220},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FScreen_Orientation_API",[1221],"nofollow","MDN：Screen Orientation API",[1214,1224,1225],{},[1217,1226,1229],{"href":1227,"rel":1228},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FScreenOrientation\u002Flock",[1221],"MDN：ScreenOrientation.lock()",[1214,1231,1232],{},[1217,1233,1236],{"href":1234,"rel":1235},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002F@media\u002Forientation",[1221],"MDN：orientation",[1238,1239,1240],"style",{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}",{"title":331,"searchDepth":462,"depth":462,"links":1242},[1243,1244,1245,1246,1247,1248,1249,1250,1251],{"id":374,"depth":393,"text":374},{"id":493,"depth":393,"text":493},{"id":576,"depth":393,"text":577},{"id":712,"depth":393,"text":712},{"id":928,"depth":393,"text":928},{"id":951,"depth":393,"text":952},{"id":993,"depth":393,"text":993},{"id":1103,"depth":393,"text":1103},{"id":1209,"depth":393,"text":1209},false,"md",null,{},"2023-03-01",[],{"title":31,"description":32},"posts\u002F20230301.screen-orientation-api",[18,34,35,36],"2026-09-08","tHAXsWq53xmOPDfiXe040EZoa6HfimBqEzsR6sj0cNI",[1264,1267],{"title":39,"path":38,"stem":1265,"description":40,"category":16,"publishedAt":1266,"children":-1},"posts\u002F20240505.pinia-vuex-state-management","2024-05-05",{"title":23,"path":22,"stem":1268,"description":24,"category":16,"publishedAt":1269,"children":-1},"posts\u002F20201025.flexbox-notes","2020-10-25",1791786901592]