vue3 + ts + svg + ECharts 实现双十一数据大屏
bigegpt 2024-10-17 08:06 5 浏览
本篇文章就来介绍下如何使用 vue3 + ts + svg + ECharts 实现一个如下所示的双十一数据大屏页面:
创建项目
执行命令 npm create vue@latest 创建基于 Vite 构建的 vue3 项目,功能选择如下:
我选择使用 pnpm 安装项目依赖:pnpm i,各安装包的版本号可见于下图:
在 vite.config.ts 中添加配置,以便在项目启动时能自动打开浏览器:
typescript
复制代码
export default defineConfig({ // ... server: { open: true } })
现在,就可以通过 pnpm dev 启动新建的项目了。
大屏适配
大屏适配的方案有很多,比如 rem、vw 和 flex 布局等,我选择使用缩放(scale)的方式来适配大屏,因为该方案使用起来比较简单,也不用考虑第三方库的单位等问题。
假设设计稿的尺寸为 1920 * 1080px,为了保证效果,在大屏中放大时应该保持宽高比 designRatio 不变,designRatio 为 1920 / 1080 ≈ 1.78。放大的倍数 scaleRatio,可以分为以下 2 种情况计算:
- 当用于展示的设备屏幕的宽高比 deviceRatio 等于或小于设计稿的宽高比 designRatio 时,我们可以按照两者的宽度之比进行缩放,即让设计稿保持宽高比的情况下放大到与设备等宽,在高度上可能留有空白;
- 当设备屏幕的宽高比 deviceRatio 大于设计稿宽高比 designRatio 时,也就是说设备为超宽屏,scaleRatio 应该按照两者的高度之比决定,即让设计稿保持宽高比的情况下放大到与设备等高,在宽度上可能留有空白,所以还要做个居中布局。
具体代码我封装成了一个 hook:
// 屏幕适配,src\hooks\useScreenAdapt.ts
import _ from 'lodash'
import { onMounted, onUnmounted } from 'vue'
export default function useScreenAdapt(dWidth: number = 1920, dHeight: number = 1080) {
// 节流
const throttleAdjustZoom = _.throttle(() => {
AdjustZoom()
}, 1000)
onMounted(() => {
AdjustZoom()
// 响应式
window.addEventListener('resize', throttleAdjustZoom)
})
// 释放资源
onUnmounted(() => {
window.removeEventListener('resize', throttleAdjustZoom)
})
function AdjustZoom() {
// 设计稿尺寸及宽高比
const designWidth = dWidth
const designHeight = dHeight
const designRatio = designWidth / designHeight // 1.78
// 当前屏幕的尺寸及宽高比
const deviceWidth = document.documentElement.clientWidth
const devicHeight = document.documentElement.clientHeight
const deviceRatio = deviceWidth / devicHeight
// 计算缩放比
let scaleRatio = 1
// 如果当前屏幕的宽高比大于设计稿的,则以高度比作为缩放比
if (deviceRatio > designRatio) {
scaleRatio = devicHeight / designHeight
} else {
// 否则以宽度比作为缩放比
scaleRatio = deviceWidth / designWidth
}
document.body.style.transform = `scale(${scaleRatio}) translateX(-50%)`
}
}
最后是给 body 添加了 transform 属性,为了实现居中效果,还需要给 body 添加上相应样式:
/* \src\assets\base.css */
* {
box-sizing: border-box;
}
body {
position: relative;
margin: 0;
width: 1920px;
height: 1080px;
transform-origin: left top;
left: 50%;
background-color: black;
}
使用 lodash 实现节流
为避免改变屏幕尺寸时过于频繁触发 AdjustZoom,我借助 lodash 的 throttle 方法做了个节流,这就需要安装 lodash:pnpm add lodash。因为用到了 ts,如果直接引入使用 lodash 会遇到如下报错:
我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。提示里已经告诉了我们解决办法,就是去安装 @types/lodash:pnpm add -D @types/lodash,之后就能在 ts 文件中正常使用 lodash 了。
使用 svg
页面头部使用的就是一张 svg,样式中给 #top 添加绝对定位 position: absolute; ,目的在于开启一个单独的渲染层,以减少之后添加动画造成的回流损耗:
<template>
<main class="main-bg">
<div id="top"></div>
</main>
</template>
<style scoped>
#top {
position: absolute;
width: 100%;
height: 183px;
background-size: cover;
background-image: url(@/assets/imgs/top_bg.svg);
}
</style>
作为背景引入的 top_bg.svg 是我使用 Illustrator 绘制后导出的,绘制时注意做好图层的命名:
因为图层的名称会影响到导出的 svg 文件中元素的 id 名称。另外导出的 svg 文件中也可能存在一些中文命名或一些不必要的代码,我们可以自行修改:
添加动画及滤镜
使用 Illustrator 绘制的都是静态图形,现在我们以其中一个圆球为例,添加上平移的动画以及高斯模糊的滤镜:
<!-- top_bg.svg 部分代码 -->
<?xml version="1.0" encoding="UTF-8"?>
<svg id="top-bg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1920 183">
<defs>
<style>
#circle-1 {
opacity: 0;
transform: translate(800px, -18px) scale(0.5);
animation: circle-1-ani 1.8s ease-out forwards infinite;
}
@keyframes circle-1-ani {
90%,
100% {
opacity: 0.95;
transform: translate(600px, 80px) scale(1);
}
}
</style>
<filter id="blurMe">
<feGaussianBlur stdDeviation="2" />
</filter>
</defs>
<circle id="circle-1" class="cls-1" r="12.96" filter="url(#blurMe)" />
</svg>
动画使用 css 定义,可以直接写在 <defs> 里的 <style> 中。一旦用到 transform,那么圆的坐标系就会移动到圆的中心点,所以我将原本 <circle> 中的用于定义圆心坐标的 cx 和 cy 属性删除了,通过在 #circle-1 中直接使用 transform: translate(800px, -18px); 来定位圆的初始位置:
滤镜定义在 <defs> 里的 <filter> 中,使用的是高斯模糊 <feGaussianBlur>, stdDeviation 用于指定钟形(bell-curve),可以理解为模糊程度。在圆形 <circle> 上通过 filter 属性,传入滤镜的 id 应用滤镜。
使用 ECharts
安装
首先是安装 ECharts:pnpm add echarts。在 npm 的仓库搜索 echarts 可以看到其带有如下所示的 ts 标志:
说明它的库文件中已经包含了 .d.ts 文件:
所以不需要像上面使用 lodash 那样再去额外安装声明文件了。
封装组件
接着就可以封装 echarts 组件了。组件中只需要提供一个展示图表的 dom 容器 <div>,然后在 onMounted(确保可以获取到 dom 容器) 中创建一个 ECharts 实例 myChart,最后通过 myChart.setOption(option) 传入从父组件获取的图表实例的配置项以及数据 option:
<!-- src\components\BaseEChart.vue -->
<template>
<div ref="mainRef" :style="{ width: width, height: height }"></div>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts'
import { onMounted, onUnmounted, ref } from 'vue'
interface IProps {
width?: string
height?: string
chartOption: echarts.EChartsOption
}
const props = withDefaults(defineProps<IProps>(), {
width: '100%',
height: '100%'
})
const mainRef = ref(null)
let myChart: echarts.ECharts | null = null
onMounted(() => {
myChart = echarts.init(mainRef.value, 'dark', { renderer: 'svg' })
const option = props.chartOption
myChart.setOption(option)
})
onUnmounted(() => {
// 销毁 echart 实例,释放资源
myChart?.dispose()
})
</script>
使用示例
以左上角的“人均消费金额排名”柱状图为例,代码如下:
<!-- src\views\HomeView.vue -->
<template>
<main class="main-bg">
<div id="left-top">
<div class="title">人均消费金额排名</div>
<div class="sub-title">Ranking of per capita consumption amount</div>
<BaseEChart :chartOption="amountRankOption" />
</div>
</main>
</template>
<script setup lang="ts">
import BaseEChart from '@/components/BaseEChart.vue'
import { amountRankOption } from './config/amount-rank-option'
</script>
<style scoped>
#left-top {
position: absolute;
top: 130px;
left: 20px;
width: 500px;
height: 320px;
}
</style>
在页面引入 BaseEChart 后,传入定义好的 amountRankOption 即可:
// 人均消费金额排名柱状图配置
import * as echarts from 'echarts'
type EChartsOption = echarts.EChartsOption
export const amountRankOption: EChartsOption = {
grid: {
top: 20,
bottom: 50,
left: 40,
right: 40
},
xAxis: {
axisTick: {
show: false // 隐藏 x 坐标轴刻度
},
data: ['思明', '湖里', '集美', '同安', '海沧', '翔安']
},
yAxis: {
axisLabel: {
show: false // 隐藏 y 坐标轴刻度标签
},
splitLine: {
show: false // 隐藏平行于 x 轴的分隔线
}
},
series: [
{
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
barWidth: 20 // 设置柱形的宽度
}
]
}
至于剩下的图表的实现,只是配置不同而已,如有兴趣可以去该项目的 git 仓库查看。
数字滚动动画
最后添加成交额的数字滚动动画,用到了 countup.js,需要先安装: pnpm add countup.js。
使用时,直接 new CountUp() 生成 countUp,第 1 个参数为要添加动画的 dom 的 id,第 2 个参数为动画结束时显示的数字,还可以传入第 3 个参数 options 实现一些配置,比如设置前缀,小数点等。然后通过 countUp.start() 即可实现动画效果:
<!-- src\components\Digital.vue -->
<template>
<div>
<span class="t1">成交额</span>
<span id="amount" class="t2">150</span>
<span class="t1">亿</span>
</div>
</template>
<script lang="ts" setup>
import { CountUp } from 'countup.js'
import { onMounted } from 'vue'
onMounted(() => {
const countUp = new CountUp('amount', 150)
if (!countUp.error) {
countUp.start()
} else {
console.error(countUp.error)
}
})
</script>
原文链接:https://juejin.cn/post/7305434729527181322
相关推荐
- LangChain4j如何自定义文档转换器实现数据清洗?
-
LangChain4j提供了3种RAG(Retrieval-AugmentedGeneration,检索增强生成)实现,我们通常在原生或高级的RAG实现中,要对数据进行清洗,也就是将外接...
- Java 8 Stream API 详解(java stream.)
-
Java8StreamAPI详解一、概述在Java8中,StreamAPI是一个重要的新特性。它为处理集合(如List、Set等)中的元素提供了一种高效且富有表现力的方式。Str...
- Java修炼终极指南:185 使用 Stream 过滤嵌套集合
-
这是面试中的一个经典问题,通常从一个模型开始,如下所示(我们假设集合是一个List):publicclassAuthor{privatefinalStringname;pri...
- java8的stream使用小示例(java stream())
-
据JetBrains发布的2021年开发者生态系统调查,Java8在java使用的版本中仍然是当前最流行的版本。72%的专业开发人员使用Java8作为其在java开发中主要编程语言版本。现...
- Node.js Stream - 实战篇(node.js in action)
-
本文转自“美团点评技术团队”http://tech.meituan.com/stream-in-action.html背景前面两篇(基础篇和进阶篇)主要介绍流的基本用法和原理,本篇从应用的角度,介...
- Java Stream:集合处理的api(java 集合操作)
-
JavaStream流:高效集合处理的函数式编程利器一、什么是JavaStream?Java8引入的StreamAPI是一套用于处理集合数据的流式编程接口,通过函数式风格(无副作用的...
- 去除 List 中的重复元素,你知道几种实现方法?
-
去除List中重复元素,这在实际编程或面试中经常遇到,每个人都有习惯的写法吧,这里抛砖引玉,汇总了一些实现方案,开拓思路。准备数据假设数组中有10个数据,可能有重复,需要将重复的数据从数组中去掉。pu...
- Java开发者必看!Stream流式编程10个爆款技巧,让你代码优雅飞起
-
为什么你的Java代码总像拧巴的麻绳?掌握这10个Stream实战技巧,代码效率与优雅度将产生质的飞跃。以下案例均来自真实电商系统场景,带你感受流式编程的降维打击!一、过滤与映射组合拳(Filter...
- leetcode每日一题之存在重复元素(存在重复元素 iii)
-
题:给定一个整数数组,判断是否存在重复元素。如果存在一值在数组中出现至少两次,函数返回true。如果数组中每个元素都不相同,则返回false。比如:输入:[1,2,3,1]输出:true...
- 告别for循环!揭秘Stream API如何让你的代码简洁度提升300%
-
一、当传统循环遇上现代需求真实场景复现:某电商平台需要处理10万条订单数据,要求:筛选出金额>500的订单提取用户ID并去重统计VIP用户数量传统实现方案://常规写法Set<Long...
- Java中List去重的N种方法:从基础到优雅
-
Java中List去重的N种方法:从基础到优雅在日常的Java开发中,我们经常会遇到需要对List集合去重的情况。无论是为了清理重复的数据,还是为了优化算法性能,掌握多种去重方式都是一项非常实用的技能...
- Java Stream流没用过?常用高频方法
-
概念Stream流是Java8添加的以一种链式调用的方法处理数据,主要侧重于计算。具有以下相关特点代码简洁链式调用Stream常用方法1.将数组变为当作List操作String[]strArr=...
- 核医学专业名词索引(M-R)(核医学重点归纳)
-
M吗啡(morphia)埋藏式心律转复除颤器(implantablecardioverterdefibrillator,ICD)麦角骨化醇(VD2,calciferol)脉冲堆积(pulsepi...
- CodeMeter 新版发布(codesigner下载)
-
威步于2022年8月4日发布CodeMeter7.50及CodeMeter软件保护套装11.10,以下为新版内容。CodeMeterRuntime7.50StreamingSIMDExten...
- 世界上最小的五轴铣床Pocket NC(最小的五轴加工中心)
-
PocketNC,由MIT学生研制,还有说法是这款产品的设计者是来自美国蒙大拿州的一对极客夫妻。目前主要有两款产品:PocketNCV2-50,9000美元;PocketNCV2-10,60...
- 一周热门
- 最近发表
-
- LangChain4j如何自定义文档转换器实现数据清洗?
- Java 8 Stream API 详解(java stream.)
- Java修炼终极指南:185 使用 Stream 过滤嵌套集合
- java8的stream使用小示例(java stream())
- Node.js Stream - 实战篇(node.js in action)
- Java Stream:集合处理的api(java 集合操作)
- 去除 List 中的重复元素,你知道几种实现方法?
- Java开发者必看!Stream流式编程10个爆款技巧,让你代码优雅飞起
- leetcode每日一题之存在重复元素(存在重复元素 iii)
- 告别for循环!揭秘Stream API如何让你的代码简洁度提升300%
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)