在开发数据大屏时最头疼的就是多分辨率适配问题,可行方案有rem方式、媒体查询、flex+百分比+vh、scale方式。这里采用scale方式,因为使用其它方式时,在使用echarts时,一些字体大小的配置参数无法转换大小,就会造成在低分辨下,字体太大不美观。使用scale方式开发时,我们在编写css样式时,就可以根据UI提供的设计稿直接写具体的数值,比如
css.header{
height: 110px;
width: 100%;
padding: 0 10px 4px 10px;
}
编写通用组件,自动将大屏页面进行scale放大缩小
screen-adpter.component.html
html<div class="tl-screen-adpter-container" [class.fit]="mode == 'fit'" [class.scrollY] = "mode == 'scrollY'" [class.scrollX] = "mode == 'scrollX'" [class.full] = "mode == 'full'">
<div class="tl-screen-adpter-scale" #datav *ngIf="!showEntity">
<ng-container *ngTemplateOutlet="inner"></ng-container>
</div>
<div #entity *ngIf="showEntity" class="tl-screen-adpter-entity">
<div class="tl-screen-adpter-scale" #datav>
<ng-container *ngTemplateOutlet="inner"></ng-container>
</div>
</div>
<ng-template #inner>
<div [style]="canvasStyle">
<ng-content></ng-content>
</div>
</ng-template>
</div>
screen-adpter.component.ts
tsimport { AfterViewInit, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from "@angular/core";
import { usePreviewFitScale, usePreviewFullScale, usePreviewScrollXScale, usePreviewScrollYScale } from "./previewScale";
type modeType = "fit" | "scrollY" | "scrollX" | "full"
@Component({
selector: 'tl-screen-adpter',
templateUrl: './screen-adpter.component.html',
styleUrls: ['./screen-adpter.component.less'],
host: {
class: "tl-screen-adpter"
}
})
export class TlScreenAdapterComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {
canvasStyle = {}
// * 默认缩放值
scale = {
width: 1,
height: 1,
}
scaleRef = {
width: 1,
height: 1,
}
showEntity = false
@Input()
mode: modeType = "fit"
@Input()
canvasConfig = {
width: 1920,
height: 1080,
}
@ViewChild("datav", { static: false })
previewRef: any
@ViewChild("entity", { static: false })
entityRef: any
unWindowResize: Function
ngOnChanges(changes: SimpleChanges): void {
if (changes.mode) {
const type = changes.mode.currentValue
this.showEntity = type === "scrollX" || type === "scrollY"
}
}
ngOnInit(): void {
}
ngAfterViewInit(): void {
this.initCanvas()
switch (this.mode) {
case "fit":
(() => {
const { calcRate, windowResize, unWindowResize } = usePreviewFitScale(
this.canvasConfig.width,
this.canvasConfig.height,
this.previewRef.nativeElement,
this.updateScaleRef
)
calcRate()
windowResize()
this.unWindowResize = unWindowResize
})()
break
case "scrollY":
(() => {
const { calcRate, windowResize, unWindowResize } = usePreviewScrollYScale(
this.canvasConfig.width,
this.canvasConfig.height,
this.previewRef.nativeElement,
scale => {
const dom = this.entityRef.nativeElement
dom.style.width = `${this.canvasConfig.width * scale.width}px`
dom.style.height = `${this.canvasConfig.height * scale.height}px`
this.updateScaleRef(scale)
}
)
calcRate()
windowResize()
this.unWindowResize = unWindowResize
})()
break
case "scrollX":
(() => {
const { calcRate, windowResize, unWindowResize } = usePreviewScrollXScale(
this.canvasConfig.width,
this.canvasConfig.height,
this.previewRef.nativeElement,
scale => {
const dom = this.entityRef.nativeElement
dom.style.width = `${this.canvasConfig.width * scale.width}px`
dom.style.height = `${this.canvasConfig.height * scale.height}px`
this.updateScaleRef(scale)
}
)
calcRate()
windowResize()
this.unWindowResize = unWindowResize
})()
break
case "full":
(() => {
const { calcRate, windowResize, unWindowResize } = usePreviewFullScale(
this.canvasConfig.width,
this.canvasConfig.height,
this.previewRef.nativeElement,
this.updateScaleRef
)
calcRate()
windowResize()
this.unWindowResize = unWindowResize
})()
break
}
}
ngOnDestroy(): void {
//window.removeEventListener('resize', this.onResize)
if (this.unWindowResize) {
this.unWindowResize()
}
}
/** 初始化画布容器大小 */
initCanvas() {
this.canvasStyle = {
position: 'relative' as const,
width: this.canvasConfig.width ? `${this.canvasConfig.width || 100}px` : '100%',
height: this.canvasConfig.height ? `${this.canvasConfig.height}px` : '100%',
}
}
updateScaleRef = (scale: { width: number; height: number }) => {
// 这里需要解构,保证赋值给scaleRef的为一个新对象
// 因为scale始终为同一引用
this.scaleRef = { ...scale }
}
}
previewScale.ts
封装了适配方案,提供如下几种方案
tsimport { throttle } from "lodash-es"
// * 屏幕缩放适配(两边留白)
export const usePreviewFitScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸(px)
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
// 当前屏幕宽高比
const currentRate = parseFloat(
(window.innerWidth / window.innerHeight).toFixed(5)
)
if (scaleDom) {
if (currentRate > baseProportion) {
// 表示更宽
scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5))
scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
} else {
// 表示更高
scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5))
scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
}
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
console.log("卸载监听");
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * X轴撑满,Y轴滚动条
export const usePreviewScrollYScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸(px)
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
if (scaleDom) {
scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5))
scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * Y轴撑满,X轴滚动条
export const usePreviewScrollXScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸(px)
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
height: 1,
width: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
if (scaleDom) {
scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5))
scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * 变形内容,宽高铺满
export const usePreviewFullScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
const calcRate = () => {
if (scaleDom) {
scale.width = parseFloat((window.innerWidth / width).toFixed(5))
scale.height = parseFloat((window.innerHeight / height).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
screen-adpter.component.less
less.tl-screen-adpter-container{
background-color: #152047;
position: relative;
height: 100vh;
width: 100vw;
&.fit{
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.tl-screen-adpter-scale{
transform-origin: center center;
}
}
&.scrollY{
overflow-x: hidden;
.tl-screen-adpter-scale{
transform-origin: left top;
}
}
&.scrollX{
overflow-y: hidden;
.tl-screen-adpter-scale{
transform-origin: left top;
}
}
&.full{
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.tl-screen-adpter-scale{
transform-origin: center center;
}
}
&.tl-screen-adpter-entity{
overflow: hidden;
}
}
在大屏使用组件使用组件
html<div class="screen preview">
<tl-screen-adpter mode="scrollY">
<div class="screen__container">
<div class="header-box">
<div class="header">
<div class="logo">
<img src="assets/images/logo.png" alt="">
<span>XXXX管理平台</span>
</div>
<div class="tile_level1"><span>大屏标题</span></div>
</div>
</div>
</div>
</tl-screen-adpter>
</div>


本文作者:千寻
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!