最近頭給安排了一個任務,做一個數據可視化頁放在公司展廳的數據大屏上展示。UI同學確定好大屏尺寸後分分鐘丟我一張 3000 * 1672 的設計稿。
屏幕適配問題
爲滿足展廳裏大屏的正常顯示,頁面編寫時肯定嚴格按照設計稿的尺寸來。但這種將尺寸固定的寫法會存在很明顯的屏幕適配問題,在小尺寸的電腦屏幕或更大尺寸的大屏上會出現滾動條或者佔不滿屏的情況。
屏幕適配原理
要適配不同的屏幕,讓UI給我們出不同尺寸的設計稿明顯不現實。唯一的辦法就是讓我們的頁面像圖片一樣能自由縮放適配不同的容器。
鎖定屏幕寬高比
我們需要將內容在一屏裏全部展示出來。這就需要頁面的內容寬高能像圖片一樣自由縮放填滿整個屏幕。這種填滿整個屏幕的方式可以理解爲鎖定屏幕寬高比。即不管設計稿尺寸多少,在不同屏幕上顯現的內容寬高要與屏幕尺寸保持一致。這種方案在確定的大屏裏展示完美,在與設計稿寬高比不一致的屏幕裏則存在一些變形。
讓內容在任何屏幕下能實現一屏展示,鎖定屏幕寬高比的方案應該是最理想的了。如果boss還糾結在電腦屏幕上的細微變形,那我也只能連夜跑路了。 o(╥﹏╥)o
實現原理
讓內容進行縮放的實現非常簡單,我們將寫好的內容放在頂層的容器組件裏,然後對容器組件進行縮放即可。
<!-- 數據大屏容器組件 -->
<bi-container class="bi-container" ref="biContainer">
<div>
數據大屏內容
</div>
</bi-container>
鎖定屏幕稿寬高比實現
// 實現鎖定設計稿寬高比
let width = xxx // 設計稿(大屏) 寬度
let height = xxx // 設計稿(大屏) 高度
let screenWidth = xxxx // 屏幕視口 寬度
let screenHeight = xxxx // 屏幕視口 高度
// 計算 縮放比
let widthScaleRadio = screenWidth / originalWidth
let heightScaleRadio = screenHeight / originalHeight
// 對內容進行縮放
this.$refs.biContainer.style.transform = `scale(${widthScaleRadio }, ${heightScaleRadio })`
這裏有個知識點,當我們對元素進行縮放時,它默認的縮放基點在中心位置,我們需要將基點設置在元素左上角,並讓它fixed定位到屏幕左上角,這樣就能完美顯示,不會出現上圖出現的問題。
.bi-container{
position: fixed;
top: 0;
left: 0;
z-index: 999;
transform-origin: left top;
}
容器組件開發
數據大屏 容器組件主要包含兩個功能
- 初始化時對寬高進行縮放,鎖定屏幕寬高比,實現一屏顯示全部內容
- 監聽 resize 事件,當用戶對瀏覽器窗口進行縮放改變視口大小時對內容進行重新縮放,鎖定屏幕寬高比,保持一屏展示全部內容。
容器組件完整實現請戳這裏: 數據大屏容器組件倉庫
下面貼上部分核心代碼:
<!-- 傳遞數據大屏寬高 -->
<bi-container :options="{ width: 3000, height: 1672 }">
<!-- 使用圖片來模擬內容 -->
<img class="content" src="../assets/1.jpeg" alt="" />
</bi-container>
data() {
return {
width: 0, // 大屏真實寬度
height: 0, // 大屏真實高度
originalWidth: 0, // 窗口原始寬度
originalHeight: 0 // 窗口原始高度
};
},
async mounted() {
// 獲取相關尺寸數據
await this.initSize();
// 設置容器尺寸,讓容器尺寸與內容尺寸一致
this.updateSize();
// 設置容器縮放比例,實現內容一屏完整顯示
this.updateScale();
// 監聽 resize事件,實現頁面動態適配
window.addEventListener('resize', this.onResize);
},
beforeDestroy() {
// 頁面銷燬前 移除 resize 事件監聽
window.removeEventListener('resize', this.onResize);
},
// 獲取相關尺寸數據
initSize() {
return new Promise(resolve => {
// 使用 nextTick 確保容器中的內容渲染完成
this.$nextTick(() => {
// 獲取大屏真實尺寸
if (this.options.width && this.options.height) {
this.width = this.options.width;
this.height = this.options.height;
} else {
// 若未傳遞大屏真實尺寸,則獲取容器被內容撐滿後的尺寸 作爲大屏真實尺寸
this.width = this.$refs.biContainer.clientWidth;
this.height = this.$refs.biContainer.clientHeight;
}
// 獲取窗口原始尺寸
if (!this.originalWidth || !this.originalHeight) {
this.originalWidth = window.screen.width;
this.originalHeight = window.screen.height;
}
});
resolve();
});
},
// 設置容器尺寸,讓容器尺寸與內容尺寸一致
updateSize() {
if (this.width && this.height) {
this.$refs.biContainer.style.width = `${this.width}px`;
this.$refs.biContainer.style.height = `${this.height}px`;
}
},
// 設置容器縮放比例,實現內容一屏完整顯示
updateScale() {
// 屏幕視口存在認爲縮放,拖動,導致真實視口發生變化,這裏獲取真實的視口尺寸
const currentWidth = document.body.clientWidth;
const currentHeight = document.body.clientHeight;
// 獲取大屏最終寬高, 若未獲得大屏幕尺寸,則將屏幕視口原始尺寸作爲大屏最終寬高
const realWidth = this.width || this.originalWidth;
const realHeight = this.height || this.originalHeight;
// 計算寬高比
const widthScale = currentWidth / realWidth;
const heightScale = currentHeight / realHeight;
this.$refs.biContainer.style.transform = `scale(${widthScale}, ${heightScale})`;
},
// 監聽 resize 事件, 動態更新容器縮放比
async onResize() {
await this.initSize();
this.updateScale();
}