您的位置:奥门新浦京网址 > Wed前段 > 用500行纯前端代码在浏览器中构建一个Tableau,编

用500行纯前端代码在浏览器中构建一个Tableau,编

发布时间:2019-12-15 01:51编辑:Wed前段浏览(94)

    了解HTML/HTML5中的download属性

    2016/04/07 · HTML5 · 5 评论 · download

    原文出处: 张鑫旭(@张鑫旭)   

    一、download属性是个什么鬼?

    首先看下面这种截图:
    图片 1

    如果我们想实现点击上面的下载按钮下载一张图片,你会如何实现?

    我们可能会想到一个最简单的方法,就是直接按钮a标签链接一张图片,类似下面这样:

    <a href="large.jpg">下载</a>

    1
    <a href="large.jpg">下载</a>

    但是,想法虽好,实际效果却不是我们想要的,因为浏览器可以直接浏览图片,因此,我们点击下面的“下载”链接,并是不下载图片,而是在新窗口直接浏览图片。

    下载

    看我的眼睛,图片 2

    于是,基本上,目前的实现都是放弃HTML策略,而是使用,例如php这样的后端语言,通过告知浏览器header信息,来实现下载。

    header('Content-type: image/jpeg'); header("Content-Disposition: attachment; filename='download.jpg'");

    1
    2
    header('Content-type: image/jpeg');
    header("Content-Disposition: attachment; filename='download.jpg'");

    然而,这种前后端都要操心的方式神烦,现在都流行前后端分离,还搅在一起太累了,感觉不会再爱了。

    那有没有什么只需要前端动动指头就能实现下载的方式呢?有,就是本文要介绍的download属性。

    例如,我们希望点击“下载”链接下载图片而不是浏览,直接增加一个download属性就可以:

    <a href="large.jpg" download>下载</a>

    1
    <a href="large.jpg" download>下载</a>

    没错,你没有看错,就这么结束了,不妨点击后面的链接试试:下载

    结果在Chrome浏览器下(FireFox浏览器因为跨域限制无效):
    图片 3

    不仅如此,我们还可以指定下载图片的文件名:

    <a href="index_logo.gif" download="_5332_.gif">下载</a>

    1
    <a href="index_logo.gif" download="_5332_.gif">下载</a>

    如果后缀名一样,我们还可以缺省,直接文件名:

    <a href="index_logo.gif" download="_5332_">下载</a>

    1
    <a href="index_logo.gif" download="_5332_">下载</a>

    截图为虚,操作为实:下载

    Chrome下的截图效果示意:
    图片 4

    一个大写的酷里!

    纯 HTML+CSS+JavaScript 编写的计算器应用

    2016/09/26 · CSS, HTML5, JavaScript · 2 评论 · 计算器

    原文出处: dunizb   

    用500行纯前端代码在浏览器中构建一个Tableau

    2018/05/16 · 基础技术 · BI, Tableau, 数据可视化

    原文出处: naughty   

    在Gartner最新的对商务智能软件的专业分析报告中,Tableau持续领跑。Microsoft因为PowerBI表现出色也处于领导者象限。而昔日的领导者像SAP,SAS,IBM,MicroStrategy等逐渐被拉开了差距。

    图片 5

    Tableau因为其灵活,出色的数据表现已经成为BI领域里无可争议的领头羊。而其数据驱动的可视化和核心思想是来自于Leland Wilkinson的The Grammar Of Graphics ,同样受到该思想影响的还有R的图形库ggplot。

    图片 6

    在数据可视化开源领域里,大家对百度开发的echarts可谓耳熟能详,echarts经过多年的发展,其功能确实非常强大,可用出色来形容。但是蚂蚁金服开源的基于The Grammar Of Graphics的语法驱动的可视化库G2,让人眼前一亮。那我们就看看如何利用G2和500行左右的纯前端代码来实现一个的类似Tableau的数据分析功能。

    • 演示参见 
    • 代码参见 https://gist.github.com/gangtao/e053cf9722b64ef8544afa371c2daaee 

     

    二、浏览器兼容性和跨域策略

    图片 7

    然而,caniuse展示的兼容性只是个笼统,根据鄙人的实地测试,事情要比看到的复杂。

    主要表现在跨域策略的处理上,由于我手上没有IE13,所以,只能对比Chrome浏览器和FireFox浏览器:

    如果需要下载的资源是跨域的,包括跨子域,在Chrome浏览器下,使用download属性是可以下载的,但是,并不能重置下载的文件的命名;而FireFox浏览器下,则download属性是无效的,也就是FireFox浏览器无论如何都不支持跨域资源的download属性下载。

    而,如果资源是同域名的,则两个浏览器都是畅通无阻的下载,不会出现下载变浏览的情况。
    图片 8

    是否支持download属性的监测
    要监测当前浏览器是否支持download属性,一行JS代码就可以了,如下:

    var isSupportDownload = 'download' in document.createElement('a');

    1
    var isSupportDownload = 'download' in document.createElement('a');

    一道笔试题

    之前偶然看到一个公司的笔试题,题目如下:

    用HTML5、CSS3、JavaScript,做一个网页,实现如下图形式计算器

    图片 9

    具体要求:

    1. 有且只有一个文件:index.html。不允许再有其他文件,不允许再有单独的CSS、JS、PNG、JPG文件。
    2. 运行环境为 Google Chrome。
    3. 必须支持标准的四则运算。例如:1+2*3=7。
    4. 请在收到邮件的48小时内独立完成本测试,并回复本邮件。

    数据加载

    第一步是加载数据:

    图片 10

    数据加载主要用到了三个库:

    • axios  基于Promise的HTTP客户端
    • alasql 基于JS的开源SQL数据库
    • jquery datatable JQuery的数据表格插件

    数据通过我存放在GitHub中的csv格式的文件,以REST请求的方式来加载。下面的代码把Axios的Promise变成 async/wait方式。

    // Ajax async request const request = { get: url => { return new Promise((resolve, reject) => { axios .get(url) .then(response => { resolve({ data: response.data }); }) .catch(error => { resolve({ data: error }); }); }); } };

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Ajax async request
    const request = {
      get: url => {
        return new Promise((resolve, reject) => {
          axios
            .get(url)
            .then(response => {
              resolve({ data: response.data });
            })
            .catch(error => {
              resolve({ data: error });
            });
        });
      }
    };

    封装好后,我们就可以用request.get()方法发送REST请求,获取csv文件。

    let csv = await request.get(url);

    1
    let csv = await request.get(url);

    这一步可能会遇到跨域请求的问题,github上的文件支持跨域。

    把数据存储在一个SQL数据库中,这样做的好处是为了下一步做数据准备的时候,可以方便的利用SQL来进行查询和分析。

    JavaScript

    class SqlTable { constructor(data) { this.data = data; } async query(sql) { // following line of code does not run in full page view due to security concern. // const query_str = sql.replace(/(?<=FROMs+)w+/, "CSV(?)"); const query_str = sql.replace("table", "CSV(?)"); return await alasql.promise(query_str, [this.data]); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class SqlTable {
      constructor(data) {
        this.data = data;
      }
     
      async query(sql) {
        // following line of code does not run in full page view due to security concern.
        // const query_str = sql.replace(/(?<=FROMs+)w+/, "CSV(?)");
        const query_str = sql.replace("table", "CSV(?)");
        return await alasql.promise(query_str, [this.data]);
      }
    }

    SqlTable是一个对数据表的封装,把csv数据存在SQL数据库表中,提供一个query()方法。这里要做的是把SQL查询个从 “SELECT * FROM table” 变成 “SELECT * FROM CSV(?)” 表示查询参数是CSV数据。因为codepen的安全性限制,运行前向查找的replace语句(这里的regex表示把前面是“FROM ”词的替换为CSV(?)的)在full page view下是不能执行的,所以我用了一个更简单的假定,用户的表名就是table,这样做有很多问题,大家如果在codepen之外的环境,可以用注释掉的代码。

    然后把”SELECT * FROM table”的查询结果(JSON Array)用datatable来展示。

    function sanitizeData(jsonArray) { let newKey; jsonArray.forEach(function(item) { for (key in item) { newKey = key.replace(/s/g, "").replace(/./g, ""); if (key != newKey) { item[newKey] = item[key]; delete item[key]; } } }); return jsonArray; } function displayData(tableId, data) { // tricky to clone array let display_data = JSON.parse(JSON.stringify(data)); display_data = sanitizeData(display_data); let columns = []; for (let item in display_data[0]) { columns.push({ data: item, title: item }); } $("#" + tableId).DataTable({ data: display_data, columns: columns, destroy: true }); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    function sanitizeData(jsonArray) {
      let newKey;
      jsonArray.forEach(function(item) {
        for (key in item) {
          newKey = key.replace(/s/g, "").replace(/./g, "");
          if (key != newKey) {
            item[newKey] = item[key];
            delete item[key];
          }
        }
      });
      return jsonArray;
    }
     
    function displayData(tableId, data) {
      // tricky to clone array
      let display_data = JSON.parse(JSON.stringify(data));
      display_data = sanitizeData(display_data);
      let columns = [];
      for (let item in display_data[0]) {
        columns.push({ data: item, title: item });
      }
      $("#" + tableId).DataTable({
        data: display_data,
        columns: columns,
        destroy: true
      });
    }

    这一步有两点要注意:

    1. 数据中,如果列的名字中有包含点,空格等字符,例如Iris数据集中的Sepal.Length,datatable是无法正常显示的,这里要调用sanitizeData()方法把列名,也就是JsonArray中Json对象的属性名中的点和空格去掉。
    2. sanitizeData()方法会改变输入对象,所以在传入之前做了一个深度拷贝,这里利用JSON的stringfy和parse方法可以对JSON兼容的对象有效的拷贝。

    这里要注意,Iris数据集中在datatable中的列名都不显示点,但实际数据并没有改变。

     

    三、结束语

    除了图片资源,我们还可以是PDF资源,或者txt资源等等。尤其Chrome等浏览器可以直接打开PDF文件,使得此文件格式需要download处理的场景越来越普遍。

    此HTML属性虽然非常实用和方便,但是兼容性制约了我们的大规模应用。

    同时考虑到很多时候,需要进行一些下载的统计,纯前端的方式想要保存下载量数据,还是有些吃紧,需要跟开发的同学配合才行,还不如使用传统方法。

    所以,download属性的未来前景在哪里?当下是否可以直接加入到实际项目?还需要我们一起好好想想。其实使用JS实现download属性的polyfill并不难,但是,考虑到为何不所有浏览器都使用polyfill的方法,又觉得为了技术而技术是不太妥当的。

    总之,先放着心上,再观察观察。

    1 赞 3 收藏 5 评论

    图片 11

    一道笔试题引发的一个练手项目

    花了一点时间写好的第一版,符合了笔试题的要求。后来左看右看觉得还可以改进做的更好,于是给它不断的改进,加新功能等,这样下来没完没了,利用业余时间一点一点的写,从刚开始的网页版,到后来做响应式的移动版,再到现在的移动App,短短续续大概写了3个月吧。

    数据准备

    数据加载完毕,我们来到第二步的数据准备阶段。数据准备是数据科学项目最花时间的一步,通常需要对数据进行大量的清洗,变形,抽取等工作,使得数据变得可用。

    在这一步我们做了两件事:

    一是显示数据的一个摘要,让我们初步了解数据的概貌,为进一步的数据变形和处理做好准备。

    这个是Iris数据集的摘要:

    图片 12

    function isString(o) { return typeof o == "string" || (typeof o == "object" && o.constructor === String); } function summaryData(data) { let summary = {}; summary.count = data.length; summary.fields = []; for (let p in data[0]) { let field = {}; field.name = p; if ( isString(data[0][p]) ) { field.type = "string"; } else { field.type = "number"; } summary.fields.push(field); } for (let f of summary.fields) { if ( f.type == "number" ) { f.max = d3.max(data, x => x[f.name]); f.min = d3.min(data, x => x[f.name]); f.mean = d3.mean(data, x => x[f.name]); f.median = d3.median(data, x => x[f.name]); f.deviation = d3.deviation(data, x => x[f.name]); } else { f.values = Array.from(new Set(data.map(x => x[f.name]))); } } return summary; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function isString(o) {
        return typeof o == "string" || (typeof o == "object" && o.constructor === String);
    }
     
    function summaryData(data) {
      let summary = {};
      summary.count = data.length;
      summary.fields = [];
      for (let p in data[0]) {
        let field = {};
        field.name = p;
        if ( isString(data[0][p]) ) {
          field.type = "string";
        } else {
          field.type = "number";
        }
        summary.fields.push(field);
      }
      
      for (let f of summary.fields) {
          if ( f.type == "number" ) {
            f.max = d3.max(data, x => x[f.name]);
            f.min = d3.min(data, x => x[f.name]);
            f.mean = d3.mean(data, x => x[f.name]);
            f.median = d3.median(data, x => x[f.name]);
            f.deviation = d3.deviation(data, x => x[f.name]);
          } else {
            f.values = Array.from(new Set(data.map(x => x[f.name])));
          }
      }
      return summary;
    }

    这里我们利用数据的类型判断出每一个字段是数值型还是字符型。对于字符型的字段,我们利用JS6的Set来获得所有的Unique数据。对于数值型,我们利用d3的max,min,mean,median,deviation方法计算出对应的最大值,最小值,平均数,中位数和偏差。

    另一个就是利用SQL查询来对数据进行进一步的加工。

    图片 13

    上图的例子中我们利用限制条件得到一个Iris数据的子集。

    另外G2还提供了Dataset的功能:

    • 源数据的解析,将csv, dsv,geojson 转成标准的JSON,查看Connector
    • 加工数据,包括 filter,map,fold(补数据) 等操作,查看 Transform
    • 统计函数,汇总统计、百分比、封箱 等统计函数,查看 Transform
    • 特殊数据处理,包括 地理数据、矩形树图、桑基图、文字云 的数据处理,查看 Transform

    数据处理是一个比较大的话题,我们的目标是利用尽可能少的代码完成一个数据分析的工具,所以这一步仅仅是利用alasql提供的SQL查询来处理数据。

     

    项目地址

    最终版的计算器,项目地址和预览图片在 GitHub:。

    数据展示

    数据处理好后就是我们的核心内容,数据展示了。

    图片 14

    这一步主要是利用select2提供的选择控件构建图形语法来驱动数据展示。如上图所示,对应的G2代码图形语法为:

    g2chart.facet('rect', { fields: [ 'Admit', 'Dept' ], eachView(view) { view.interval().position('Gender*Freq').color('Gender').label('Freq'); } });

    1
    2
    3
    4
    5
    6
    g2chart.facet('rect', {
      fields: [ 'Admit', 'Dept' ],
      eachView(view) {
        view.interval().position('Gender*Freq').color('Gender').label('Freq');
      }
    });

    图形语法主要包含以下几个主要的元素:

     

    功能说明

    最终版的功能如下:

    1. 界面布局采用CSS3 的 Flex box布局
    2. 内置两套主题可切换
    3. 计算历史记录显示
    4. 左滑右滑可以切换单手模式(App)
    5. 当输入手机号码后长按等于号可以拨打手机号码(App)
    6. 版本更新检查(App)

    界面布局

    由于这个项目只是练手,所以采用了HTML5个CSS3技术,也不打算兼容IE等低版本浏览器,所以直接使用CSS3提供的Flexbox布局方式。并且使用rem单位来进行自动计算尺寸。

    计算计算历史记录显示功能,使用HTML5提供的本地存储功能之Local Storage,为了方便使用Local Storage,对它进行了简单的封装(见js/common.js文件)使之key值按一定规律生产,方便管理。

    图片 15

    key由appName+id组成,id是自动增长不重复的,可以按id和appName删除一条记录,输入*则全部删除。

    打包APP

    移动Web版计算器写完后,又想把他做成APP在手机上运行,由于本人没用过混合APP诸如ionic之类的框架,所以参考了一下,选择了Hbuild来进行开发和APP的打包,非常方便。(HBuild).

    单手模式

    左滑右滑可以切换单手模式,这就需要移动端的touch事件了,使用如下代码判断是左滑还是又滑:

    JavaScript

    /** 单手模式 */ function singleModel(){ var calc = document.getElementById("calc"); var startX = 0,moveX = 0,distanceX = 0; var distance = 100; var width = calc.offsetWidth; //滑动事件 calc.addEventListener("touchstart",function(e){ startX = e.touches[0].clientX; }); calc.addEventListener("touchmove",function(e){ moveX = e.touches[0].clientX; distanceX = moveX - startX; isMove = true; }); window.addEventListener("touchend",function(e){ if(Math.abs(distanceX) > width/3 && isMove){ if( distanceX > 0 ){ positionFun("right"); //右滑 }else{ positionFun("left"); //作滑 } } startY = moveY = 0; isMove = false; }); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /** 单手模式 */
    function singleModel(){
        var calc = document.getElementById("calc");
        var startX = 0,moveX = 0,distanceX = 0;
        var distance = 100;  
        var width = calc.offsetWidth;
        //滑动事件
        calc.addEventListener("touchstart",function(e){
            startX = e.touches[0].clientX;
        });
        calc.addEventListener("touchmove",function(e){
            moveX = e.touches[0].clientX;
            distanceX = moveX - startX;
            isMove = true;
        });
        window.addEventListener("touchend",function(e){
            if(Math.abs(distanceX) > width/3 && isMove){
                if( distanceX > 0 ){
                    positionFun("right");        //右滑
                }else{
                    positionFun("left");          //作滑
                }
            }
            startY = moveY = 0;
            isMove = false;
        });  
    }

    如果是坐滑,就position:absolut;left:0,bottom:0,再把最外层DIV缩小到80%,这样就实现了左滑计算器缩小移动到左下角。右滑道理一样。

    电话拨打功能

    当输入手机号码后长按等于号可以拨打手机号码。这个功能没什么神奇,在移动Web上会对那些看起来像是电话号码的数字处理为电话链接,比如:

    • 7位数字,形如:1234567
    • 带括号及加号的数字,形如:(+86)123456789
    • 双连接线的数字,形如:00-00-00111
    • 11位数字,形如:13800138000

    可能还有其他类型的数字也会被识别。我们可以通过如下的meta来开启电话号码的自动识别:

    XHTML

    <meta name="format-detection" content="telephone=yes" />

    1
    <meta name="format-detection" content="telephone=yes" />

    开启电话功能

    XHTML

    <a href="tel:123456">123456</a>

    1
    <a href="tel:123456">123456</a>

    开启短信功能:

    XHTML

    <a href="sms:123456">123456</a>

    1
    <a href="sms:123456">123456</a>

    但是,在Android系统上,只能调用系统的拨号界面,在iOS上则能调过这一步直接把电话拨打出去。

    版本更新检查

    在关于页面,有一个版本更新检查按钮,就能检查是否有新版本,这个功能的原理是发送一个JSOPN请求去检查服务器上的JSON文件,比对版本号,如果服务器上的版本比APP的版本高则会提示有新版本可以下载。

    客户端JavaScript代码:

    JavaScript

    function updateApp(){ //检查新版本 var updateApp = document.getElementById("updateApp"); updateApp.onclick = function(){ var _this = this; $.ajax({ type:'get', url:'', dataType:'jsonp', beforeSend : function(){ _this.innerHTML = "<i class='iconfont updateAppIcon updateAppIconRotate'></i> 正在检查新版本..."; }, success:function(data){ var newVer = data[0].version; if(newVer > appConfig.version){ var log = data[0].log; var downloadUrl = data[0].downloadUrl; if(confirm("检查到新版本【"+newVer+"】,是否立即下载?n 更新日志:n " + log)){ var a = document.getElementById("telPhone"); a.href = downloadUrl; a.target = "_blank"; a.click(); } }else{ alert("你很潮哦,当前已经是最新版本!"); } _this.innerHTML = "<i class='iconfont updateAppIcon'></i> 检查新版本"; }, error:function(msg){ _this.innerHTML = "<i class='iconfont updateAppIcon'></i> 检查新版本"; alert("检查失败:"+msg.message); } }); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    function updateApp(){
        //检查新版本
        var updateApp = document.getElementById("updateApp");
        updateApp.onclick = function(){
            var _this = this;
            $.ajax({
                type:'get',
                url:'http://duni.sinaapp.com/demo/app.php?jsoncallback=?',
                dataType:'jsonp',
                beforeSend : function(){
                    _this.innerHTML = "<i class='iconfont updateAppIcon updateAppIconRotate'></i> 正在检查新版本...";
                },
                success:function(data){
                    var newVer = data[0].version;
                    if(newVer > appConfig.version){
                        var log = data[0].log;
                        var downloadUrl = data[0].downloadUrl;
                        if(confirm("检查到新版本【"+newVer+"】,是否立即下载?n 更新日志:n " + log)){
                            var a = document.getElementById("telPhone");
                            a.href = downloadUrl;
                            a.target = "_blank";
                            a.click();
                        }
                    }else{
                        alert("你很潮哦,当前已经是最新版本!");
                    }
                    _this.innerHTML = "<i class='iconfont updateAppIcon'></i> 检查新版本";
                },
                error:function(msg){
                    _this.innerHTML = "<i class='iconfont updateAppIcon'></i> 检查新版本";
                    alert("检查失败:"+msg.message);
                }
            });
        }
    }

    服务端JSON:

    JavaScript

    [ { "version":"3.1.0", "downloadUrl":"", "hashCode":"20160812224616", "log":"1.新增切换主题功能 n 2.新增单手切换模式功能 n 3.调整UI " } ]

    1
    2
    3
    4
    5
    6
    7
    8
    [
        {
            "version":"3.1.0",
            "downloadUrl":"http://dunizb.b0.upaiyun.com/demo/app/myCalc-3.1.0.apk",
            "hashCode":"20160812224616",
            "log":"1.新增切换主题功能 n 2.新增单手切换模式功能 n 3.调整UI "
        }
    ]

    几何标记 Geometry

    几何标记定义了使用什么样的几何图形来表征数据。G2现在支持如下这些几何标记:

    geom 类型 描述
    point 点,用于绘制各种点图。
    path 路径,无序的点连接而成的一条线,常用于路径图的绘制。
    line 线,点按照 x 轴连接成一条线,构成线图。
    area 填充线图跟坐标系之间构成区域图,也可以指定上下范围。
    interval 使用矩形或者弧形,用面积来表示大小关系的图形,一般构成柱状图、饼图等图表。
    polygon 多边形,可以用于构建色块图、地图等图表类型。
    edge 两个点之间的链接,用于构建树图和关系图中的边、流程图中的连接线。
    schema 自定义图形,用于构建箱型图(或者称箱须图)、蜡烛图(或者称 K 线图、股票图)等图表。
    heatmap 用于热力图的绘制。

    这里要注意,intervalstack是官方支持的,但是文档没有提到,在阅读G2的API文档的时候,我也发现文档讲的不是很清楚,有很多地方没有讲清楚如何使用API。这也是开源软件值得改进的地方。

     

    下个版本计划

    当前3.1.0版本还存在一些问题:

    1. 由于JS本身存在计算浮点数精度丢失问题,所以这个问题在项目中同意存在,需要自己去处理这个问题
    2. 由于使用了第三方的天气接口,用了jquery.Ajax方法,所以违背了使用纯原生写的初衷。

    所以下个版本的开发计划为:

    1. 解决浮点数计算精度问题
    2. 把获取天气信息的jquery.Ajax方法替换为原生JavaScript代码,自己封装JSONP请求函数
    3. 使用面向对象方式重构APP

    欢迎大家到github上来看看,如果喜欢可以star、watch一下,或提issue。

    4 赞 21 收藏 2 评论

    图片 16

    图形属性 Attributes

    图形属性对应视觉编码中的不同元素,大家可以参考我的另一博客 数据可视化中的视觉属性 。

    图形属性主要有以下几种。

    1. position:位置,二维坐标系内映射至 x 轴、y 轴;
    2. color:颜色,包含了色调、饱和度和亮度;
    3. size:大小,不同的几何标记对大小的定义有差异;
    4. shape:形状,几何标记的形状决定了某个具体图表类型的表现形式,例如点图,可以使用圆点、三角形、图片表示;线图可以有折线、曲线、点线等表现形式;
    5. opacity:透明度,图形的透明度,这个属性从某种意义上来说可以使用颜色代替,需要使用 ‘rgba’ 的形式,所以在 G2 中我们独立出来。

    在构建语法的时候,我们把图形属性绑定一个或者多个数据字段。

     

    本文由奥门新浦京网址发布于Wed前段,转载请注明出处:用500行纯前端代码在浏览器中构建一个Tableau,编

    关键词: