ag真人用500行纯前端代码在浏览器中营造叁个Ta

2019-11-22 01:25栏目:人才招聘
TAG:

了解HTML/HTML5中的download属性

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

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

一、download属性是个什么鬼?

首先看下面这种截图:
ag真人 1

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

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

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

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

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

下载

看我的眼睛,ag真人 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浏览器因为跨域限制无效):
ag真人 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下的截图效果示意:
ag真人 4

一个大写的酷里!

H5单页面手势滑屏切换原理

2016/03/22 · HTML5 · 2 评论 · 滑屏

原文出处: 一像素   

H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路。

ag真人 5

1、实现原理

假设有5个页面,每个页面占屏幕100%宽,则创建一个DIV容器viewport,将其宽度(width) 设置为500%,然后将5个页面装入容器中,并让这5个页面平分整个容器,最后将容器的默认位置设置为0,overflow设置为hidden,这样屏幕就默认显示第一个页面。

<div id="viewport" class="viewport"> <div class="pageview" style="background: #3b76c0" > <h3 >页面-1</h3> </div> <div class="pageview" style="background: #58c03b;"> <h3>页面-2</h3> </div> <div class="pageview" style="background: #c03b25;"> <h3>页面-3</h3> </div> <div class="pageview" style="background: #e0a718;"> <h3>页面-4</h3> </div> <div class="pageview" style="background: #c03eac;"> <h3>页面-5</h3> </div> </div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="viewport" class="viewport">
    <div class="pageview" style="background: #3b76c0" >
        <h3 >页面-1</h3>
    </div>
    <div class="pageview" style="background: #58c03b;">
        <h3>页面-2</h3>
    </div>
    <div class="pageview" style="background: #c03b25;">
        <h3>页面-3</h3>
    </div>
    <div class="pageview" style="background: #e0a718;">
        <h3>页面-4</h3>
    </div>
    <div class="pageview" style="background: #c03eac;">
        <h3>页面-5</h3>
    </div>
</div>

CSS样式:

.viewport{ width: 500%; height: 100%; display: -webkit-box; overflow: hidden; pointer-events: none; -webkit-transform: translate3d(0,0,0); backface-visibility: hidden; position: relative; }

1
2
3
4
5
6
7
8
9
10
.viewport{
   width: 500%;
   height: 100%;
   display: -webkit-box;
   overflow: hidden;
   pointer-events: none;
   -webkit-transform: translate3d(0,0,0);
   backface-visibility: hidden;
   position: relative;
}

注册touchstart,touchmove和touchend事件,当手指在屏幕上滑动时,使用CSS3的transform来实时设置viewport的位置,比如要显示第二个页面,就设置viewport的transform:translate3d(100%,0,0) 即可, 在这里我们使用translate3d来代替translateX,translate3d可以主动开启手机GPU加速渲染,页面滑动更流畅。

2、主要思路

从手指放在屏幕上、滑动操作、再到离开屏幕是一个完整的操作过程,对应的操作会触发如下事件:

手指放在屏幕上:ontouchstart

手指在屏幕上滑动:ontouchmove

手指离开屏幕:ontouchend

我们需要捕获触摸事件的这三个阶段来完成页面的滑动:

ontouchstart: 初始化变量, 记录手指所在的位置,记录当前时间

/*手指放在屏幕上*/ document.addEventListener("touchstart",function(e){ e.preventDefault(); var touch = e.touches[0]; startX = touch.pageX; startY = touch.pageY; initialPos = currentPosition; //本次滑动前的初始位置 viewport.style.webkitTransition = ""; //取消动画效果 startT = new Date().getTime(); //记录手指按下的开始时间 isMove = false; //是否产生滑动 }.bind(this),false);

1
2
3
4
5
6
7
8
9
10
11
/*手指放在屏幕上*/
document.addEventListener("touchstart",function(e){
   e.preventDefault();
   var touch = e.touches[0];
   startX = touch.pageX;
   startY = touch.pageY;
   initialPos = currentPosition;   //本次滑动前的初始位置
   viewport.style.webkitTransition = ""; //取消动画效果
   startT = new Date().getTime(); //记录手指按下的开始时间
   isMove = false; //是否产生滑动
}.bind(this),false);

ontouchmove: 获得当前所在位置,计算手指在屏幕上的移动差量deltaX,然后使页面跟随移动

/*手指在屏幕上滑动,页面跟随手指移动*/ document.addEventListener("touchmove",function(e){ e.preventDefault(); var touch = e.touches[0]; var deltaX = touch.pageX - startX; var deltaY = touch.pageY - startY; //如果X方向上的位移大于Y方向,则认为是左右滑动 if (Math.abs(deltaX) > Math.abs(deltaY)){ moveLength = deltaX; var translate = initialPos + deltaX; //当前需要移动到的位置 //如果translate>0 或 if (translate = maxWidth){ //移动页面 this.transform.call(viewport,translate); isMove = true; } direction = deltaX>0?"right":"left"; //判断手指滑动的方向 } }.bind(this),false);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*手指在屏幕上滑动,页面跟随手指移动*/
document.addEventListener("touchmove",function(e){
   e.preventDefault();
   var touch = e.touches[0];
   var deltaX = touch.pageX - startX;
   var deltaY = touch.pageY - startY;
   //如果X方向上的位移大于Y方向,则认为是左右滑动
   if (Math.abs(deltaX) > Math.abs(deltaY)){
       moveLength = deltaX;
       var translate = initialPos + deltaX; //当前需要移动到的位置
       //如果translate>0 或
       if (translate = maxWidth){
           //移动页面
           this.transform.call(viewport,translate);
           isMove = true;
       }
       direction = deltaX>0?"right":"left"; //判断手指滑动的方向
   }
}.bind(this),false);

ontouchend:手指离开屏幕时,计算屏幕最终停留在哪一页。首先计算手指在屏幕上的停留时间deltaT,如果deltaT

(1)如果是快速滑动,则让当前页面完整的停留在屏幕中央(需要计算当前页面还有多少需要滑动)

(2)如果是慢速滑动,还需要判断手指在屏幕上滑动的距离,如果滑动的距离没有超过屏幕宽度50%,则要回退到上一页,相反则要停留在当前页面

/*手指离开屏幕时,计算最终需要停留在哪一页*/ document.addEventListener("touchend",function(e){ e.preventDefault(); var translate = 0; //计算手指在屏幕上停留的时间 var deltaT = new Date().getTime() - startT; if (isMove){ //发生了左右滑动 //使用动画过渡让页面滑动到最终的位置 viewport.style.webkitTransition = "0.3s ease -webkit-transform"; if(deltaT //如果停留时间小于300ms,则认为是快速滑动,无论滑动距离是多少,都停留到下一页 translate = direction == 'left'? currentPosition-(pageWidth+moveLength):currentPosition+pageWidth-moveLength; //如果最终位置超过边界位置,则停留在边界位置 translate = translate > 0 ? 0 : translate; //左边界 translate = translate //右边界 }else { //如果滑动距离小于屏幕的50%,则退回到上一页 if (Math.abs(moveLength)/pageWidth moveLength; }else{ //如果滑动距离大于屏幕的50%,则滑动到下一页 translate = direction == 'left'? currentPosition-(pageWidth+moveLength):currentPosition+pageWidth-moveLength; translate = translate > 0 ? 0 : translate; translate = translate maxWidth : translate; } } //执行滑动,让页面完整的显示到屏幕上 this.transform.call(viewport,translate); } }.bind(this),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
28
29
30
/*手指离开屏幕时,计算最终需要停留在哪一页*/
document.addEventListener("touchend",function(e){
   e.preventDefault();
   var translate = 0;
   //计算手指在屏幕上停留的时间
   var deltaT = new Date().getTime() - startT;
   if (isMove){ //发生了左右滑动
        //使用动画过渡让页面滑动到最终的位置
        viewport.style.webkitTransition = "0.3s ease -webkit-transform";
        if(deltaT //如果停留时间小于300ms,则认为是快速滑动,无论滑动距离是多少,都停留到下一页
            translate = direction == 'left'?
            currentPosition-(pageWidth+moveLength):currentPosition+pageWidth-moveLength;
            //如果最终位置超过边界位置,则停留在边界位置
            translate = translate > 0 ? 0 : translate; //左边界
            translate = translate //右边界
        }else {
            //如果滑动距离小于屏幕的50%,则退回到上一页
            if (Math.abs(moveLength)/pageWidth moveLength;
            }else{
                //如果滑动距离大于屏幕的50%,则滑动到下一页
                translate = direction == 'left'?
                currentPosition-(pageWidth+moveLength):currentPosition+pageWidth-moveLength;
                translate = translate > 0 ? 0 : translate;
                translate = translate  maxWidth : translate;
            }
        }
        //执行滑动,让页面完整的显示到屏幕上
        this.transform.call(viewport,translate);
    }
}.bind(this),false);

除此之外,还要计算当前页面是第几页,并设置当前页码

//计算当前的页码 pageNow = Math.round(Math.abs(translate) / pageWidth) + 1; setTimeout(function(){ //设置页码,DOM操作需要放到子线程中,否则会出现卡顿 this.setPageNow(); }.bind(this),100);

1
2
3
4
5
6
7
//计算当前的页码
pageNow = Math.round(Math.abs(translate) / pageWidth) + 1;
 
setTimeout(function(){
    //设置页码,DOM操作需要放到子线程中,否则会出现卡顿
    this.setPageNow();
}.bind(this),100);

基本的思路就这些,当然在实际操作过程中还有一些细节需要注意,这里就不详细说了,都在代码里能体现出来,源代码已传至Git:https://github.com/git-onepixel/guesture, 有兴趣的同学欢迎一起讨论,(由于时间原因,本示例没有加入history路由),你也可以点击或扫描下面二维码来查看示例效果:

ag真人 6

 

3 赞 24 收藏 2 评论

ag真人 7

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

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

原文出处: naughty   

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

ag真人 8

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

ag真人 9

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

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

 

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

ag真人 10

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

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

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

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

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

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

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

数据加载

第一步是加载数据:

ag真人 12

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

  • 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 评论

ag真人 13

数据准备

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

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

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

这个是Iris数据集的摘要:

ag真人 14

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查询来对数据进行进一步的加工。

ag真人 15

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

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

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

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

 

数据展示

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

ag真人 16

这一步主要是利用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');
  }
});

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

 

几何标记 Geometry

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

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

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

 

图形属性 Attributes

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

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

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

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

 

版权声明:本文由ag真人发布于人才招聘,转载请注明出处:ag真人用500行纯前端代码在浏览器中营造叁个Ta