Compare commits
2 Commits
6a6414510e
...
6575b0be0f
| Author | SHA1 | Date | |
|---|---|---|---|
| 6575b0be0f | |||
| 18bf98aa16 |
@@ -1,67 +0,0 @@
|
|||||||
# UI 屏幕适配方案
|
|
||||||
|
|
||||||
## 原则
|
|
||||||
|
|
||||||
**不做响应式布局,只做等比缩放。** 天书的 UI 是为 1920×1080 画布设计的,所有屏幕统一等比缩放,不发散断点。
|
|
||||||
|
|
||||||
## 实施方案:`transform: scale()`
|
|
||||||
|
|
||||||
### 入口处加 3 行
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- index.html / editor/index.html -->
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
width: 100vw; height: 100vh; overflow: hidden;
|
|
||||||
}
|
|
||||||
#app {
|
|
||||||
width: 1920px; height: 1080px;
|
|
||||||
transform-origin: top left;
|
|
||||||
transform: scale(var(--scale));
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// main.ts — app mount 前
|
|
||||||
const scale = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
|
|
||||||
document.documentElement.style.setProperty('--scale', String(scale))
|
|
||||||
|
|
||||||
// resize 时更新
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
|
|
||||||
document.documentElement.style.setProperty('--scale', String(s))
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### 影响范围
|
|
||||||
|
|
||||||
| 影响的 | 说明 |
|
|
||||||
|--------|------|
|
|
||||||
| ✅ 所有 `px` 值 | 等比缩放,按钮/字号/间距全部生效 |
|
|
||||||
| ✅ SVG / Canvas 内部坐标 | 天然缩放,无需换算 |
|
|
||||||
| ⚠️ `position: fixed` | `transform` 非 `none` 的元素会创建新的坐标基准,`fixed` 定位可能"粘"在缩放容器内而非视口。**解决:** overlay 统一用 `position: absolute`,根容器设 `position: relative` |
|
|
||||||
|
|
||||||
### 兼容注意事项
|
|
||||||
|
|
||||||
| 属性 | 兼容性 |
|
|
||||||
|------|--------|
|
|
||||||
| `backdrop-filter` | Chromium bug: `transform` 父容器内可能失效。把 `backdrop-filter` 放到 `transform` 容器**外面**(比如直接在 `body` 上) |
|
|
||||||
| `getBoundingClientRect()` | 返回的是缩放前的逻辑坐标,需 `÷ scale` 得到实际屏幕坐标 |
|
|
||||||
| `window.innerWidth/Height` | 始终返回物理像素,用于计算 scale,内部逻辑坐标始终 = 设计尺寸 |
|
|
||||||
|
|
||||||
### 为什么不用 rem / clamp / media query
|
|
||||||
|
|
||||||
- **无断点布局需求** — 不是手机→平板→桌面三套排版,只是等比缩放一张画布
|
|
||||||
- **改动量** — `rem` 需要把 200+ 处 `px` 改为 `rem`,收益为零
|
|
||||||
- **创作者** — 后续页面新增不需要考虑响应式,直接按 1920×1080 画布设计
|
|
||||||
|
|
||||||
## 新增页面的适配清单
|
|
||||||
|
|
||||||
当需要新增一个全屏页面(如新弹窗/面板)时:
|
|
||||||
|
|
||||||
- [ ] 所有尺寸使用 `px`(不要用 `vw`/`vh`/`rem`)
|
|
||||||
- [ ] 弹窗用 `position: absolute`(不用 `fixed`)
|
|
||||||
- [ ] 弹窗的父容器设 `position: relative`
|
|
||||||
- [ ] 有 `backdrop-filter` 时,确认它在非 `transform` 的父容器内
|
|
||||||
- [ ] 设计画布按 1920×1080 基准
|
|
||||||
@@ -4,14 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>剧情编辑器 — 交互式电影游戏</title>
|
<title>剧情编辑器 — 交互式电影游戏</title>
|
||||||
<style>
|
<link rel="stylesheet" href="./style.css" />
|
||||||
html, body { width: 100vw; height: 100vh; margin: 0; overflow: hidden; background: #0a0a16; }
|
|
||||||
#editor-app {
|
|
||||||
width: 1920px; height: 1080px;
|
|
||||||
transform-origin: top left;
|
|
||||||
transform: scale(var(--scale));
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="editor-app"></div>
|
<div id="editor-app"></div>
|
||||||
|
|||||||
@@ -2,13 +2,6 @@ import { createApp } from 'vue'
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import EditorApp from './App.vue'
|
import EditorApp from './App.vue'
|
||||||
|
|
||||||
function applyScale() {
|
|
||||||
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
|
|
||||||
document.documentElement.style.setProperty('--scale', String(s))
|
|
||||||
}
|
|
||||||
applyScale()
|
|
||||||
window.addEventListener('resize', applyScale)
|
|
||||||
|
|
||||||
const app = createApp(EditorApp)
|
const app = createApp(EditorApp)
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.mount('#editor-app')
|
app.mount('#editor-app')
|
||||||
|
|||||||
@@ -4,14 +4,6 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>交互式电影游戏</title>
|
<title>交互式电影游戏</title>
|
||||||
<style>
|
|
||||||
html, body { width: 100vw; height: 100vh; margin: 0; overflow: hidden; background: #000; }
|
|
||||||
#app {
|
|
||||||
width: 1920px; height: 1080px;
|
|
||||||
transform-origin: top left;
|
|
||||||
transform: scale(var(--scale));
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -405,6 +405,10 @@ html, body {
|
|||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -2,13 +2,6 @@ import { createApp } from 'vue'
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
function applyScale() {
|
|
||||||
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
|
|
||||||
document.documentElement.style.setProperty('--scale', String(s))
|
|
||||||
}
|
|
||||||
applyScale()
|
|
||||||
window.addEventListener('resize', applyScale)
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
Reference in New Issue
Block a user