这个也是工作中已经实现过了,拿出来讲解一下,因为是公司项目,所以项目源代码就不分享了,现在看一下实现过程和echarts各配置
正式效果:

动态效果:(点击下面图片新窗口查看gif效果)

1、实时统计动画效果实现方法:
主要是使用了NPM插件,因为有成熟的,就不必在这个上面浪费时间了
npm i vue-count-to
然后使用页面给予起始值和结束值,每次接口更新时,只需更新结束值即可
import countTo from “vue-count-to”;
<countTo :startVal=’起始值’ :endVal=’结束值’></countTo>
2、保费收入占比动画效果实现方法:
这里主要是利用了echarts的饼状图,通过各属性设置达到该效果,配置如下
timePie = null;// 构建饼状myChartPie: any;drawLine_pie(item: any, list: any) {let _data: any = [];let colors = [{ color: "#ff0000" },{ color: "#ff9c00" },{ color: "#ffd200" },{ color: "#00b4ff" },{ color: "#0066ff" },];list.forEach((item: any, index: number) => {_data.unshift({value: item.ratio,name: item.scope,itemStyle: { color: colors[index].color },});});this.myChartPie = echarts.init(item as HTMLDivElement);let _myChart = this.myChartPie;let _option = {top: {bottom: "20px",containLabel: true,},legend: {bottom: -5,itemWidth: 10,itemHeight: 10,selectedMode: false,textStyle: {color: "#6381AE",fontSize: "12px",},},series: [{name: "保费收入占比",type: "pie",radius: ["45%", "68%"],height: "98%",labelLine: {length2: 5,},label: {color: "auto",formatter: "{d}%\n",},labelLayout: function (params) {const isLeft = params.labelRect.x < _myChart.getWidth() / 2;const points = params.labelLinePoints;// Update the end point.points[2][0] = isLeft? params.labelRect.x: params.labelRect.x + params.labelRect.width;return {labelLinePoints: points,};},emphasis: {label: {show: true,fontSize: "13px",},},data: _data,},],};_myChart.setOption(_option);}timePie = null; // 构建饼状 myChartPie: any; drawLine_pie(item: any, list: any) { let _data: any = []; let colors = [ { color: "#ff0000" }, { color: "#ff9c00" }, { color: "#ffd200" }, { color: "#00b4ff" }, { color: "#0066ff" }, ]; list.forEach((item: any, index: number) => { _data.unshift({ value: item.ratio, name: item.scope, itemStyle: { color: colors[index].color }, }); }); this.myChartPie = echarts.init(item as HTMLDivElement); let _myChart = this.myChartPie; let _option = { top: { bottom: "20px", containLabel: true, }, legend: { bottom: -5, itemWidth: 10, itemHeight: 10, selectedMode: false, textStyle: { color: "#6381AE", fontSize: "12px", }, }, series: [ { name: "保费收入占比", type: "pie", radius: ["45%", "68%"], height: "98%", labelLine: { length2: 5, }, label: { color: "auto", formatter: "{d}%\n", }, labelLayout: function (params) { const isLeft = params.labelRect.x < _myChart.getWidth() / 2; const points = params.labelLinePoints; // Update the end point. points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width; return { labelLinePoints: points, }; }, emphasis: { label: { show: true, fontSize: "13px", }, }, data: _data, }, ], }; _myChart.setOption(_option); }
3、订单排行效果实现方法:
这里主要是利用了echarts的柱状图,通过各属性设置达到该效果,配置如下
myChartBar: any;drawLine_bar(item: any, list: any) {let _y: any = [];let _s: any = [];const colors = [{ color: "#ff0000" },{ color: "#ff9c00" },{ color: "#ffd200" },{ color: "#afff48" },{ color: "#00b4ff" },{ color: "#0066ff" },];list.forEach((item: any, index: number) => {_y.unshift({value: item.org_name,textStyle: {color: "#81ACD2",},});_s.unshift({value: item.ratio,itemStyle: { color: colors[index].color },});});this.myChartBar = echarts.init(item as HTMLDivElement);let _myChart = this.myChartBar;let _option = {grid: {top: "20px",left: "50px",right: "50px",bottom: "20px",containLabel: true,},xAxis: {show: false,},yAxis: {type: "category",inverse: true,data: _y,axisLabel: {formatter: function (params: any) {if (params.length > 4) {return params.substr(0, 4);} else {return params;}},},axisTick: {show: false,},axisLine: {lineStyle: {color: "#1C68DA",},},animationDuration: 300,animationDurationUpdate: 300,},series: [{realtimeSort: true,type: "bar",label: {show: true,position: "right",color: "#B9B4B4",formatter: "{c}",},barWidth: 10,data: _s,},],animationDuration: 0,animationDurationUpdate: 2500,animationEasing: "linear",animationEasingUpdate: "linear",};_myChart.setOption(_option);}myChartBar: any; drawLine_bar(item: any, list: any) { let _y: any = []; let _s: any = []; const colors = [ { color: "#ff0000" }, { color: "#ff9c00" }, { color: "#ffd200" }, { color: "#afff48" }, { color: "#00b4ff" }, { color: "#0066ff" }, ]; list.forEach((item: any, index: number) => { _y.unshift({ value: item.org_name, textStyle: { color: "#81ACD2", }, }); _s.unshift({ value: item.ratio, itemStyle: { color: colors[index].color }, }); }); this.myChartBar = echarts.init(item as HTMLDivElement); let _myChart = this.myChartBar; let _option = { grid: { top: "20px", left: "50px", right: "50px", bottom: "20px", containLabel: true, }, xAxis: { show: false, }, yAxis: { type: "category", inverse: true, data: _y, axisLabel: { formatter: function (params: any) { if (params.length > 4) { return params.substr(0, 4); } else { return params; } }, }, axisTick: { show: false, }, axisLine: { lineStyle: { color: "#1C68DA", }, }, animationDuration: 300, animationDurationUpdate: 300, }, series: [ { realtimeSort: true, type: "bar", label: { show: true, position: "right", color: "#B9B4B4", formatter: "{c}", }, barWidth: 10, data: _s, }, ], animationDuration: 0, animationDurationUpdate: 2500, animationEasing: "linear", animationEasingUpdate: "linear", }; _myChart.setOption(_option); }
4、地图和金币飞行效果实现方法:
金币是利用了echarts的航线效果,地图背景是设计师给的一张图,然后做到和我echarts控件地图一样大,一开始也是想用echarts的控件绘制,后来发现就算是绘制两层,也无法实现设计师的效果,所以干脆用一样大的图片做背景,然后echarts地图组件只需构造坐标和航线即可,然后金币进钱的声音更加简单了,增加一个是否播放按钮,开启后如果有单进执行播放即可,默认不播放(因为需要手动触发才会有声音)
// 地图构造myChartMap: any;async drawLine_map(item: any, list: any) {const res: any = await axios.get("/chinaMap.json");echarts.registerMap("china", res.data);this.myChartMap = echarts.init(item as HTMLDivElement, "none", {renderer: "svg",});let _myChart = this.myChartMap;const gb = "image://" + require("../assets/latlon.png");const jb = "image://" + require("../assets/jb.gif");const jb_dh = "image://" + require("../assets/jb_dh.png");const effect = {type: "effectScatter",coordinateSystem: "geo",animation: false, //坐标点是否显示动画symbol: gb,symbolSize: 14,data: list.map((item) => {return {name: item.name,value: item.value,itemStyle: {normal: {color: "#ffff00",},},label: {show: false,color: "rgb(255, 255, 0)",formatter: "{b}",offset: [20, 0],fontSize: "10",},};}),};let _option = {geo: {map: "china",zoom: 1.68,top: "31.5%",label: {show: true,fontSize: "8",color: "#28a7e1",},itemStyle: {borderColor: "#1085AC",areaColor: "#1a439a",},show: false,silent: true,},tooltip: {backgroundColor: "#0c1a4f",borderColor: "#2ca3ff",textStyle: {color: "#aaddff",},position: "right",className: "echarts-tooltip",triggerOn: "none",padding: [5, 10],fontSize: 12,formatter: function (params) {return params.name;},},series: [effect],};_myChart.setOption(_option);var index = 0;this.timeMap = setInterval(function () {_myChart.dispatchAction({type: "showTip",seriesIndex: 0,dataIndex: index,});index++;if (index > list.length) {index = 0;}}, 3000);let isOne = true;let gold = [];let changeMap = () => {let _kz = {};if (isOne) {isOne = false;api.post("A" + this.system + "010", { count: this.OrderNumber }).then((data) => {gold = data;_kz = {series: [effect,{type: "scatter",coordinateSystem: "geo",symbol: jb,symbolSize: 25,data: gold.map((item) => {return {name: "1",value: item,itemStyle: {normal: {color: "#ffff00",opacity: 1,},},};}),},],};_myChart.setOption(_kz);}).finally(()=>{if (gold.length > 0) {this.timeMapOut = setTimeout(() => {changeMap();}, 3000);} else {isOne = true;this.timeMapOut = setTimeout(() => {changeMap();}, 5000);}});} else {isOne = true;this.changeMapData().then((data) => {_kz = {series: [effect,{type: "lines",symbolSize: 10,effect: {show: true,period: 1,trailLength: 0,symbol: jb_dh,symbolSize: 25,loop: false,},lineStyle: {color: "#2d9df1", //航线的颜色width: 1,opacity: 0,curveness: 0.2, // 弯曲程度},data: gold.map((item) => {return {fromName: "1",toName: "金币",coords: [item, [87.68, 51.77]],value: [87.68, 51.77],};}),},],};_myChart.setOption(_kz);//更新计数setTimeout(() => {//播放音效if (this.isAudio) {this.audio.play();}this.OrderNumber = data;this.toOrderNum(this.OrderNumber);this.setNumberTransform();}, 1000);}).finally(()=>{this.timeMapOut = setTimeout(() => {changeMap();}, 5000);});}};this.timeMapOut = setTimeout(() => {changeMap();}, 5000);}// 地图构造 myChartMap: any; async drawLine_map(item: any, list: any) { const res: any = await axios.get("/chinaMap.json"); echarts.registerMap("china", res.data); this.myChartMap = echarts.init(item as HTMLDivElement, "none", { renderer: "svg", }); let _myChart = this.myChartMap; const gb = "image://" + require("../assets/latlon.png"); const jb = "image://" + require("../assets/jb.gif"); const jb_dh = "image://" + require("../assets/jb_dh.png"); const effect = { type: "effectScatter", coordinateSystem: "geo", animation: false, //坐标点是否显示动画 symbol: gb, symbolSize: 14, data: list.map((item) => { return { name: item.name, value: item.value, itemStyle: { normal: { color: "#ffff00", }, }, label: { show: false, color: "rgb(255, 255, 0)", formatter: "{b}", offset: [20, 0], fontSize: "10", }, }; }), }; let _option = { geo: { map: "china", zoom: 1.68, top: "31.5%", label: { show: true, fontSize: "8", color: "#28a7e1", }, itemStyle: { borderColor: "#1085AC", areaColor: "#1a439a", }, show: false, silent: true, }, tooltip: { backgroundColor: "#0c1a4f", borderColor: "#2ca3ff", textStyle: { color: "#aaddff", }, position: "right", className: "echarts-tooltip", triggerOn: "none", padding: [5, 10], fontSize: 12, formatter: function (params) { return params.name; }, }, series: [effect], }; _myChart.setOption(_option); var index = 0; this.timeMap = setInterval(function () { _myChart.dispatchAction({ type: "showTip", seriesIndex: 0, dataIndex: index, }); index++; if (index > list.length) { index = 0; } }, 3000); let isOne = true; let gold = []; let changeMap = () => { let _kz = {}; if (isOne) { isOne = false; api .post("A" + this.system + "010", { count: this.OrderNumber }) .then((data) => { gold = data; _kz = { series: [ effect, { type: "scatter", coordinateSystem: "geo", symbol: jb, symbolSize: 25, data: gold.map((item) => { return { name: "1", value: item, itemStyle: { normal: { color: "#ffff00", opacity: 1, }, }, }; }), }, ], }; _myChart.setOption(_kz); }).finally(()=>{ if (gold.length > 0) { this.timeMapOut = setTimeout(() => { changeMap(); }, 3000); } else { isOne = true; this.timeMapOut = setTimeout(() => { changeMap(); }, 5000); } }); } else { isOne = true; this.changeMapData().then((data) => { _kz = { series: [ effect, { type: "lines", symbolSize: 10, effect: { show: true, period: 1, trailLength: 0, symbol: jb_dh, symbolSize: 25, loop: false, }, lineStyle: { color: "#2d9df1", //航线的颜色 width: 1, opacity: 0, curveness: 0.2, // 弯曲程度 }, data: gold.map((item) => { return { fromName: "1", toName: "金币", coords: [item, [87.68, 51.77]], value: [87.68, 51.77], }; }), }, ], }; _myChart.setOption(_kz); //更新计数 setTimeout(() => { //播放音效 if (this.isAudio) { this.audio.play(); } this.OrderNumber = data; this.toOrderNum(this.OrderNumber); this.setNumberTransform(); }, 1000); }).finally(()=>{ this.timeMapOut = setTimeout(() => { changeMap(); }, 5000); }); } }; this.timeMapOut = setTimeout(() => { changeMap(); }, 5000); }
5、地图上的累计出函笔数计数效果实现:
这个效果就是手动实现的,没什么好的插件,
<li :class="{'number-item': !isNaN(item) }" v-for="(item,index) in orderNum" :key="index"><span v-if="!isNaN(item)"><i ref="numberItem">0123456789</i></span><span class="comma" v-else>{{item}}</span></li>// 订单滚动orderNum = ["0", "0", "0", "0", "0", "0"];OrderNumber = 823123;setNumberTransform() {let numberItems = this.$refs.numberItem; // 拿到数字的ref,计算元素数量let numberArr: any = this.orderNum.filter((item: any) => !isNaN(item));// 结合CSS 对数字字符进行滚动,显示订单数量for (let index = 0; index < (numberItems as any).length; index++) {const elem = numberItems[index];elem.style.transform = `translate(-50%, -${numberArr[index] * 10}%)`;}}// 处理总订单数字toOrderNum(num) {num = num.toString();// 把订单数变成字符串if (num.length < 6) {num = "0" + num; // 如未满八位数,添加"0"补位this.toOrderNum(num); // 递归添加"0"补位} else if (num.length === 6) {// 订单数中加入逗号this.orderNum = num.split(""); // 将其便变成数据,渲染至滚动数组}}<li :class="{'number-item': !isNaN(item) }" v-for="(item,index) in orderNum" :key="index"> <span v-if="!isNaN(item)"> <i ref="numberItem">0123456789</i> </span> <span class="comma" v-else>{{item}}</span> </li> // 订单滚动 orderNum = ["0", "0", "0", "0", "0", "0"]; OrderNumber = 823123; setNumberTransform() { let numberItems = this.$refs.numberItem; // 拿到数字的ref,计算元素数量 let numberArr: any = this.orderNum.filter((item: any) => !isNaN(item)); // 结合CSS 对数字字符进行滚动,显示订单数量 for (let index = 0; index < (numberItems as any).length; index++) { const elem = numberItems[index]; elem.style.transform = `translate(-50%, -${numberArr[index] * 10}%)`; } } // 处理总订单数字 toOrderNum(num) { num = num.toString(); // 把订单数变成字符串 if (num.length < 6) { num = "0" + num; // 如未满八位数,添加"0"补位 this.toOrderNum(num); // 递归添加"0"补位 } else if (num.length === 6) { // 订单数中加入逗号 this.orderNum = num.split(""); // 将其便变成数据,渲染至滚动数组 } }
6、订单资讯这个用的是无缝滚动插件
npm i vue-seamless-scroll
7、成单率/活跃客户数量/近一年出函统计这些都是用的echarts现成的组件,改改颜色而已
总的来说,利用echarts实现难度不是很高,大部分效果都是echarts,吃透echarts就很简单
数据大屏这个看起来很难,实际操作没那么复杂,有一个设计师配合你,如果不是太酷炫的效果,都可以搞定