大屏可视化组件

今天分享的是我们在开发大屏可视化项目时,常用到的组件。

自适应组件
  • 始终铺满全屏

    效果图如下:

    在这里插入图片描述

    源码
    // full-screen-scale.js
    
    export default {
        data() {
            return {
                resizeFn: null,
            }
        },
        methods: {
            debounce(fn, delay) {
                let timer = null;
                return function (...args) {
                    timer = setTimeout(
                        () => {
                            typeof fn === "function" && fn.apply(null, args);
                            clearTimeout(timer);
                        },
                        delay > 0 ? delay : 100
                    );
                };
            }
        },
        mounted() {
            const documentWidth = document.body.offsetWidth;
            const ratio = documentWidth / 1920;
            if (documentWidth > 1920) {
                document.body.style.transform = `scale(${ratio}, ${ratio})`
            }
            this.resizeFn = this.debounce(function () {
                const documentWidth = document.body.offsetWidth;
                const ratio = documentWidth / 1920;
                if (documentWidth > 1920) {
                    document.body.style.transform = `scale(${ratio}, ${ratio})`
                }
            }, 200)
            window.addEventListener('resize',this.resizeFn);
        },
        beforeDestroy() {
            window.removeEventListener('resize', this.resizeFn);
        },
    }
    
    使用
    <template>
    	<div class="full-screen-scale">
    			<el-row class="listTop" :gutter="20">
    				<el-col :span="7">
    					<div class="leftTop"></div>
    				</el-col>
    				<el-col :span="10">
    					<div class="leftMiddle"></div>
    				</el-col>
    				<el-col :span="7">
    					<div class="leftFooter"></div>
    				</el-col>
    			</el-row>
    			<el-row class="listBottom" :gutter="20">
    				<el-col :span="9">
                        <div class="bottomLeft"></div>
                    </el-col>
    				<el-col :span="4">
                        <div class="bottomLeft"></div>
                    </el-col>
    				<el-col :span="4">
                        <div class="bottomLeft"></div>
                    </el-col>
    				<el-col :span="7">
                        <div class="bottomLeft"></div>
                    </el-col>
    			</el-row>
    		</div>
    </template>
    <script>
    import fullScreenScale from "@/mixins/full-screen-scale";
    
    export default {
    	mixins: [fullScreenScale],
    }
    </script>
    <style lang="less" scoped>
    .full-screen-scale {
        height: 100%;
        width: 100%;
        padding: 14px 20px 20px;
        background: #03044A;
        overflow: hidden;
        .listTop {
            height: 60%;
            display: flex;
            .el-col {
                height: 100%;
                width: 100%;
            }
            .leftTop {
                width: 100%;
                height: 100%;
                border: 1px solid #0D2451;
                background-color: #ebebeb;
            }
            .leftMiddle {
                width: 100%;
                height: 100%;
                border: 1px solid #0D2451;
                background-color: #ebebeb;
            }
            .leftFooter {
                width: 100%;
                height: 100%;
                border: 1px solid #0D2451;
                background-color: #ebebeb;
            }
        }
        .listBottom {
            height: 40%;
            display: flex;
            .el-col {
                height: 100%;
                width: 100%;
            }
            .bottomLeft {
                width: 100%;
                height: 100%;
                border: 1px solid #0D2451;
                background-color: #ebebeb;
            }
        }
    }
    </style>
    
  • 根据宽高比例,自适应

    效果图如下:

    在这里插入图片描述

    源码
    // scale-screen.vue
    
    <!--
     * @Author: duanfanchao
     * @description: 大屏自适应容器组件
    -->
    <template>
    	<div class="screen-wrapper" ref="screenWrapper" :style="wrapperStyle">
    		<slot></slot>
    	</div>
    </template>
    
    <script>
    /**
     * 防抖函数
     * @param {T} fn
     * @param {number} delay
     * @return
     */
    function debounce(fn, delay) {
    	let timer = null;
    	return function (...args) {
    		timer = setTimeout(
    			() => {
    				typeof fn === "function" && fn.apply(null, args);
    				clearTimeout(timer);
    			},
    			delay > 0 ? delay : 100
    		);
    	};
    }
    
    export default {
    	name: "VScaleScreen",
    	props: {
    		width: {
    			type: [String, Number],
    			default: 1920,
    		},
    		height: {
    			type: [String, Number],
    			default: 1080,
    		},
    		fullScreen: {
    			type: Boolean,
    			default: false,
    		},
    		autoScale: {
    			type: [Object, Boolean],
    			default: true,
    		},
    		delay: {
    			type: Number,
    			default: 500,
    		},
    		boxStyle: {
    			type: Object,
    			default: () => ({}),
    		},
    		wrapperStyle: {
    			type: Object,
    			default: () => ({}),
    		},
    	},
    	data() {
    		return {
    			currentWidth: 0,
    			currentHeight: 0,
    			originalWidth: 0,
    			originalHeight: 0,
    			onResize: null,
    			observer: null,
    		};
    	},
    	computed: {
    		screenWrapper() {
    			return this.$refs["screenWrapper"];
    		},
    	},
    	methods: {
    		initSize() {
    			return new Promise((resolve, reject) => {
    				//给父元素设置 overflow:hidden
    				this.screenWrapper.parentNode.style.overflow = "hidden";
    				this.screenWrapper.parentNode.scrollLeft = 0;
    				this.screenWrapper.parentNode.scrollTop = 0;
    
    				this.$nextTick(() => {
    					// region 获取大屏真实尺寸
    					if (this.width && this.height) {
    						this.currentWidth = this.width;
    						this.currentHeight = this.height;
    					} else {
    						this.currentWidth = this.screenWrapper.clientWidth;
    						this.currentHeight = this.screenWrapper.clientHeight;
    					}
    					// endregion
    					// region 获取画布尺寸
    					if (!this.originalHeight || !this.originalWidth) {
    						this.originalWidth = window.screen.width;
    						this.originalHeight = window.screen.height;
    					}
    					// endregion
    					resolve();
    				});
    			});
    		},
    		updateSize() {
    			if (this.currentWidth && this.currentHeight) {
    				this.screenWrapper.style.width = `${this.currentWidth}px`;
    				this.screenWrapper.style.height = `${this.currentHeight}px`;
    			} else {
    				this.screenWrapper.style.width = `${this.originalWidth}px`;
    				this.screenWrapper.style.height = `${this.originalHeight}px`;
    			}
    		},
    		handleAutoScale(scale) {
    			if (!this.autoScale) return;
    			const screenWrapper = this.screenWrapper;
    			const domWidth = screenWrapper.clientWidth;
    			const domHeight = screenWrapper.clientHeight;
    			const currentWidth = document.body.clientWidth;
    			const currentHeight = document.body.clientHeight;
    			screenWrapper.style.transform = `scale(${scale},${scale}) `;
    			let mx = Math.max((currentWidth - domWidth * scale) / 2, 0);
    			let my = Math.max((currentHeight - domHeight * scale) / 2, 0);
    			if (typeof this.autoScale === "object") {
    				// @ts-ignore
    				!this.autoScale.x && (mx = 0);
    				// @ts-ignore
    				!this.autoScale.y && (my = 0);
    			}
    			this.screenWrapper.style.margin = `${my}px ${mx}px`;
    		},
    		updateScale() {
    			const screenWrapper = this.screenWrapper;
    			// 获取真实视口尺寸
    			const currentWidth = document.body.clientWidth;
    			const currentHeight = document.body.clientHeight;
    			// 获取大屏最终的宽高onResize
    			const realWidth = this.currentWidth || this.originalWidth;
    			const realHeight = this.currentHeight || this.originalHeight;
    			// 计算缩放比例
    			const widthScale = currentWidth / realWidth;
    			const heightScale = currentHeight / realHeight;
    
    			// 若要铺满全屏,则按照各自比例缩放
    			if (this.fullScreen) {
    				screenWrapper.style.transform = `scale(${widthScale},${heightScale})`;
    				return false;
    			}
    			// 按照宽高最小比例进行缩放
    			const scale = Math.min(widthScale, heightScale);
    			this.handleAutoScale(scale);
    		},
    		initMutationObserver() {
    			const screenWrapper = this.screenWrapper;
    			const observer = (this.observer = new MutationObserver(() => {
    				this.onResize();
    			}));
    
    			observer.observe(screenWrapper, {
    				attributes: true,
    				attributeFilter: ["style"],
    				attributeOldValue: true,
    			});
    		},
    		clearListener() {
    			window.removeEventListener("resize", this.onResize);
    		},
    		addListener() {
    			window.addEventListener("resize", this.onResize);
    		},
    		clearStyle() {
    			const screenWrapper = this.screenWrapper;
    			screenWrapper.parentNode.style.overflow = "auto";
    
    			screenWrapper.style = "";
    		},
    		async resize() {
    			await this.initSize();
    			this.updateSize();
    			this.updateScale();
    		},
    	},
    	mounted() {
    		this.onResize = debounce(() => {
    			this.resize();
    		}, this.delay);
    		this.$nextTick(() => {
    			this.resize();
    			this.addListener();
    		});
    	},
    	beforeDestroy() {
    		this.clearListener();
    	},
    };
    </script>
      
    <style scoped>
    .screen-box {
    	overflow: hidden;
    	background-size: 100% 100%;
    	background: #000;
    	width: 100vw;
    	height: 100vh;
    }
    
    .screen-wrapper {
    	transition-property: all;
    	transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    	transition-duration: 500ms;
    	position: relative;
    	overflow: hidden;
    	z-index: 100;
    	transform-origin: left top;
    }
    </style>
    
    使用
    <template>
        <scale-screen :width="1920" :height="1080" class="scale-wrap">
    		<div class="bg"></div>
    	</scale-screen>
    </template>
    
    <script>
    import scaleScreen from "@/components/scale-screen/index.vue";
    
    export default {
        components: {
        	scaleScreen
        },
    }
    </script>
    
    <style lang="less" scoped>
    .scale-wrap {
        color: #d3d6dd;
        width: 1920px;
        height: 1080px;
        overflow: hidden;
        background-color: #03050C;
        .bg {
            width: 100%;
            height: 100%;
            padding: 16px 16px 10px 16px;
            box-sizing: border-box;
            background-color: #ebebeb;
        }
    }
    </style>
    
无缝滚动效果组件

vue-seamless-scroll 是一个用于实现无缝滚动效果的 Vue.js 插件,常用于创建水平或垂直的自动滚动视图。它可以使得内容循环滚动,并且无缝衔接,看起来像是一个连续的流动内容,适用于展示例如图片、广告条、新闻轮播等。

// 安装
npm install vue-seamless-scroll --save

// 使用
import VueSeamlessScroll from 'vue-seamless-scroll';

效果图如下:

在这里插入图片描述

样例
<template>
	<div class="container">
		<vue-seamless-scroll
			:class-option="scrollOptions"
			:data="notices"
			class="content"
		>
			<div class="item" v-for="item in notices" :key="item.name">
				Item {{ item.name }}
			</div>
		</vue-seamless-scroll>
	</div>
</template>
<script>
import VueSeamlessScroll from 'vue-seamless-scroll';

export default {
  name: 'App',
  components: {
    VueSeamlessScroll
  },
  data() {
    return {
      	notices: [
			{ name: "a1" },
			{ name: "a2" },
			{ name: "a3" },
			{ name: "a4" },
			{ name: "a5" },
			{ name: "a6" },
			{ name: "a7" },
			{ name: "a8" },
			{ name: "a9" },
		],
		scrollOptions: {
			step: 0.3, // 数值越大速度滚动越快
			limitMoveNum: 2, // 开始无缝滚动的数据量 this.dataList.length
			hoverStop: true, // 是否开启鼠标悬停stop
			direction: 1, // 0向下 1向上 2向左 3向右
			openWatch: true, // 开启数据实时监控刷新dom
			singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
			singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
			waitTime: 1000, // 单步运动停止的时间(默认值1000ms)
		},
    };
  }
};
</script>
<style lang="less" scoped>
.container {
    height: 400px;
    width: 600px;
    background: pink;
}
.item {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: lightblue;
  border: 1px solid #ccc;
  height: 80px;
}
</style>

版本:"vue": "2.6.14","vue-seamless-scroll": "^1.1.23",

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值