为您推荐:
精华内容
最热下载
问答
  • 5星
    5.37MB lildkdkdkjf 2021-08-26 13:40:09
  • 5星
    91.43MB daokedream 2021-02-09 15:30:35
  • 5星
    3.43MB weixin_44860200 2021-05-03 18:12:37
  • 近年来,数据可视化大屏的出现,掀起一番又一番的浪潮,众多企业主纷纷想要打造属于自己的 “酷炫吊炸天” 的霸道总裁大屏驾驶舱。今天为大家分享的是 【 销售指标 - 数据可视化大屏解决方案】。 话不多说,开始...

    目录

    精彩案例汇总

    效果展示

    1、首先看动态效果图 

    2、再看实时分片数据图

    3、丰富的主题样式

    一、 确定需求方案

    1、确定产品上线部署的屏幕LED分辨率

    2、功能模块

    3、部署方式 

    二、整体架构设计

    三、编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码)

    1、前端html代码 

    2、前端JS代码

    产品销量分析

     利润目标达成率

    3、后端python代码

    四、上线运行

    五、源码下载

    精彩案例汇总


    近年来,数据可视化大屏的出现,掀起一番又一番的浪潮,众多企业主纷纷想要打造属于自己的 “酷炫吊炸天” 的霸道总裁大屏驾驶舱。今天为大家分享的是 【 销售指标 - 数据可视化大屏解决方案】。

    之前小伙伴们建议我出一些视频课程来学习Echarts,这样可以更快上手,所以我就追星赶月的录制了精彩案例汇总 ,希望小伙伴们多多支持。 

    话不多说,开始分享干货,欢迎讨论!QQ微信同号: 6550523

    精彩案例汇总

    YYDatav的数据可视化《精彩案例汇总》_YYDataV的博客-CSDN博客

    效果展示

    1、首先看动态效果图 

    2、再看实时分片数据图

    3、丰富的主题样式

      

    一、 确定需求方案

    1、确定产品上线部署的屏幕LED分辨率

    1280px*768px,F11全屏后占满整屏无滚动条;其它分辨率屏幕可自适应显示。

    2、功能模块

    • 销量目标达成率

    • 利润目标达成率

    • 产品销量分析

    • 門店TOP

    • 3D 地图 总销量

    • 合同目标达成率

    • 成本目标达成率

    • 成本分析

    • 门店数量

    3、部署方式 

    • 基于免安装可执行程序:支持Windows、Linux、Mac等各种主流操作系统;将可执行程序exe复制到服务器上即可,无需其它环境依赖;
    • 观看方式:既可在服务器上直接观看程序界面,也可远程使用浏览器打开播放,支持Chrome浏览器、360浏览器等主流浏览器。

    二、整体架构设计

    1. 前端基于Echarts开源库设计,使用WebStorm编辑器;
    2. 后端基于Python Web实现,使用Pycharm编辑器;
    3. 数据传输格式:JSON;
    4. 数据源类型:目前已支持PostgreSQL、MySQL、Oracle、Microsoft SQL Server、SQLite、Excel表格等,还可以定制HTTP API接口方式或其它类型数据库。
    5. 数据更新方式:摒弃了前端页面定时拉取的方式(这种方式带来严重的资源浪费),采用后端数据实时更新,实时推送到前端展示;

    三、编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码)

    1、前端html代码 

    
    <body background="/myimg/starfield.jpg" style="color: rgb(230, 171, 84);">
    
          <div class="container_fluid">
                <div class="row_fluid" id="vue_app">
                      <div style="padding:0 0" class="col-xs-12 col-md-12">
                            <dv-decoration-1 style="height:4%;">
                            </dv-decoration-1>
                            <h3 id="container_h"></h3>
                      </div>
    
                      <div style="padding:0 0" class="col-xs-12 col-md-3">
                            <dv-border-box-1 style="height:29%;padding:0 0">
                                  <div style="height:100%;padding:5% 5% 5% 5%;" id="container_0_0"
                                        class="col-xs-12 col-md-6"></div>
                                  <div style="height:100%;padding:5% 5% 5% 5%; " id="container_0_1"
                                        class="col-xs-12 col-md-6"></div>
                            </dv-border-box-1>
    
                            <dv-border-box-1 style="height:29%;padding:0 0">
                                  <div style="height:100%;padding:5% 5% 5% 5%;" id="container_1"
                                        class="col-xs-12 col-md-12"></div>
                            </dv-border-box-1>
    
                            <dv-border-box-1 style="height:29%;padding:0 0">
                                  <div style="height:100%;padding:5% 5% 5% 5%;" id="container_2"
                                        class="col-xs-12 col-md-12"></div>
                            </dv-border-box-1>
                      </div>
    
                      <div style="padding:0 0" class="col-xs-12 col-md-6">
                            <dv-border-box-1 style="height:87%;padding:0 0" class="col-xs-12 col-md-12">
                                  <div style="height:100%;padding: 2% 2% 2% 2%" id="container_3"></div>
    
                                  <div style="position: absolute; z-index: 2; left:25%; top:10%; right: 25%; bottom: 20%; height:20%; width: 50%;  ">
                                        <h3 id="container_3_top_title"> </h3>
                                        <h1 id="container_3_top"></h1>
                                  </div>
    
                            </dv-border-box-1>
                      </div>
    
                      <div style="padding:0 0" class="col-xs-12 col-md-3">
                            <dv-border-box-1 style="height:29%;padding:0 0">
                                  <div style="height:100%;padding:5% 5% 5% 5%;" id="container_4_0"
                                        class="col-xs-12 col-md-6"></div>
                                  <div style="height:100%;padding:5% 5% 5% 5%; " id="container_4_1"
                                        class="col-xs-12 col-md-6"></div>
                            </dv-border-box-1>
    
                            <dv-border-box-1 style="height:29%;padding:0 0" class="col-xs-12 col-md-12">
                                  <div style="height:100%;padding:5% 5% 5% 5%;" id="container_5"></div>
                            </dv-border-box-1>
    
                            <dv-border-box-1 style="height:29%;padding:0 0" class="col-xs-12 col-md-12">
                                  <div style="height:100%;padding:5% 5% 5% 5%;" id="container_6"></div>
                            </dv-border-box-1>
                      </div>
    
                </div>
          </div>
    </body>
    

    2、前端JS代码

    产品销量分析

    // import * as echarts from 'echarts';
    var idContainer_1 = "container_1";
    var chartDom = document.getElementById(idContainer_1);
    
    function initEchart_1(){
      var myChart = echarts.init(chartDom, window.gTheme);
      var option = {
        title: {
          text: "产品销量分析",
          left: "left",
          textStyle: {
            color: "#3690be",
            fontSize: "10",
          },
        },
        tooltip: {
          trigger: "item",
          formatter: "{a} <br/>{b}: {c} 百万",
          position: function (p) {
            //其中p为当前鼠标的位置
            return [p[0] + 10, p[1] - 10];
          },
        },
        legend: {
          data: ["成本", "利润"],
          textStyle: {
              color: "rgba(255,255,255,.5)",
              fontSize: "10",
          },
          // 距离顶部边框的相对距离,太近压到了title
          top: "10%",
          // orient:"vertical"
        },
        grid: {
          left: "3%",
          right: "4%",
          bottom: "3%",
          // 距离顶部边框的相对距离,太近压到了legend
          top: "30%",
          containLabel: true,
        },
        xAxis: {
          name: '百万',
          type: "value",
          axisLabel: {
            textStyle: {
              color: "rgba(255,255,255,.5)",
              fontSize: 10,
            },
          },
          axisLine: {
            lineStyle: {
              color: "rgba(255,255,255,.2)",
            },
          },
          splitLine: {
            lineStyle: {
              color: "rgba(255,255,255,.1)",
            },
          },
        },
        yAxis: {
          type: "category",
          data: ["Mate", "P", "Nova", "畅享"],
          axisLabel: {
            textStyle: {
              color: "rgba(255,255,255,.5)",
              fontSize: 10,
            },
          },
          axisLine: {
            lineStyle: {
              color: "rgba(255,255,255,.2)",
            },
          },
          splitLine: {
            lineStyle: {
              color: "rgba(255,255,255,.1)",
            },
          },
        },
        series: [
          {
            name: "成本",
            type: "bar",
            stack: "total",
            label: {
              show: true,
            },
            emphasis: {
              focus: "series",
            },
            data: [334, 390, 330, 320],
          },
          {
            name: "利润",
            type: "bar",
            stack: "total",
            label: {
              show: true,
            },
            emphasis: {
              focus: "series",
            },
            data: [134, 90, 230, 210],
          },
        ],
      };
    
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
        window.addEventListener("resize", function () {
          myChart.resize();
        });
    }
    
    
    function asyncData_1() {
      $.getJSON("json/bar_stacked.json").done(function (data) {
        var myChart = echarts.init(document.getElementById(idContainer_1));
    
        myChart.setOption({
          series: [{ data: data }],
        });
      }); //end $.getJSON}
    }
    initEchart_1();
    asyncData_1();
    
    

     利润目标达成率

    var idContainer_0_1 = 'container_0_1'
    var chartDom_0_1 = document.getElementById(idContainer_0_1);
    
    function initEchart_0_1() {
    
        var myChart = echarts.init(chartDom_0_1, window.gTheme);
        var option = {
            title: {
                text: '利润目标达成率',
                left: 'left',
                textStyle: {
                    color: '#3690be',
                    fontSize: '10'
                }
            },
            series: [{
                type: 'gauge',
                progress: {
                    show: true,
                    width: 12
                },
                axisLine: {
                    lineStyle: {
                        width: 12
                    }
                },
                axisTick: {
                    show: false,
                },
                splitLine: {
                    show: false,
                },
                axisLabel: {
                    show: false,
                },
                pointer:{
                    show: false,
                },
                anchor:{
                    show: false,
                },
                title:{
                    show: false,
                },
                detail: {
                    valueAnimation: true,
                    offsetCenter: [0, 0],
                    formatter: '{value}%',
                },
                data: []
            }]
        };
         // 使用刚指定的配置项和数据显示图表。
         myChart.setOption(option);
         window.addEventListener("resize", function () {
             myChart.resize();
         });
    }
    
    function asyncData_0_1() {
        $.getJSON('json/gauge_benefit_rate.json').done(function (data) {
            var myChart = echarts.init(document.getElementById(idContainer_0_1));
    
            myChart.setOption({
                series: [{ data: data }],
            });
        });//end $.getJSON}
    }
    initEchart_0_1();
    asyncData_0_1();
    

    3、后端python代码

    # -*- coding:utf-8 -*-
    
    import io
    import os
    import sys
    import urllib
    import json
    from http.server import HTTPServer, SimpleHTTPRequestHandler, ThreadingHTTPServer
    
    ip = "localhost"   # 监听IP,配置项
    port = 8811       # 监听端口,配置项
    index_url = "http://%s:%d/index.html" %(ip, port)  # 监听主页url,配置项
    
    class MyRequestHandler(SimpleHTTPRequestHandler):
        protocol_version = "HTTP/1.0"
        server_version = "PSHS/0.1"
        sys_version = "Python/3.7.x"
        target = "./"  # 监听目录,配置项
    
        def do_GET(self):
            if self.path.find("/json/") > 0:
                print(self.path)
                req = {"success": "true"}
                self.send_response(200)
                self.send_header("Content-type", "json")
                self.end_headers()
                with open(self.path, 'r', encoding="utf-8") as f:
                    data = json.load(f)
                    rspstr = json.dumps(data)
                    self.wfile.write(rspstr.encode("utf-8"))
    
            else:
                SimpleHTTPRequestHandler.do_GET(self);
    
        def do_POST(self):
            if self.path == "/signin":
                print("postmsg recv, path right")
            else:
                print("postmsg recv, path error")
                data = self.rfile.read(int(self.headers["content-length"]))
                data = json.loads(data)
                self.send_response(200)
                self.send_header("Content-type", "text/html")
                self.end_headers()
                rspstr = "recv ok, data = "
                rspstr += json.dumps(data, ensure_ascii=False)
                self.wfile.write(rspstr.encode("utf-8"))
    
    
    def HttpServer():
        try:
            server = HTTPServer((ip, port), MyRequestHandler)
            listen = "http://%s:%d" %(ip, port)
            print("服务器监听地址: ", listen)
            server.serve_forever()
        except ValueError as e:
            print("Exception", e)
            server.socket.close()
    
    if __name__ == "__main__":
        HttpServer()

    四、上线运行

    五、源码下载

    11【源码】数据可视化:基于Echarts+Python实现的动态实时大屏范例-销售指标.zip-企业管理文档类资源-CSDN下载

    精彩案例汇总

    YYDatav的数据可视化《精彩案例汇总》_YYDataV的博客-CSDN博客

    本次分享结束,欢迎讨论!QQ微信同号: 6550523

    展开全文
    lildkdkdkjf 2021-08-25 14:03:47
  • 接下来就制作简单的大屏展示系统了。 制作步骤 一、确定需求功能 (1)图片可从StreamingAssets文件夹中自动读取 (2)加载出来的图片需要自动循环移动展示于大屏幕中 (3)可实现图片的多人点击放大、全屏拖拽、...

    TouchScript插件简介与下载

    在unity中,TouchScript非常适合做展示类大屏幕的多点触控,在AssetStore也是五星好评,还是免费资源,也可以在GitHub上下载对应的TouchScript。接下来就制作简单的大屏展示系统了。

    制作步骤

    一、确定需求功能
    (1)图片可从StreamingAssets文件夹中自动读取
    (2)加载出来的图片需要自动循环移动展示于大屏幕中
    (3)可实现图片的多人点击放大、全屏拖拽、自动归位等功能
    (4)实现图片放大之后大图周围图片缩小并且围绕大图旋转功能
    (5)CPU处理、GPU渲染无压力测试优化

    二、准备前期素材
    (1)下载并导入相应的插件TouchScript
    (2)准备LitJson.dll和System.Drawing.dll(文件流数据读取方式)
    (3)创建StreamingAssets并且导入需要展示的图片

    三、编程实现

    制作过程

    一、实现从StreamingAssets文件夹中获取所有图片,具体的代码如下:

    	List<Texture2D> textureList = new List<Texture2D>();//将获取的图片存于集合中
        int texturelistIndex = 0;
    
        string texpath; //获取本地图片的文件夹路径(自定义)
    
        void Start()
        {
            texpath = Application.streamingAssetsPath + "/testimage";
    
            StartCoroutine(LoadTextureFromFile());//获取对应文件夹的所有文件
        }
    
        /// <summary>
        /// 使用协程加载文件夹内所有图片文件
        /// </summary>
        /// <returns></returns>
        IEnumerator LoadTextureFromFile()
        {
            yield return null;
            List<FileInfo> fileInfos = GetAllFileFrom(texpath);//获取相应文件夹所有的图片文件流数据
    
            for (int i = 0; i < fileInfos.Count; i++)
            {
                LoadSingleTexture(fileInfos[i].FullName,fileInfos[i].Name);
            }
    
        }
    
        /// <summary>
        /// 将集合里的图片取出赋值到需要显示的RawImgae
        /// </summary>
        /// <returns></returns>
        private Texture2D GetTexture()
        {
            if (textureList.Count>0)
            {
                Texture2D newTexture2D = textureList[texturelistIndex];
                texturelistIndex += 1;
                if (texturelistIndex>=textureList.Count)
                {
                    texturelistIndex = 0;
                }
                return newTexture2D;
            }
            else
            {
                return null;
            }
        }
    
        /// <summary>
        /// 加载单张本地图片
        /// </summary>
        private void LoadSingleTexture(string imagepath,string imagename) 
        {
            FileStream filestream = new FileStream(imagepath,FileMode.Open,FileAccess.Read);
            filestream.Seek(0,SeekOrigin.Begin);
            byte[] bytes = new byte[filestream.Length];
            filestream.Read(bytes,0,(int)filestream.Length);
            System.Drawing.Image image = System.Drawing.Image.FromStream(filestream);
            filestream.Close();
            filestream.Dispose();
            filestream = null;
    
            int imageWidth=image.Width;
            int imageheight=image.Height;
    
            Texture2D tex2D = new Texture2D(1920,1080);
            tex2D.LoadImage(bytes);
            tex2D.name = imagename;
            textureList.Add(tex2D);
    
            bytes = null;
            filestream = null;
            image = null;
            tex2D = null;
    
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
    
        /// <summary>
        /// 得到相应文件夹下的所有文件夹内的所有文件的路径
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
    	private List<FileInfo> GetAllFileFrom(string filepath)
    	{
    		List<FileInfo> filesinfolist = new List<FileInfo>();
    		DirectoryInfo infoDir = new DirectoryInfo(filepath);
    		FileInfo[] filesinfo = infoDir.GetFiles("*", SearchOption.AllDirectories);//获取路径内所有文件信息
    
            for (int i = 0; i < filesinfo.Length; i++)
            {
                if (filesinfo[i].Name.Contains(".meta"))
                {
                    continue;
                }
                filesinfolist.Add(filesinfo[i]);
            }
            return filesinfolist;
    	}
    

    二、加载出图片,并自动循环移动展示于大屏幕
    (1)制作移动图片以及点击之后放大图片的预制体,用于接收从文件夹中获取的texture2D赋值以及展示
    在这里插入图片描述

    (2)首先要确定每次生成一列图片时间、的图片的行列数,以及移动的速度、方向,在此项目中我想使得图片每行移动的速度有一定的参差感,所以1,3,5行为一个速度,2,4,6行为一个速度。设置每次生成1,3,5行图片时间为2秒,2,4,6行图片时间为1.35秒,行列数为8行,移动方向为从左到右移动。
    相关的代码如下:

     	int row=8;//图片的行数
    
        float timer1=0;//计时器
        float timer2=0;//计时器
        float createTime1= 2f;//生成一列图片的时间间隔
        float createTime2= 1.35f;//生成一列图片的时间间隔
    
    
    	 void FixedUpdate()
        {
            if (isStartMove)
            {
                timer1 += Time.deltaTime;
                if (timer1>=createTime1)
                {
                    timer1 = 0;
                    GetNewImageItemObjSingleRow();
                }
    
    
                timer2 += Time.deltaTime;
                if (timer2 >= createTime2)
                {
                    timer2 = 0;
                    GetNewImageItemObjDoubleRow();
                }
            }
        }
        /// <summary>
        ///  //获取单行数小图
        /// </summary>
    
        public void GetNewImageItemObjSingleRow()
        {
            for (int i = 0; i < row; i++)
            {
                if (i%2!=0)
                {
                    GameObject image = GetNewMoveImageItem(startpos - new Vector3(0, (i - 1) * offset_y));
                    image.SetActive(true);
                    AddImageItemCompomemt(image);
                    image.GetComponent<ImageItem>().currentpos = startpos;
                    image.GetComponent<ImageItem>().targetTrans = startTran;
                    float speed = 2f;
                    image.GetComponent<ImageItem>().OnEnter(startpos - new Vector3(0, (i - 1) * offset_y), speed, GetTexture());
                }
            }
        }
        /// <summary>
        /// 获取双行数小图
        /// </summary>
        public void GetNewImageItemObjDoubleRow()
        {
            for (int i = 0; i < row; i++)
            {
                if (i % 2 == 0)
                {
                    GameObject image = GetNewMoveImageItem(startpos - new Vector3(0, (i - 1) * offset_y));
                    image.SetActive(true);
                    AddImageItemCompomemt(image);
                    image.GetComponent<ImageItem>().currentpos = startpos;
                    image.GetComponent<ImageItem>().targetTrans = startTran;
                    float speed = 3f;
                    image.GetComponent<ImageItem>().OnEnter(startpos - new Vector3(0, (i - 1) * offset_y), speed, GetTexture());
                }
            }
        }
        
     	/// <summary>
        /// 获取新的移动小图
        /// </summary>
        /// <returns></returns>
        public GameObject GetNewMoveImageItem(Vector3 pos)
        {
            GameObject newimageItem;
            if (newMoveImageQue.Count > 0)
            {
                newimageItem = newMoveImageQue.Dequeue();
    
            }
            else
            {
                newimageItem = Instantiate(moveImagePrefab, moveImageParent, false);
                newimageItem.transform.localPosition = pos;
            }
            return newimageItem;
        }
    
    	 /// <summary>
        /// 将小图展示于视图
        /// </summary>
        /// <param name="imageitem"></param>
        /// <param name="callback"></param>
        public void PushNewMoveImageItem(GameObject imageitem, Action callback)
        {
            if (newMoveImageQue.Contains(imageitem))
            {
                return;
            }
            newMoveImageQue.Enqueue(imageitem);
            if (callback != null)
            {
                callback();
            }
        }
     public void RegisterItem(GameObject obj, ImageType imageType)
        {
            if (imageType == ImageType.MoveImage)
            {
                if (!moveimagelist.Contains(obj.GetComponent<ImageItem>()))
                {
                    moveimagelist.Add(obj.GetComponent<ImageItem>());
                }
            }
            else if (imageType == ImageType.BigImage)
            {
                if (!bigImagelist.Contains(obj.GetComponent<BigImageItem>()))
                {
                    bigImagelist.Add(obj.GetComponent<BigImageItem>());
                }
            }
        }
    
        public void ReleaseItem(GameObject obj, ImageType imageType)
        {
            if (imageType == ImageType.MoveImage)
            {
                if (moveimagelist.Contains(obj.GetComponent<ImageItem>()))
                {
                    moveimagelist.Remove(obj.GetComponent<ImageItem>());
                }
            }
            else if (imageType == ImageType.BigImage)
            {
                if (bigImagelist.Contains(obj.GetComponent<BigImageItem>()))
                {
                    bigImagelist.Remove(obj.GetComponent<BigImageItem>());
                }
            }
    
        }
    

    三、实现图片的多人点击放大、全屏拖拽、自动归位等功能

    /// <summary>
    	/// 点击获取相应的大图
    	/// </summary>
    	public void GetBigImageItem()
        {
    		GameObject bigimage = Instantiate(bigimagePrefab, bigimageparent,false);
    		bigimage.GetComponent<RawImage>().texture = raw.texture;
    		bigimage.transform.localPosition = transform.localPosition + new Vector3(0,50,0);
    		bigimage.AddComponent<BigImageItem>();
    		bigimage.GetComponent<BigImageItem>().originalPos = this.transform.localPosition + new Vector3(0,50,0);
    		bigimage.GetComponent<BigImageItem>().targetTrans = targetTrans;
    		
    	}
    
      /// <summary>
        /// 当拖拽大图的手指抬起 大图归位
        /// </summary>
        private void LerpToOringinPos()
        {
            if (isCrossPos)
            {
                transform.localPosition = Vector3.Lerp(transform.localPosition, originalPos, 0.05f);
                if (Vector3.Distance(transform.localPosition, originalPos) < 50f)
                {
                    CloseCircle();
                }
            }
        }
    	
    

    四、实现图片放大之后大图周围图片缩小并且围绕大图旋转功能

     /// <summary>
        /// 判断是否满足环绕条件
        /// </summary>
        public void Rotation()
        {
            for (int i = 0; i < moveImageList.Count; i++)
            {
                if (GameManager.instance.IsInCircle(transform.localPosition, 300f, moveImageList[i].currentpos))
                {
                    //Debug.Log("在环绕条件中");
                    if (!moveImageList[i].bigImageItemlist.Contains(bigImage))
                    {
                        moveImageList[i].bigImageItemlist.Add(bigImage);
                        moveImageList[i].isInCircle = true;
                    }
                }
                else
                {
                    if (moveImageList[i].bigImageItemlist.Contains(bigImage))
                    {
                        moveImageList[i].bigImageItemlist.Remove(bigImage);
                        if (moveImageList[i].bigImageItemlist.Count <= 0)
                        {
                            moveImageList[i].isInCircle = false;
                        }
                    }
                }
            }
        }
    
    
    /// <summary>
    	/// 小图的环绕
    	/// </summary>
    	public void CirCleImage()
    	{
    		if (isInCircle)
    		{
    			Vector3 nor = (currentpos - bigImageItemlist[0].currentPos).normalized;
    			currentpos = bigImageItemlist[0].currentPos + nor * 300;
    			if (bigImageItemlist.Count > 0)
    			{
    				for (int i = 0; i < bigImageItemlist.Count; i++)
    				{
    					if (bigImageItemlist[i] == null)
    					{
    						bigImageItemlist.RemoveAt(i);
    					}
    				}
    				float scale = 0;
    				scale = 1 - bigImageItemlist.Count * 0.5f;
    				scale = scale >= 0f ? scale : 0f;
    				targetScale = Vector3.one * scale;
    			}
    		}
    		else
    		{
    			offsetpos = Vector3.zero;
    			targetScale = Vector3.one;
    		}
    	}
    
    /// <summary>
        /// 判断图片是否围绕大图旋转
        /// </summary>
        /// <param name="CirClepoint">大图的坐标点</param>
        /// <param name="R">图片旋转半径</param>
        /// <param name="point">小图的坐标点</param>
        /// <returns></returns>
        public bool IsInCircle(Vector2 CirClepoint, float R, Vector2 point)
        {
            float r = Mathf.Sqrt((point.x - CirClepoint.x) * (point.x - CirClepoint.x) + (point.y - CirClepoint.y) * (point.y - CirClepoint.y));
            return r <= R;
        }
    

    因为是一边写这篇文章一边进行代码的编写,还没有优化代码,以上的功能我把它集中于四个脚本中,如下:
    GameManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.UI;
    using System;
    
    /// <summary>
    /// 图片的状态(移动还是被点击放大)
    /// </summary>
    public enum ImageType
    { 
        Null,
        MoveImage,
        BigImage
    }
    
    public class GameManager : MonoBehaviour {
    
        public static GameManager instance;
        void Awake() 
        {
            instance = this;
        }
    
        List<Texture2D> textureList = new List<Texture2D>();//将获取的图片存于集合中
        int texturelistIndex = 0;
        string texpath; //获取本地图片的文件夹路径(自定义)
    
        int row=8;//图片的行数
    
        float timer1=0;//计时器
        float timer2=0;//计时器
        float createTime1= 2f;//生成一列图片的时间间隔
        float createTime2= 1.35f;//生成一列图片的时间间隔
    
        bool isStartMove=false;//是否加载完图片开始移动
        Vector3 startpos =new Vector3(-2200, 300);//此位置为第一行生成点,其他行以此类推
        float offset_y=120;
    
        Transform startTran;
        public List<ImageItem> moveimagelist = new List<ImageItem>(); //移动中的图片集合
        public List<BigImageItem> bigImagelist = new List<BigImageItem>();//被点击放大的图片集合
        public Queue<GameObject> newMoveImageQue = new Queue<GameObject>(); //加载出来的移动图片存于此集合,以便之后利用
    
        GameObject moveImagePrefab; //移动图片预制体
        Transform moveImageParent; //移动图片的父级
        void Start()
        {
            texpath = Application.streamingAssetsPath + "/testimage";
            moveImagePrefab = Resources.Load<GameObject>("imageItem");
            moveImageParent = GameObject.Find("Canvas/panel").transform;
            startTran = GameObject.Find("Canvas/startpos").transform;
            StartCoroutine(LoadTextureFromFile());
    
        }
    
        void FixedUpdate()
        {
            if (isStartMove)
            {
                timer1 += Time.deltaTime;
                if (timer1>=createTime1)
                {
                    timer1 = 0;
                    GetNewImageItemObjSingleRow();
                }
    
    
                timer2 += Time.deltaTime;
                if (timer2 >= createTime2)
                {
                    timer2 = 0;
                    GetNewImageItemObjDoubleRow();
                }
            }
        }
        /// <summary>
        ///  //获取单行数小图
        /// </summary>
    
        public void GetNewImageItemObjSingleRow()
        {
            for (int i = 0; i < row; i++)
            {
                if (i%2!=0)
                {
                    GameObject image = GetNewMoveImageItem(startpos - new Vector3(0, (i - 1) * offset_y));
                    image.SetActive(true);
                    AddImageItemCompomemt(image);
                    image.GetComponent<ImageItem>().currentpos = startpos;
                    image.GetComponent<ImageItem>().targetTrans = startTran;
                    float speed = 2f;
                    image.GetComponent<ImageItem>().OnEnter(startpos - new Vector3(0, (i - 1) * offset_y), speed, GetTexture());
                }
            }
        }
        /// <summary>
        /// 获取双行数小图
        /// </summary>
        public void GetNewImageItemObjDoubleRow()
        {
            for (int i = 0; i < row; i++)
            {
                if (i % 2 == 0)
                {
                    GameObject image = GetNewMoveImageItem(startpos - new Vector3(0, (i - 1) * offset_y));
                    image.SetActive(true);
                    AddImageItemCompomemt(image);
                    image.GetComponent<ImageItem>().currentpos = startpos;
                    image.GetComponent<ImageItem>().targetTrans = startTran;
                    float speed = 3f;
                    image.GetComponent<ImageItem>().OnEnter(startpos - new Vector3(0, (i - 1) * offset_y), speed, GetTexture());
                }
            }
        }
    
    
        /// <summary>
        /// 添加ImageItem脚本
        /// </summary>
        public void AddImageItemCompomemt(GameObject obj)
        {
            if (!obj.GetComponent<ImageItem>())
            {
                ImageItem item=obj.AddComponent<ImageItem>();
                moveimagelist.Add(item);
            }
            else
            {
                ImageItem item = obj.GetComponent<ImageItem>();
                moveimagelist.Add(item);
            }
        }
      
    
    
        /// <summary>
        /// 获取新的移动小图
        /// </summary>
        /// <returns></returns>
        public GameObject GetNewMoveImageItem(Vector3 pos)
        {
            GameObject newimageItem;
            if (newMoveImageQue.Count > 0)
            {
                newimageItem = newMoveImageQue.Dequeue();
    
            }
            else
            {
                newimageItem = Instantiate(moveImagePrefab, moveImageParent, false);
                newimageItem.transform.localPosition = pos;
            }
            return newimageItem;
        }
    
        /// <summary>
        /// 将小图展示于视图
        /// </summary>
        /// <param name="imageitem"></param>
        /// <param name="callback"></param>
        public void PushNewMoveImageItem(GameObject imageitem, Action callback)
        {
            if (newMoveImageQue.Contains(imageitem))
            {
                return;
            }
            newMoveImageQue.Enqueue(imageitem);
            if (callback != null)
            {
                callback();
            }
        }
    
        public void RegisterItem(GameObject obj, ImageType imageType)
        {
            if (imageType == ImageType.MoveImage)
            {
                if (!moveimagelist.Contains(obj.GetComponent<ImageItem>()))
                {
                    moveimagelist.Add(obj.GetComponent<ImageItem>());
                }
            }
            else if (imageType == ImageType.BigImage)
            {
                if (!bigImagelist.Contains(obj.GetComponent<BigImageItem>()))
                {
                    bigImagelist.Add(obj.GetComponent<BigImageItem>());
                }
            }
        }
    
        public void ReleaseItem(GameObject obj, ImageType imageType)
        {
            if (imageType == ImageType.MoveImage)
            {
                if (moveimagelist.Contains(obj.GetComponent<ImageItem>()))
                {
                    moveimagelist.Remove(obj.GetComponent<ImageItem>());
                }
            }
            else if (imageType == ImageType.BigImage)
            {
                if (bigImagelist.Contains(obj.GetComponent<BigImageItem>()))
                {
                    bigImagelist.Remove(obj.GetComponent<BigImageItem>());
                }
            }
    
        }
    
    
        /// <summary>
        /// 判断图片是否围绕大图旋转
        /// </summary>
        /// <param name="CirClepoint">大图的坐标点</param>
        /// <param name="R">图片旋转半径</param>
        /// <param name="point">小图的坐标点</param>
        /// <returns></returns>
        public bool IsInCircle(Vector2 CirClepoint, float R, Vector2 point)
        {
            float r = Mathf.Sqrt((point.x - CirClepoint.x) * (point.x - CirClepoint.x) + (point.y - CirClepoint.y) * (point.y - CirClepoint.y));
            return r <= R;
        }
    
    
        /// <summary>
        /// 使用协程加载文件夹内所有图片文件
        /// </summary>
        /// <returns></returns>
        IEnumerator LoadTextureFromFile()
        {
            yield return null;
            List<FileInfo> fileInfos = GetAllFileFrom(texpath);//获取相应文件夹所有的图片文件流数据
            for (int i = 0; i < fileInfos.Count; i++)
            {
                LoadSingleTexture(fileInfos[i].FullName,fileInfos[i].Name);
            }
            yield return new WaitForSeconds(0.2f);
            isStartMove = true;
    
        }
    
        /// <summary>
        /// 将集合里的图片取出赋值到需要显示的RawImgae
        /// </summary>
        /// <returns></returns>
        private Texture2D GetTexture()
        {
            if (textureList.Count>0)
            {
                Texture2D newTexture2D = textureList[texturelistIndex];
                texturelistIndex += 1;
                if (texturelistIndex>=textureList.Count)
                {
                    texturelistIndex = 0;
                }
                return newTexture2D;
            }
            else
            {
                return null;
            }
        }
    
    
        /// <summary>
        /// 加载单张本地图片
        /// </summary>
        private void LoadSingleTexture(string imagepath,string imagename) 
        {
            FileStream filestream = new FileStream(imagepath,FileMode.Open,FileAccess.Read);
            filestream.Seek(0,SeekOrigin.Begin);
            byte[] bytes = new byte[filestream.Length];
            filestream.Read(bytes,0,(int)filestream.Length);
            System.Drawing.Image image = System.Drawing.Image.FromStream(filestream);
            filestream.Close();
            filestream.Dispose();
            filestream = null;
    
            int imageWidth=image.Width;
            int imageheight=image.Height;
    
            Texture2D tex2D = new Texture2D(1920,1080);
            tex2D.LoadImage(bytes);
            tex2D.name = imagename;
            textureList.Add(tex2D);
    
            bytes = null;
            filestream = null;
            image = null;
            tex2D = null;
    
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
    
        /// <summary>
        /// 得到相应文件夹下的所有文件夹内的所有文件的路径
        /// </summary>
        /// <param name="filepath">文件夹名称</param>
        /// <returns></returns>
    	private List<FileInfo> GetAllFileFrom(string filepath)
    	{
    		List<FileInfo> filesinfolist = new List<FileInfo>();
    		DirectoryInfo infoDir = new DirectoryInfo(filepath);
    		FileInfo[] filesinfo = infoDir.GetFiles("*", SearchOption.AllDirectories);//获取路径内所有文件信息
    
            for (int i = 0; i < filesinfo.Length; i++)
            {
                if (filesinfo[i].Name.Contains(".meta"))
                {
                    continue;
                }
                filesinfolist.Add(filesinfo[i]);
            }
            return filesinfolist;
    	}
    }
    
    

    ImageItem.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class ImageItem : ImageBase {
    
    	public bool isInCircle = false; //是否在围绕大图旋转
    	public ImageType imageTpye = ImageType.MoveImage; //小图的初始状态
    
    	public List<BigImageItem> bigImageItemlist = new List<BigImageItem>(); //将点击放大后的图添加进此集合
    
    	public Vector3 currentpos; //实时记录此时小图的位置
    	public Vector3 targetpos;  
    
    	GameObject bigimagePrefab; //大图预制体
    	Transform bigimageparent; //大图的父级
    
    	public bool isEnable = false; //是否初始化
    
    	Vector3 offsetpos; 
    	Vector3 targetScale = Vector3.one;
    	int R = 300;
    
    	Button self_btn; //点击放大按钮
        public RawImage raw; 
    	public float movespeed; //移动速度
    
    	public Transform targetTrans;//大图预设出来的位置参考
    
    	void Start() 
    	{
    		self_btn = transform.GetComponent<Button>();
    		bigimagePrefab = Resources.Load<GameObject>("bigimageItem");
    		bigimageparent = GameObject.Find("Canvas/bigimagepanel").transform;
    		self_btn.onClick.AddListener(()=>{
    			GetBigImageItem();
    		});
    	}
    	
    	/// <summary>
    	/// 获取相应的大图
    	/// </summary>
    	public void GetBigImageItem()
        {
    		GameObject bigimage = Instantiate(bigimagePrefab, bigimageparent,false);
    		bigimage.GetComponent<RawImage>().texture = raw.texture;
    		bigimage.transform.localPosition = transform.localPosition + new Vector3(0,50,0);
    		bigimage.AddComponent<BigImageItem>();
    		bigimage.GetComponent<BigImageItem>().originalPos = this.transform.localPosition + new Vector3(0,50,0);
    		bigimage.GetComponent<BigImageItem>().targetTrans = targetTrans;
    		
    	}
    
    	public override void OnEnter(Vector3 pos,float speed,Texture2D intoSprite)
        {
    		//GameManager.instance.RegisterItem(this.gameObject, imageTpye);
    		movespeed = speed;
    		isEnable = true;
    		currentpos = pos;
    		targetpos = currentpos;
    		Texture2D newSprite = intoSprite;
            if (newSprite!=null)
            {
    			raw = transform.GetComponent<RawImage>();
    			raw.texture = newSprite;
    		}
    		bigImageItemlist.Clear();
    		transform.localScale = Vector3.one;
    		targetScale = Vector3.one;
    		isInCircle = false;
        }
    
    	private void FixedUpdate()
    	{
            if (isEnable)
            {
    			currentpos += new Vector3(movespeed, 0, 0);
    			targetpos += new Vector3(movespeed, 0, 0);
    			if (currentpos.x>2200)
                {
    				OnExit();
    			}
    
    			CirCleImage();
    
    			currentpos = Vector3.Lerp(currentpos, targetpos + offsetpos, 0.1f);
    			transform.localScale = Vector3.Lerp(transform.localScale, targetScale, 0.1f);
    			transform.localPosition = currentpos;
    		}
    	}
    
    	/// <summary>
    	/// 小图的环绕
    	/// </summary>
    	public void CirCleImage()
    	{
    		if (isInCircle)
    		{
    			Vector3 nor = (currentpos - bigImageItemlist[0].currentPos).normalized;
    			currentpos = bigImageItemlist[0].currentPos + nor * 300;
    			if (bigImageItemlist.Count > 0)
    			{
    				for (int i = 0; i < bigImageItemlist.Count; i++)
    				{
    					if (bigImageItemlist[i] == null)
    					{
    						bigImageItemlist.RemoveAt(i);
    					}
    				}
    				float scale = 0;
    				scale = 1 - bigImageItemlist.Count * 0.5f;
    				scale = scale >= 0f ? scale : 0f;
    				targetScale = Vector3.one * scale;
    			}
    		}
    		else
    		{
    			offsetpos = Vector3.zero;
    			targetScale = Vector3.one;
    		}
    	}
    
    	public override void OnExit()
    	{
    		GameManager.instance.PushNewMoveImageItem(this.gameObject, () =>
    		{
    			gameObject.SetActive(false);
    			isEnable = false;
    		});
    		GameManager.instance.ReleaseItem(this.gameObject,imageTpye);
    	}
    }
    

    BigImageItem.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class BigImageItem : MonoBehaviour {
    
        public ImageType imageType = ImageType.BigImage;
        public List<ImageItem> moveImageList = new List<ImageItem>();
    
        public Vector3 currentPos;
        BigImageItem bigImage;
        Button quitButton;
        public bool isTouch = true;
        public float touchTimer = 0;
        float returnTimer = 60;
        
        public Vector3 originalPos;// 初始位置
    
        public Transform targetTrans;//移动范围检测 移出范围 回到原始位置,销毁,不移出范围,指定指定时间后销毁
        public bool isCrossPos;//判断是否超出展示边界,超出,归位 销毁
        float offset;
    
        Button CheckTouchbtn;
        private void Awake()
        {
            bigImage = this.GetComponent<BigImageItem>();
            quitButton = this.transform.Find("Button").GetComponent<Button>();
            CheckTouchbtn = this.GetComponent<Button>();
        }
    
        private void OnEnable()
        {
            GameManager.instance.RegisterItem(this.gameObject, imageType);
            moveImageList = GameManager.instance.moveimagelist;
            isTouch = true;
        }
    
        private void Start()
        {
            quitButton.onClick.AddListener(() => {
                CloseCircle();
            });
            CheckTouchbtn.onClick.AddListener(()=> {
                isTouch = true;
                offset = 2160f;
                if (transform.localPosition.x>(targetTrans.localPosition.x+offset)||transform.localPosition.x>targetTrans.localPosition.x)
                {
                    isCrossPos = true;
                }
            });
        }
    
        private void FixedUpdate()
        {
            if (isTouch)
            {
                touchTimer += Time.fixedDeltaTime;
                if (touchTimer > returnTimer)
                {
                    CloseCircle();
                    touchTimer = 0;
                    isTouch = false;
                }
            }
            Rotation();
            currentPos = this.transform.localPosition;
            LerpToOringinPos();
        }
    
    
        /// <summary>
        /// 判断是否满足环绕条件
        /// </summary>
        public void Rotation()
        {
            for (int i = 0; i < moveImageList.Count; i++)
            {
                if (GameManager.instance.IsInCircle(transform.localPosition, 300f, moveImageList[i].currentpos))
                {
                    //Debug.Log("在环绕条件中");
                    if (!moveImageList[i].bigImageItemlist.Contains(bigImage))
                    {
                        moveImageList[i].bigImageItemlist.Add(bigImage);
                        moveImageList[i].isInCircle = true;
                    }
                }
                else
                {
                    if (moveImageList[i].bigImageItemlist.Contains(bigImage))
                    {
                        moveImageList[i].bigImageItemlist.Remove(bigImage);
                        if (moveImageList[i].bigImageItemlist.Count <= 0)
                        {
                            moveImageList[i].isInCircle = false;
                        }
                    }
                }
            }
        }
    
    
        /// <summary>
        /// 当拖拽大图的手指抬起 大图归位
        /// </summary>
        private void LerpToOringinPos()
        {
            if (isCrossPos)
            {
                transform.localPosition = Vector3.Lerp(transform.localPosition, originalPos, 0.05f);
                if (Vector3.Distance(transform.localPosition, originalPos) < 50f)
                {
                    CloseCircle();
                }
            }
        }
    
        IEnumerator CloseCircleIE()
        {
            yield return new WaitForSeconds(0.1f);
            for (int i = 0; i < moveImageList.Count; i++)
            {
                if (moveImageList[i].bigImageItemlist.Contains(bigImage))
                {
                    moveImageList[i].bigImageItemlist.Remove(bigImage);
                    if (moveImageList[i].bigImageItemlist.Count <= 0)
                    {
                        moveImageList[i].isInCircle = false;
                    }
                }
    
            }
            GameManager.instance.ReleaseItem(this.gameObject, imageType);
            Destroy(gameObject);
        }
    
        public void CloseCircle()
        {
            StartCoroutine(CloseCircleIE());
        }
    
        private void OnDisable()
        {
            GameManager.instance.ReleaseItem(this.gameObject, imageType);
        }
    
    }
    
    

    ImageBase.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ImageBase : MonoBehaviour {
    
        public virtual void OnEnter(Vector3 pos,float speed,Texture2D intoSprite)
        { 
            
        }
    
        public virtual void OnExit()
        { 
            
        }
    }
    
    

    效果演示:

    在这里插入图片描述
    五、设备性能测试
    在这里插入图片描述
    这一个简易的大屏展示系统便是写完了,在此基础上,此系统可以玩很多花样,例如不同的展示图片轨迹、实时拍照上传展示、在大屏内编辑图片保存,实时下载网络图片展示等

    如果有需要源码的请私聊,免费提供

    展开全文
    weixin_43541308 2021-12-01 10:47:04
  • 数据大屏可视化1-pyecharts库说明(python) 数据大屏可视化2-超全的基础图形模板(基础模版) 数据大屏可视化3-通用数据大屏模版 简单设计: 1.简单折线图显示变化趋势 import random import pyecharts.options as ...

    数据大屏可视化1-pyecharts库说明(python)
    数据大屏可视化2-超全的基础图形模板(基础模版)
    数据大屏可视化3-通用数据大屏模版

    简单设计:

    1.简单折线图显示变化趋势

    import random
    import pyecharts.options as opts
    from pyecharts.charts import Line
    from pyecharts.commons.utils import JsCode
    line = (
        Line()
        .add_xaxis(['1月','2月','3月','4月','5月'])  # 1-5月
        .add_yaxis('产品1', [2022211819])
        .add_yaxis('产品2', [2125272522])
        .add_yaxis('产品3', [2628282730])
        .set_series_opts(title_opts=opts.TitleOpts(title='近两季度三个产品的销售'))     
        )
    

    2.横向条形图进行数据对比

    from pyecharts.charts import Bar
    from pyecharts import options as opts
    bar = (
        Bar()
        .add_xaxis(["产品1", "产品2", "产品3"])
        .add_yaxis("一季度",[63,73,82])
        .add_yaxis("二季度",[37,47,57])
        .reversal_axis()
        .set_global_opts(title_opts=opts.TitleOpts("rever")
                           ,toolbox_opts = opts.ToolboxOpts(is_show = True))
        .set_series_opts(title_opts=opts.TitleOpts(title='近两季度三个产品的销售'),label_opts=opts.LabelOpts(position = "right"))
    )
    

    3.环形图进行查看占比

    from pyecharts import options as opts
    from pyecharts.charts import Pie
    pie = (
        Pie()
        .add(
            '销售:',
            [list(z) for z in zip(["产品1", "产品2", "产品3"],[100,120,139])],
            radius=['50%', '75%'],          #设置内径外径           
            label_opts=opts.LabelOpts(is_show=True)        
        )
        .set_global_opts(title_opts=opts.TitleOpts(title='近两季度三个产品的销售'),
                         legend_opts=opts.LegendOpts(is_show=False))
    )
    

    4.地理图显示各个区域的门店的销售情况

    from pyecharts import options as opts
    from pyecharts.charts import Map
    import random
    province = ['广东', '湖北', '湖南', '四川', '重庆', '黑龙江', '浙江', '山西', '河北', '安徽', '河南', '山东', '西藏']
    data = [(i, random.randint(50, 150)) for i in province]
    map = (
            Map()
            .add("销售额", data, "china")
            .set_global_opts(
                title_opts=opts.TitleOpts(title="近两个季度各地门店销售情况"),
                legend_opts=opts.LegendOpts(is_show=False),
                visualmap_opts=opts.VisualMapOpts(max_=200, is_piecewise=True),
            )
        )
    

    5.雷达图显示多维度信息的影响度

    import random
    from pyecharts import options as opts
    from pyecharts.charts import Page, Radar
    def radar_simple() -> Radar:
        c = (
            Radar()
            .add_schema(
                # 各项的max_值可以不同
                schema=[
                    opts.RadarIndicatorItem(name='款式', max_=100),
                    opts.RadarIndicatorItem(name='颜色', max_=100),
                    opts.RadarIndicatorItem(name='码数', max_=100),
                    opts.RadarIndicatorItem(name='材质', max_=100),
                    opts.RadarIndicatorItem(name='质量', max_=100),
                    opts.RadarIndicatorItem(name='价格', max_=100),
                ]
            )
            .add('产品1', [[random.randint(10, 101) for _ in range(6)]],           
                 color='red',           
                 areastyle_opts = opts.AreaStyleOpts(  #设置填充的属性
                     opacity = 0.5,                  
                     color='red'                     
             ),)
            .add('产品2', [[random.randint(10, 101) for _ in range(6)]],
                 color='blue',
                 areastyle_opts = opts.AreaStyleOpts(
                     opacity = 0.5,#透明度
                     color='blue'
             ),)
            .set_series_opts(label_opts=opts.LabelOpts(is_show=True))
            .set_global_opts(title_opts=opts.TitleOpts(title='款色码质价对产品影响度'))
        )
        return c
    #radar_simple()
    
    
    
    from pyecharts.charts import Funnel
    from pyecharts import options as opts
    funnel = (Funnel()
              .add("用户数", [list(z) for z in zip(['访问', '注册', '加入购物车', '提交订单', '付款成功'], [30398, 15230, 10045, 8109, 5698])], 
                   sort_='ascending',
                   label_opts=opts.LabelOpts(position="inside"))
              .set_global_opts(title_opts=opts.TitleOpts(title="Funnel-基本示例", subtitle="我是副标题"))
             )
    

    最后得到一个比较丑的数据大屏
    在这里插入图片描述
    全篇代码

    import random
    from pyecharts.commons.utils import JsCode
    from pyecharts.charts import Bar,Pie,Map,Page, Radar,Funnel,Line,Grid
    from pyecharts import options as opts
    from pyecharts.globals import ThemeType
    
    
    line1 = (
        Line()
        .add_xaxis(['1月','2月','3月','4月','5月'])  # 1-5月
        .add_yaxis('产品1', [20,22,21,18,19])
        .add_yaxis('产品2', [21,25,27,25,22])
        .add_yaxis('产品3', [26,28,28,27,30])
        .set_global_opts(xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-30)), # 设置x轴标签旋转角度
                         yaxis_opts=opts.AxisOpts(name='销售额', min_=3), 
                         title_opts=opts.TitleOpts(title='近两季度销售'))     
        )
    line1.theme = ThemeType.PURPLE_PASSION
    
    bar1 = (
        Bar()
        .add_xaxis(["产品1", "产品2", "产品3"])
        .add_yaxis("一季度",[63,73,82])
        .add_yaxis("二季度",[37,47,57])
        .reversal_axis()
        # .set_global_opts(title_opts=opts.TitleOpts("rever")
        #                    ,toolbox_opts = opts.ToolboxOpts(is_show = True))
        .set_series_opts(title_opts=opts.TitleOpts(title='近两季度三个产品的销售'),label_opts=opts.LabelOpts(position = "right"))
    )
    bar1.theme = ThemeType.PURPLE_PASSION
    
    
    pie = (
        Pie()
        .add(
            '销售:',
            [list(z) for z in zip(["产品1", "产品2", "产品3"],[100,120,139])],
            radius=['50%', '75%'],          #设置内径外径           
            label_opts=opts.LabelOpts(is_show=True)        
        )
        .set_global_opts(title_opts=opts.TitleOpts(title='近两季度三个产品的销售\n'),
                         legend_opts=opts.LegendOpts(is_show=False))
    )
    pie.theme = ThemeType.PURPLE_PASSION
    
    
    
    province = ['广东', '湖北', '湖南', '四川', '重庆', '黑龙江', '浙江', '山西', '河北', '安徽', '河南', '山东', '西藏']
    data = [(i, random.randint(50, 150)) for i in province]
    map1 = (
            Map()
            .add("销售额", data, "china")
            .set_global_opts(
                title_opts=opts.TitleOpts(title="近两个季度各地门店销售情况"),
                legend_opts=opts.LegendOpts(is_show=False),
                visualmap_opts=opts.VisualMapOpts(max_=200, is_piecewise=True),
            )
        )
    map1.theme = ThemeType.PURPLE_PASSION
    
    
    radar1 = (
        Radar()
        .add_schema(
            # 各项的max_值可以不同
            schema=[
                opts.RadarIndicatorItem(name='款式', max_=100),
                opts.RadarIndicatorItem(name='颜色', max_=100),
                opts.RadarIndicatorItem(name='码数', max_=100),
                opts.RadarIndicatorItem(name='材质', max_=100),
                opts.RadarIndicatorItem(name='质量', max_=100),
                opts.RadarIndicatorItem(name='价格', max_=100),
            ]
        )
        .add('产品1', [[random.randint(10, 101) for _ in range(6)]],           
                color='red',           
                areastyle_opts = opts.AreaStyleOpts(  #设置填充的属性
                    opacity = 0.5,                  
                    color='red'                     
            ),)
        .add('产品2', [[random.randint(10, 101) for _ in range(6)]],
                color='blue',
                areastyle_opts = opts.AreaStyleOpts(
                    opacity = 0.5,#透明度
                    color='blue'
            ),)
        .set_series_opts(label_opts=opts.LabelOpts(is_show=True))
        .set_global_opts(title_opts=opts.TitleOpts(title='款色码质价影响'))
    )
    radar1.theme = ThemeType.PURPLE_PASSION
    
    
    
    x_data = ["{}月".format(i) for i in range(1, 13)]
    zengfaliang =  [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
    jiangshuiliang = [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
    average_wendu = [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
    grid = Grid()
    bar = Bar()
    grid.theme = ThemeType.PURPLE_PASSION
    line = Line()
    bar.add_xaxis(x_data)
    bar.add_yaxis("产品A销售量",zengfaliang)
    bar.add_yaxis("产品B销售量",jiangshuiliang)
    bar.set_global_opts(title_opts=opts.TitleOpts("天气影响")
                       ,tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross") # 交叉指向工具
                       )
    bar.extend_axis(yaxis=opts.AxisOpts(type_="value",
                                         name="温度",
                                         min_=0,
                                         max_=25,
                                         position="right",                    
                                         axislabel_opts=opts.LabelOpts(formatter="{value} °C"),
                                         ))
    # 在bar上增加Y轴,在line图上选择对应的轴向
    line.add_xaxis(x_data)
    line.add_yaxis("平均温度",average_wendu,yaxis_index = 1)
    # 把line添加到bar上
    bar.overlap(line)
    # 这里如果不需要grid也可以,直接设置bar的格式,然后显示bar即可
    #bar.render_notebook()
    grid.add(chart = bar,grid_opts = opts.GridOpts(),is_control_axis_index = True)
    
    
    
    funnel = (Funnel()
              .add("用户数", [list(z) for z in zip(['访问', '注册', '加入购物车', '提交订单', '付款成功'], [30398, 15230, 10045, 8109, 5698])], 
                   sort_='ascending',
                   label_opts=opts.LabelOpts(position="inside"))
              .set_global_opts(title_opts=opts.TitleOpts(title=" ", subtitle="线上交易用户数据分析"))
             )
    funnel.theme = ThemeType.PURPLE_PASSION
    
    #拼合所有图表
    page = (Page().add(line1).add(bar1).add(pie).add(map1).add(radar1).add(grid).add(funnel))
    page.render('产品销售情况大屏幕数据显示.html')
    
    
    
    #调整图表位置
    from bs4 import BeautifulSoup
    with open("产品销售情况大屏幕数据显示.html", "r+", encoding='utf-8') as html:
        html_bf = BeautifulSoup(html, 'lxml')
        divs = html_bf.select('.chart-container')
        #宽度:500px;高度:250像素;位置:绝对;顶部:50像素;左:0px;边框样式:纯色;边框颜色:#444444;边框-宽度:3px;
        divs[0]["style"] = "width:500px;height:300px;position:absolute;top:50px;left:50px;border-style:solid;border-color:#444444;border-width:0px;"
        divs[1]['style'] = "width:500px;height:300px;position:absolute;top:350px;left:50px;border-style:solid;border-color:#444444;border-width:0px;"
        divs[2]["style"] = "width:500px;height:300px;position:absolute;top:650px;left:50px;border-style:solid;border-color:#444444;border-width:0px;"
        divs[3]["style"] = "width:600px;height:600px;position:absolute;top:150px;left:650px;border-style:solid;border-color:#444444;border-width:0px;"
        divs[4]["style"] = "width:500px;height:300px;position:absolute;top:50px;left:1350px;border-style:solid;border-color:#444444;border-width:0px;"
        divs[5]["style"] = "width:500px;height:300px;position:absolute;top:350px;left:1350px;border-style:solid;border-color:#444444;border-width:0px;"
        divs[6]["style"] = "width:500px;height:300px;position:absolute;top:650px;left:1350px;border-style:solid;border-color:#444444;border-width:0px;"
        body = html_bf.find("body")
        body["style"] = "background-color:rgb(91 92 110);"
        div_title="<div align=\"center\" style=\"width:1800px;\">\n<span style=\"font-size:32px;font face=\'黑体\';color:#FFFFFF\"><b>产品销售情况大屏幕数据显示</b></div>"  
        #修改页面背景色、追加标题
        body.insert(0,BeautifulSoup(div_title,"lxml").div)
        html_new = str(html_bf)
        html.seek(0, 0)
        html.truncate()
        html.write(html_new)
        html.close()
    
    
    展开全文
    weixin_40929065 2020-12-18 08:55:32
  • Django开发数据可视化大屏,并通过JS来绘制大屏动态3d背景,后期将使用它作为大屏视图模板使用。最后的效果肯定不用多说。

    查看本文前请先查看 Django开发数据可视化大屏-项目启动配置

    通过前面的文章,我们已经创建了一个Django简单项目,并且做了相关的配置,今天我们来制作视图模板,通过JS绘制3D动态背景效果。

    我们先新建相关目录和文件:
    在这里插入图片描述
    下面我们来写index.html里面的代码:

    <!DOCTYPE html>
    <html lang="en" >
    <head>
    	<meta charset="UTF-8">
    	<title>3D背景</title>
    </head>
    <body>
    </body>
    </html>
    

    目前页面没有任何东西,下面加点东西:

    <!DOCTYPE html>
    <html lang="en" >
    <head>
    	<meta charset="UTF-8">
    	<title>3D背景</title>
    	<link rel="stylesheet" href="css/style.css">
    </head>
    <body>
    	<canvas></canvas>
    </body>
    </html>
    

    然后我们来加点样式:

    html, body {
    	overflow: hidden;
    	touch-action: none;
    	position: absolute;
    	margin: 0;
    	padding: 0;
    	width: 100%;
    	height: 100%;
    	background: #000;
    }
    canvas {
    	position: absolute;
    	width: 100%; 
    	height: 100%;
    	user-select: none;
    	background: #000;
    	cursor: pointer;
    }
    

    把样式代码放入style.css文件中,打开网页后是黑色的。
    在这里插入图片描述
    下面我们新建js文件,并引入。

    <!DOCTYPE html>
    <html lang="en" >
    <head>
        <meta charset="UTF-8">
        <title>3D背景</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <canvas></canvas>
        <!-- 单击以生成新结构 -->
        <script  src="js/script.js"></script>
    </body>
    </html>
    

    接下来就是写js代码(script.js):

    const webGL = (transforms, setup) => {
    	const canvas = document.querySelector("canvas");
    	const gl = canvas.getContext("webgl", { alpha: false });
    	if (!gl) return false;
    	gl.enable(gl.DEPTH_TEST);
    	gl.enable(gl.CULL_FACE);
    
    	const resize = () => {
    		canvas.width = canvas.offsetWidth;
    		canvas.height = canvas.offsetHeight;
    		gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    		camera.projection(fov);
    	};
    
    	window.addEventListener("resize", () => {
    		mustResize = true;
    	});
    
    	const clearScreen = () => {
    		if (mustResize) resize();
    		gl.clearColor(0.2, 0.225, 0.25, 1);
    		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    	};
    
    	const u3 = (program, name) => {
    		const loc = gl.getUniformLocation(program, name);
    		return {
    			set (v) {
    				gl.uniform3f(loc, v[0], v[1], v[2]);
    			}
    		}
    	};
    
    	const computeNormals = (v) => {
    		const n = [];
    		for (let i = 0; i < v.length; i += 9) {
    			const p1x = v[i + 3] - v[i];
    			const p1y = v[i + 4] - v[i + 1];
    			const p1z = v[i + 5] - v[i + 2];
    			const p2x = v[i + 6] - v[i];
    			const p2y = v[i + 7] - v[i + 1];
    			const p2z = v[i + 8] - v[i + 2];
    			const p3x = p1y * p2z - p1z * p2y;
    			const p3y = -(p1x * p2z - p1z * p2x);
    			const p3z = p1x * p2y - p1y * p2x;
    			const mag = Math.sqrt(p3x * p3x + p3y * p3y + p3z * p3z);
    			if (mag === 0) {
    				n.push(0, 0, 0, 0, 0, 0, 0, 0, 0);
    			} else {
    				n.push(p3x / mag, p3y / mag, p3z / mag);
    				n.push(p3x / mag, p3y / mag, p3z / mag);
    				n.push(p3x / mag, p3y / mag, p3z / mag);
    			}
    		}
    		return n;
    	};
    
    	const Camera = () => {
    		const camProj = gl.getUniformLocation(program, "camProj");
    		const mView = new Float32Array(16);
    		const mProj = new Float32Array(16);
    		const camView = gl.getUniformLocation(program, "camView");
    		return {
    			dist: -setup.camDist,
    			move(rx, ry, z) {
    				mView.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, z, 1]);
    				if (rx !== 0) transforms.rx(mView, rx);
    				if (ry !== 0) transforms.ry(mView, ry);
    				gl.uniformMatrix4fv(camView, false, mView);
    			},
    			projection(fov) {
    				const near = 0.001;
    				const far = 100;
    				const top = near * Math.tan((fov * Math.PI) / 360);
    				const right = top * (gl.drawingBufferWidth / gl.drawingBufferHeight);
    				const left = -right;
    				const bottom = -top;
    				mProj[0] = (2 * near) / (right - left);
    				mProj[5] = (2 * near) / (top - bottom);
    				mProj[8] = (right + left) / (right - left);
    				mProj[9] = (top + bottom) / (top - bottom);
    				mProj[10] = -(far + near) / (far - near);
    				mProj[11] = -1;
    				mProj[14] = -(2 * far * near) / (far - near);
    				gl.uniformMatrix4fv(camProj, false, mProj);
    			}
    		};
    	};
    
    	const buffer = (program, attributeName, size, type) => {
    		const buffer = gl.createBuffer();
    		const loc = gl.getAttribLocation(program, attributeName);
    		gl.enableVertexAttribArray(loc);
    		return {
    			load (array) {
    				gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    				gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), type);
    				gl.vertexAttribPointer(loc, size, gl.FLOAT, false, 0, 0);
    			}
    		}
    	};
    
    	const createShader = (source, type) => {
    		const shader = gl.createShader(type);
    		gl.shaderSource(shader, source);
    		gl.compileShader(shader);
    		if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    			throw new Error(gl.getShaderInfoLog(shader));
    		}
    		return shader;
    	}
    
    	const compileShaders = (vertex, fragment) => {
    		const vertexShader = createShader(vertex, gl.VERTEX_SHADER);
    		const fragmentShader = createShader(fragment, gl.FRAGMENT_SHADER);
    		const program = gl.createProgram();
    		gl.attachShader(program, vertexShader);
    		gl.attachShader(program, fragmentShader);
    		gl.linkProgram(program);
    		gl.useProgram(program);
    		return program;
    	};
    
    	// ---- shaders -----
    
    	const vertex = `
    		precision highp float;
    		uniform mat4 camProj, camView;
    		attribute vec3 aPosition, aNormal;
    		attribute float aBrillance;
    		varying vec4 vPosition;
    		varying vec3 vNormal, vColor;
    		varying vec3 v_surfaceToLight, v_surfaceToView;
    		void main() {
    			vPosition = camView * vec4(aPosition, 1.0);
    			gl_Position = camProj * vPosition;
    			vNormal = normalize(vec3(camView * vec4(aNormal, 0.0)));
    			vColor = vec3(aBrillance, aBrillance, aBrillance);
    		  v_surfaceToLight = vec3(0.0,0.0, 3.0) - gl_Position.xyz;
      		v_surfaceToView =  vec3(0.0,0.0, 0.0) - gl_Position.xyz;
    		}`;
    
    	const fragment = `
    		precision highp float;
    		varying vec4 vPosition;
    		varying vec3 vNormal, vColor;
    		varying vec3 v_surfaceToLight, v_surfaceToView;
    		uniform vec3 uLightPosition, uAmbientColor;
    		uniform vec3 uSpecularColor, uDiffuseColor;
    		const float density = 0.2; // fog density
    		void main() {
    
    			vec3 normal = normalize(vNormal);
    			vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
    			vec3 surfaceToViewDirection = normalize(v_surfaceToView);
    			float dotFromDirection = dot(surfaceToLightDirection, -vec3(0.0,-0.2,-1.0));
    			float inLight = smoothstep(0.988, 0.99, dotFromDirection);
    			float light = inLight * dot(normal, surfaceToLightDirection);
    		
    			float z = gl_FragCoord.z / gl_FragCoord.w;
    			float fogFactor = clamp(exp2( - density * density * pow(z, 3.0)), 0.0, 1.0);
    			vec3 lightDirection = normalize(uLightPosition - vPosition.xyz);
    			vec3 eyeDirection = normalize(-vPosition.xyz);
    			vec3 reflectionDirection = reflect(-lightDirection, vNormal);
    			float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), 6.0); // shininess
    			float diffuseLightWeighting = max(dot(vNormal, lightDirection), 0.0);
    			vec3 lightWeighting = uAmbientColor
    			+ uSpecularColor * specularLightWeighting
    			+ uDiffuseColor * diffuseLightWeighting
    			+ 5.0 * light * specularLightWeighting + light * uSpecularColor * 0.2;
    			vec3 color = vColor * lightWeighting;
    			gl_FragColor = mix(vec4(0.2, 0.225, 0.25, 1.0), vec4(color, 1.0), fogFactor);
    		}`;
    
    	const program = compileShaders(vertex, fragment);
    	const aPosition = buffer(program, "aPosition", 3, gl.STATIC_DRAW);
    	const aNormal = buffer(program, "aNormal", 3, gl.STATIC_DRAW);
    	const aBrillance = buffer(program, "aBrillance", 1, gl.STATIC_DRAW);
    
    	u3(program, "uLightPosition").set(setup.lightPosition);
    	u3(program, "uAmbientColor").set(setup.ambientColor);
    	u3(program, "uSpecularColor").set(setup.specularColor);
    	u3(program, "uDiffuseColor").set(setup.diffuseColor);
    
    	const fov = setup.fov;
    	let ry = 0;
    	let nVertices = 0;
    	let mustResize = true;
    	const camera = Camera();
    
    	const loadVertices = (vertices, brillance, rx, cz,) => {
    		const normals = computeNormals(vertices);
    		nVertices = vertices.length / 3;
    		aPosition.load(vertices);
    		aNormal.load(normals);
    		aBrillance.load(brillance);
    		camera.rx = rx;
    		camera.cz = cz;
    	};
    
    	const anim = () => {
    		requestAnimationFrame(anim);
    		clearScreen();
    		camera.move(camera.rx, ry, camera.dist + camera.cz);
    		ry -= 0.1;
    		gl.drawArrays(gl.TRIANGLES, 0, nVertices);
    	};
    
    	return {
    		gl: gl,
    		loadVertices: loadVertices,
    		resize: resize,
    		anim: anim
    	};
    };
    

    我们先定义一个webGL。
    接下来写个structure,用来设置绘图结构

    const structure = (setup, rules) => {
    
    	const shapes = [];
    	const stack = [];
    	const vertices = [];
    	const brillance = [];
    	let seed = 0;
    
    	const transforms = {
    		x(m, v) {
    			m[12] += m[0] * v;
    			m[13] += m[1] * v;
    			m[14] += m[2] * v;
    			m[15] += m[3] * v;
    		},
    		y(m, v) {
    			m[12] += m[4] * v;
    			m[13] += m[5] * v;
    			m[14] += m[6] * v;
    			m[15] += m[7] * v;
    		},
    		z(m, v) {
    			m[12] += m[8] * v;
    			m[13] += m[9] * v;
    			m[14] += m[10] * v;
    			m[15] += m[11] * v;
    		},
    		s(m, v) {
    			const a = Array.isArray(v);
    			const x = a ? v[0] : v;
    			const y = a ? v[1] : x;
    			const z = a ? v[2] : x;
    			m[0] *= x;
    			m[1] *= x;
    			m[2] *= x;
    			m[3] *= x;
    			m[4] *= y;
    			m[5] *= y;
    			m[6] *= y;
    			m[7] *= y;
    			m[8] *= z;
    			m[9] *= z;
    			m[10] *= z;
    			m[11] *= z;
    		},
    		rx(m, v) {
    			const rad = Math.PI * (v / 180);
    			const s = Math.sin(rad);
    			const c = Math.cos(rad);
    			const a10 = m[4];
    			const a11 = m[5];
    			const a12 = m[6];
    			const a13 = m[7];
    			const a20 = m[8];
    			const a21 = m[9];
    			const a22 = m[10];
    			const a23 = m[11];
    			m[4] = a10 * c + a20 * s;
    			m[5] = a11 * c + a21 * s;
    			m[6] = a12 * c + a22 * s;
    			m[7] = a13 * c + a23 * s;
    			m[8] = a10 * -s + a20 * c;
    			m[9] = a11 * -s + a21 * c;
    			m[10] = a12 * -s + a22 * c;
    			m[11] = a13 * -s + a23 * c;
    		},
    		ry(m, v) {
    			const rad = Math.PI * (v / 180);
    			const s = Math.sin(rad);
    			const c = Math.cos(rad);
    			const a00 = m[0];
    			const a01 = m[1];
    			const a02 = m[2];
    			const a03 = m[3];
    			const a20 = m[8];
    			const a21 = m[9];
    			const a22 = m[10];
    			const a23 = m[11];
    			m[0] = a00 * c + a20 * -s;
    			m[1] = a01 * c + a21 * -s;
    			m[2] = a02 * c + a22 * -s;
    			m[3] = a03 * c + a23 * -s;
    			m[8] = a00 * s + a20 * c;
    			m[9] = a01 * s + a21 * c;
    			m[10] = a02 * s + a22 * c;
    			m[11] = a03 * s + a23 * c;
    		},
    		rz(m, v) {
    			const rad = Math.PI * (v / 180);
    			const s = Math.sin(rad);
    			const c = Math.cos(rad);
    			const a00 = m[0];
    			const a01 = m[1];
    			const a02 = m[2];
    			const a03 = m[3];
    			const a10 = m[4];
    			const a11 = m[5];
    			const a12 = m[6];
    			const a13 = m[7];
    			m[0] = a00 * c + a10 * s;
    			m[1] = a01 * c + a11 * s;
    			m[2] = a02 * c + a12 * s;
    			m[3] = a03 * c + a13 * s;
    			m[4] = a00 * -s + a10 * c;
    			m[5] = a01 * -s + a11 * c;
    			m[6] = a02 * -s + a12 * c;
    			m[7] = a03 * -s + a13 * c;
    		},
    		b(m, v) {
    			if (v > 0) {
    				m[16] += v * (1 - m[16]);
    			} else {
    				m[16] += v * m[16];
    			}
    		}
    	};
    
    	const genCube = (x, y, z) => {
    		return [
    			[-x, -y, -z], [-x,  y, -z], [ x,  y, -z], [ x, -y, -z], [-x, -y, -z], [ x,  y, -z],
    			[ x,  y,  z], [-x,  y,  z], [-x, -y,  z], [ x,  y,  z], [-x, -y,  z], [ x, -y,  z],
    			[-x,  y, -z], [-x,  y,  z], [ x,  y,  z], [ x,  y, -z], [-x,  y, -z], [ x,  y,  z],
    			[ x, -y,  z], [-x, -y,  z], [-x, -y, -z], [ x, -y,  z], [-x, -y, -z], [ x, -y, -z],
    			[-x, -y, -z], [-x, -y,  z], [-x,  y,  z], [-x,  y, -z], [-x, -y, -z], [-x,  y,  z],
    			[ x,  y,  z], [ x, -y,  z], [ x, -y, -z], [ x,  y,  z], [ x, -y, -z], [ x,  y, -z]
    		];
    	};
    
    	const vCube = genCube(0.5, 0.5, 0.5);
    
    	const webgl = webGL(transforms, setup);
    
    	const pushVertices = (v, m) => {
    		const x = v[0];
    		const y = v[1];
    		const z = v[2];
    		const w = m[3] * x + m[7] * y + m[11] * z + m[15];
    		vertices.push(
    			(m[0] * x + m[4] * y + m[8] * z + m[12]) / w,
    			(m[1] * x + m[5] * y + m[9] * z + m[13]) / w,
    			(m[2] * x + m[6] * y + m[10] * z + m[14]) / w
    		);
    		brillance.push(m[16]);
    	};
    
    	const cube = (m, t) => {
    		const s = copy(m);
    		for (const c in t) {
    			if (transforms[c]) transforms[c](s, t[c]);
    			else console.log("error: " + c);
    		}
    		for (const v of vCube) {
    			pushVertices(v, s);
    		}
    	};
    	
    	const size = (m) => {
    		return Math.min(
    			m[0] * m[0] + m[1] * m[1] + m[2] * m[2],
    			m[4] * m[4] + m[5] * m[5] + m[6] * m[6],
    			m[8] * m[8] + m[9] * m[9] + m[10] * m[10]
    		);
    	};
    
    	const transform = (s, p) => {
    		const m = copy(s);
    		m[18]++;
    		for (const c in p) {
    			if (transforms[c]) transforms[c](m, p[c]);
    			else console.log("error: " + c);
    		}
    		if (minSize === 0) return m;
    		else {
    			if (size(m) < minSize) m[17] = -1;
    			return m;
    		}
    	};
    
    	const random = (_) => {
    		seed = (seed * 16807) % 2147483647;
    		return (seed - 1) / 2147483646;
    	};
    
    	const copy = (s) => {
    		return [
    			// 4 x 4 transform matrix
    			s[0], s[1], s[2], s[3], 
    			s[4], s[5], s[6], s[7], 
    			s[8], s[9], s[10], s[11], 
    			s[12], s[13], s[14], s[15], 
    			s[16], // brillance
    			s[17], // not used
    			s[18], // recurcivity depth
    			s[19]  // function index
    		];
    	};
    
    	const newStructure = () => {
    		console.log("seed:", seed);
    		vertices.length = 0;
    		brillance.length = 0;
    		runshapes(setup.start, setup.transform || {});
    		webgl.loadVertices(vertices, brillance, 20, 0);
    	};
    
    	const runshapes = (start, t) => {
    		let comp = 0;
    		let minComp = minComplexity;
    		do {
    			comp = 0;
    			stack.length = 0;
    			vertices.length = 0;
    			brillance.length = 0;
    			rule[start]([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0], t);
    			do {
    				const s = stack.shift();
    				if (s !== undefined && s[18] <= maxDepth) {
    					shapes[s[19]](s);
    					comp++;
    				}
    			} while (stack.length);
    		} while (comp < minComp--);
    	};
    
    	const singlerule = (i) => {
    		return (s, t) => {
    			s = transform(s, t);
    			if (s[17] === -1) return;
    			s[19] = i;
    			stack.push(s);
    		};
    	};
    
    	const randomrule = (totalWeight, weight, index, len) => {
    		return (s, t) => {
    			s = transform(s, t);
    			if (s[17] === -1) return;
    			let w = 0;
    			const r = random() * totalWeight;
    			for (let i = 0; i < len; i++) {
    				w += weight[i];
    				if (r <= w) {
    					s[19] = index[i];
    					stack.push(s);
    					return;
    				}
    			}
    		};
    	};
    
    	// ---- init ----
    	window.rule = {};
    	window.CUBE = cube;
    	window.SIZE = size;
    	if (setup.seed) seed = setup.seed;
    	else seed = Math.round(Math.random() * 1000);
    	const minSize = setup.minSize || 0;
    	const maxDepth = minSize === 0 ? setup.maxDepth || 100 : 1000000;
    	const minComplexity = setup.minComplexity || 0;
    	shapes.length = 0;
    	for (const namerule in rules) {
    		const r = rules[namerule];
    		if (Array.isArray(r)) {
    			let totalWeight = 0;
    			const weight = [];
    			const index = [];
    			for (let i = 0; i < r.length; i += 2) {
    				totalWeight += r[i];
    				shapes.push(r[i + 1]);
    				weight.push(r[i]);
    				index.push(shapes.length - 1);
    			}
    			rule[namerule] = randomrule(totalWeight, weight, index, index.length);
    		} else {
    			shapes.push(r);
    			rule[namerule] = singlerule(shapes.length - 1);
    		}
    	}
    
    	newStructure();
    	webgl.resize();
    	webgl.anim();
    	
    	window.addEventListener("pointerdown", (e) => newStructure(), false);
    
    };
    

    下面写个配置相关的设置:

    const setup = {
    	start: "start",
    	transform: { y: 0, s: 0.1 },
    	maxDepth: 20,
    	fov: 60,
    	camDist: 3,
    	lightPosition: [0, 0.2, -3],
    	ambientColor: [0.0, 0.0, 0.0],
    	specularColor: [1.5*0.2, 1.5*0.4, 1.5*0.6],
    	diffuseColor: [1.5*0.7, 1.5*0.35, 0]
    };
    

    下面定义下规则:

    const rules = {
    	start(s) {
    		CUBE(s, {y: -5, s:[300, 0.1, 300], b: 1});
    		for (let x = -8; x < 8; x++) {
    			for (let z = -8; z < 8; z++) {
    				rule.R1(s, {x: 3 * x, z: 3 * z, s:[0.38, 0.5, 0.4]});
    			}
    		}
    	},
    	R1: [
    		100, (s) => {
    			CUBE(s);
    			rule.R1(s, { z: 0.4, rx: 90, s: 0.99});
    		},
    		100, (s) => {
    			CUBE(s);
    			rule.R1(s, { z: 0.4, rx: 90, ry: -90, s: 0.99 });
    		},
    		100, (s) => {
    			CUBE(s);
    			rule.R1(s, { z: 0.4, rx: 90, ry: 90, s: 0.99 });
    		},
    		30, (s) => {
    			rule.R1(s, { rx: 90, s: [4, 1, 1.3] });
    			rule.R1(s, { rz: 90, s: [1, 0.7, 1] });
    		},
    		40, (s) => {
    			rule.R1(s, { rx: 90, s: [0.5, 1, 1.3] });
    			rule.R1(s, { rz: -90, s: [1, 1, 0.7] });
    		},
    	]
    };
    

    最后我们调用下函数,完成绘制,并转入设置信息和规则信息。

    structure(setup, rules);
    

    在头部加上严格模式

    "use strict";
    

    为了每次都从新绘制,在头部加上清除控制台上所有信息代码:

    console.clear();
    

    现在我们可以看效果了。
    在这里插入图片描述
    是不是感觉像那么回事了?
    如果不想要这么多的元素,可以修改下代码中的设置参数:

    const setup = {
    	start: "start",
    	transform: { y: 0, s: 0.1 },
    	maxDepth: 10,
    	fov: 20,
    	camDist: 3,
    	lightPosition: [0, 0.2, -3],
    	ambientColor: [0.0, 0.0, 0.0],
    	specularColor: [1.5*0.2, 1.5*0.4, 1.5*0.6],
    	diffuseColor: [1.5*0.7, 1.5*0.35, 0]
    };
    

    在这里插入图片描述
    或者可以再修改下设置的参数;

    const setup = {
    	start: "start",
    	transform: { y: 0, s: 0.1 },
    	maxDepth: 10,
    	fov: 60,
    	camDist: 3,
    	lightPosition: [0, 0.2, -3],
    	ambientColor: [0.0, 0.0, 0.0],
    	specularColor: [1.5*0.2, 1.5*0.4, 1.5*0.6],
    	diffuseColor: [1.5*0.7, 1.5*0.35, 0]
    };
    

    在这里插入图片描述
    总之,你如果想调整效果,可以尝试修改下面的代码就可以了。

    const setup = {
    	start: "start",
    	transform: { y: 0, s: 0.1 },
    	maxDepth: 10,
    	fov: 60,
    	camDist: 1,
    	lightPosition: [0, 0.2, -3],
    	ambientColor: [0.0, 0.0, 0.0],
    	specularColor: [1.5*0.2, 1.5*0.4, 1.5*0.6],
    	diffuseColor: [1.5*0.7, 1.5*0.35, 0]
    };
    

    在这里插入图片描述
    3d动态背景就到这了,下次我们就要使用这个背景做可视化大屏的背景。

    展开全文
    huidaoli 2021-11-30 11:03:31
  • weixin_36147152 2020-12-24 18:57:15
  • LiChengzhong01 2021-11-15 09:30:59
  • broadview2006 2021-06-09 09:23:31
  • weixin_41937552 2021-06-08 01:13:33
  • A15511052336 2021-10-25 10:33:47
  • weixin_32722209 2020-12-23 05:42:25
  • weixin_39900023 2020-12-22 13:06:50
  • weixin_29111277 2021-01-14 08:09:18
  • huangzbin 2021-11-05 08:29:29
  • weberhuangxingbo 2020-12-28 13:21:12
  • Summer_4336 2021-12-16 09:45:46
  • weixin_39746652 2020-12-22 10:54:42
  • weixin_42499692 2021-08-02 03:01:16
  • ZGL_cyy 2021-09-10 14:37:59
  • esensoft123 2021-12-06 12:10:46
  • weixin_44106334 2021-05-20 14:10:59
  • lildkdkdkjf 2021-12-13 15:48:00
  • weixin_49426904 2021-03-18 09:36:03
  • weixin_35879989 2021-06-18 09:48:14
  • u011916503 2021-12-05 19:35:29
  • weixin_39623750 2020-12-22 10:54:47
  • weixin_39709134 2021-09-18 10:27:45
  • weixin_35293785 2020-12-31 11:53:46
  • qq_18932003 2021-10-03 17:17:17
  • qq_31760543 2021-07-22 15:04:25
  • wuhuama_ 2021-12-07 19:58:03

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 72,971
精华内容 29,188
关键字:

大屏