今天分享的是我们在开发大屏可视化项目时,常用到的组件。
自适应组件
-
始终铺满全屏
效果图如下:
源码
// 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",