最近淘宝了一个PSV
版本的神秘海域,卖家说是繁体跟英文双语言的,结果插卡后一直是英文,始终找不到中文语言的选项。
网上搜索了以下,才发现是跟机器语言绑定的,如果要使用繁体中文版本,必须把机器的语言从简体中文调整到繁体中文才行。
也就是,在PSV
设置里将系统语言设置为繁体中文,再进入游戏里就可以了。
杂记
最近淘宝了一个PSV
版本的神秘海域,卖家说是繁体跟英文双语言的,结果插卡后一直是英文,始终找不到中文语言的选项。
网上搜索了以下,才发现是跟机器语言绑定的,如果要使用繁体中文版本,必须把机器的语言从简体中文调整到繁体中文才行。
也就是,在PSV
设置里将系统语言设置为繁体中文,再进入游戏里就可以了。
前言
日常的开发工作中,为代码添加注释是代码可维护性的一个重要方面,但是仅仅提供注释是不够的,特别是当系统功能越来越复杂,涉及到的模块越来越多的时候,仅仅靠代码就很难从宏观的层次去理解。因此我们需要图例的支持,图例不仅仅包含功能之间的交互,也可以包含复杂的数据结构的示意图,数据流向等。
但是,常用的UML建模工具,如Visio等都略显复杂,且体积庞大。对于开发人员,特别是后台开发人员来说,命令行,脚本才是最友好的,而图形界面会很大程度的限制开发效率。相对于鼠标,键盘才是开发人员最好的朋友。
graphviz简介
本文介绍一个高效而简洁的绘图工具graphviz。graphviz是贝尔实验室开发的一个开源的工具包,它使用一个特定的DSL(领域特定语言): dot作为脚本语言,然后使用布局引擎来解析此脚本,并完成自动布局。graphviz提供丰富的导出格式,如常用的图片格式,SVG,PDF格式等。
在github上fork
出一些感觉比较好的项目,已经做了部分修改,由于某些原因,无法通过pull request
合并到原作者的分支,但是想把原项目的最近更新代码合并进来,可以通过git fetch
原始项目到本地,通过git merge
的进行代码合并。
以fork
出来的pyseeta
项目为例
1 |
$ git checkout https://github.com/wangqiang1588/pyseeta.git |
1 |
$ git remote add upstream https://github.com/TuXiaokang/pyseeta.git |
1 |
$ git fetch upstream |
1 |
$ git checkout master |
1 |
$ git merge upstream/master |
1 |
$ git commit -m "merge ......." |
1 |
$ git push |
最近服务器升级到了Ubuntu 16.04 LTS
,结果遇到了与Ubuntu 12.04通过SFTP更新 WordPress相似的问题,界面中没有出现SSH
的选项,只不过目前(2017.06.04
)最新版本的WordPress
(4.7.5
)一直提示的是"无法连接到服务器
",原因依旧是缺少PHP
的SSH
支持库,总结一下解决方法如下:
1 2 3 4 5 6 7 8 |
$ sudo apt-get install php-ssh2 $ sudo phpenmod ssh2 #如果使用PHP-FPM模块处理PHP协议,则重启PHP-FPM $ sudo service php7.0-fpm restart #如果使用Apache2自身的模块处理PHP协议,则重启Apache2 $ sudo service apache2 restart |
如果更新或者删除插件的时候提示 "未能找到WordPress插件目录
",则参照
Ubuntu 14.04系统WordPress 4.5升级到PHP7之后执行插件升级报错“无法定位WordPress内容目录(wp-content)”中的方法修改即可。
目前(2017.06.09
)依然存在的问题是更新插件的时候,提示拷贝出错。查看系统日志
1 |
$ cat /var/log/apache2/error.log | grep error |
可以看到如下错误信息
1 |
'PHP message: PHP Warning: file_put_contents(ssh2.sftp://Resource id #85/var/www/wordpress/.maintenance): failed to open stream: operation failed in /var/www/wordpress/wp-admin/includes/class-wp-filesystem-ssh2.php on line 253\nPHP message: PHP Warning: file_put_contents(ssh2.sftp://Resource id #90/var/www/wordpress/wp-content/upgrade/wp-statistics.12.0.7-HJk6Bj/wp-statistics/ajax.php): failed to open stream: operation failed in /var/www/wordpress/wp-admin/includes/class-wp-filesystem-ssh2.php on line 253\n', referer: https://www.mobibrw.com/wp-admin/plugins.php |
这个问题属于php-ssh2
自身的BUG
导致的,问题已经确认并修复,但是还没合并到Ubuntu 16.04 LTS
所属的分支上。
目前的解决方法是单独安装Ubuntu 17.04
系统上已经编译好的对应系统的deb
包,然后手工安装更新。
详细的版本信息可以从Ubuntu php-ssh2 package查询各个系统版本上的关于php-ssh2
的包信息。
比如本服务器上更新的命令示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ sudo apt-get install php-ssh2 $ wget https://launchpad.net/ubuntu/+archive/primary/+files/php-ssh2_1.0+0.13-2_amd64.deb $ sudo dpkg -i php-ssh2_1.0+0.13-2_amd64.deb $ rm -rf php-ssh2_1.0+0.13-2_amd64.deb #如果使用PHP-FPM模块处理PHP协议,则重启PHP-FPM $ sudo service php7.0-fpm restart #如果使用Apache2自身的模块处理PHP协议,则重启Apache2 $ sudo service apache2 restart |
如果外网访问不畅通,也可以从本站下载,示例脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ sudo apt-get install php-ssh2 $ wget https://www.mobibrw.com/wp-content/uploads/2017/06/php-ssh2_1.00.13-2_amd64.deb_.zip $ unzip php-ssh2_1.00.13-2_amd64.deb_.zip $ sudo dpkg -i php-ssh2_1.0+0.13-2_amd64.deb $ rm -rf php-ssh2_1.0+0.13-2_amd64.deb $ rm -rf php-ssh2_1.00.13-2_amd64.deb_.zip #如果使用PHP-FPM模块处理PHP协议,则重启PHP-FPM $ sudo service php7.0-fpm restart #如果使用Apache2自身的模块处理PHP协议,则重启Apache2 $ sudo service apache2 restart |
顶层pom中可定义dependencyManagement和dependencies,它们的区别在于
dependencyManagement只是用来管理版本号,防止版本冲突,子pom依赖相应包时,会优先用dependencyManagement指定的版本号。
dependencies是实实在在的下载/依赖,子模块可以直接依赖到
本文大部分内容来源于Hollow官方文档
开发过程中会遇到这样的数据:体量算不上“大数据”,数据在变化,幅度也不大。处理这类数据的时候,一般是把数据放到内存中(容器、json、xml、RDBMS),隔断时间更新一次。
这样处理有很多局限性
世界陷入水火,一般的套路现在主角就该出场了
Netflix推出了Hollow,它是数据的利剑,内存的盾牌,它将不JSON、不XML、少GC、高效率的解决问题,总而言之,他是人民的大救星,下面请一起走进科学,走进Hollow的内心世界。
Hollow致力于解决内存数据问题,处理的量级(将数据转为JSON)一般在GB级别,TB/PB就爱莫能助了。
快照-增量
生产的数据有两种类型,Snapshot、Delta,即全量、增量数据。大多数情况下,我们处理的是增量数据。
生产-消费者模式
一个生产者服务多个消费者,生产者生产快照和增量数据更新至BLOB(二进制大数据)文件,消费者在内存中使用数据,只读属性保证了消费的高效。生产文件到内存对开发者是透明的。
数据模型
数据模型基于一个POJO,又相当于数据库的一行。开发者只需要定义一个POJO,Hollow的API-Generator会生产这个POJO相应的消费文件。
它适用于只读数据、单个生产者、可能多个消费者的情形。Hollow实现持久化的唯一机制是利用BLOB存储,它只是一个文件存储,可能是S3、NFS、甚至一台FTP服务器。启动的时候,消费者们读取整个数据集的快照,并将数据集引导到内存中。通过增量的方法,可以保证内存中的数据集是最新的。对于每个消费者,内存中的数据备份是临时性的,如果消费者重新启动,需要从BLOB存储中重新加载快照。
实际上BLOB快照文件的格式很简单,它在很大程度上和在内存布局的结构相同,因此数据初始化主要是将BLOB的内容直接复制到内存中,这一步可以快速完成,确保了初始化的时间很短。
ObjectMapper写入一个数据,包含一个java.util.Date成员,报Expected class sun.util.calendar.BaseCalendar$Date but object was class java.util.Date错误。
这是hollow v2.1.0以下的一个bug,详见:
https://github.com/Netflix/hollow/issues/13
Thanks very much for the report! I verified the issue and released a fix in v2.1.1. |
有趣的是,将hollow替换成2.1.1,还是报这个错误,替换成2.2.1就好了
这句I verified the issue and released a fix in v2.1.1.像极了研发的口头禅‘下个版本修复’,承包了我一上午的笑点。
Jdom的XmlOutputter默认生成的文件不带换行,所有key-value对写在一行里,使用起来很不方便,XmlOutputter支持设置换行。
jdom1.0中写法如下
XmlOutputter xmlOut = new XmlOutputter(" ", true, "utf-8");
三个参数分别为,缩进(这里是两个空格),是否换行,字符编码
jdom1.0以后不支持上面的写法,而是把三个参数剥离出来,形成了Format类
1 2 3 4 |
Format format = Format.getCompactFormat(); format.setEncoding("utf-8"); format.setIndent(" "); XMLOutputter XMLOut = new XMLOutputter(format); |
项目原因,需要使用web绘制图表,比较靠谱的有echart和d3。
echart:应该是百度的项目,首页做的很漂亮,文档也比较全。个人感觉是一个入手较易,主要在学习怎么配参数,地图是echart的一个亮点。缺点就明显了,入手容易的肯定高度封装过,很难根据自己的需求定制化。而且使用了cavas绘图,在移动端性能不如svg。个人感觉echart适合一些时间比较紧迫,设计没那么明确的场合,直接套上echart,就已经很漂亮了。
d3:大名鼎鼎,受欢迎程度超过jquery,首页的绚丽特效让人眼花,教程的话强烈推荐http://www.ourd3js.com/wordpress/,这个教程好像出了书,叫精通D3.js,基本上把教程撸一遍就没啥大问题了。
d3的优点:用过一段d3后,感觉d3就是svg的jquery,把svg的操作封装为更易用的接口,并提供各种数据可视化的接口。所以d3很灵活,因为你可以用d3在svg中添加一个点、线、饼等等,svg性能不错。链式写法。
缺点:入手稍微慢点,不过撸一遍教程也就一下午的时间。svg的接口网上文档比较少,有些用法stackoverflow都找不到。
如果是学习d3的,推荐前面的教程网站,这里只是个人备忘录,想到哪里说到哪里的流水账,记录那些让人眼前一亮或者眼前一黑的点,以及几个开发中反复查询的用法。
好了,出发
1.
1 2 3 4 |
var svg = d3.select("body") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight); |
首先,你要在一个dom中添加svg,需要两个参数,width和height,一般我是这么给的
1 |
this.svgContent = document.getElementById("id"); |
1 2 |
this.svgWidth = this.svgContent.clientWidth; this.svgHeight = this.svgContent.clientHeight; |
踩坑:需求在指定操作后才绘制该图标,dom display:none的时候,clientWidth和clientHeight是0,所以svg等dom display正常后再添加。
接下来最好设置一下svg的padding,用来应付后面出现的各种文字绘制超出不显示,UI修改等灵细操作。
1 |
this.padding = {left: Ruler.size_1920(200), right: Ruler.size_1920(220), top: Ruler.size_1920(66), bottom: Ruler.size_1920(28)}; |
因为svg直接传的都是px,所以需要根绝屏幕做一下自适应,size_1920函数如下:
1 2 |
let deviceWidth = document.documentElement.clientWidth; return (px * deviceWidth) / 1920; |
坐标轴:
图标中用到最多的是坐标轴,使用坐标轴首先要设定比例尺,目前共用到了4种比例尺
线性比例尺
1 2 3 |
this.yScale = d3.scale.linear() .domain([0, this.threshold]) .range([this.svgHeight - this.padding.top - this.padding.bottom, 0]); |
domain中设置数据的min/max,range中设置svg实际位置。
备注:svg是以左上角为原点的,而常规视角中原点是左下角,所以这里的range起止位置是 svgHeight-0,即起始位置是下边缘y=svgHeight,终止位置是上边缘y=0。这一点需要谨记,后面有许多计算x、y位置的,y的位置总要反着想。其他比例尺也是一样的。
离散比例尺
1 2 3 |
this.xScale = d3.scale.ordinal() .domain(d3.range(data.length)) .rangeRoundBands([0, this.svgWidth - this.padding.left - this.padding.right]); |
or
1 2 3 |
this.xScale = d3.scale.ordinal() .domain(["1","2","3","4"]) .rangeRoundBands([0, this.svgWidth - this.padding.left - this.padding.right]); |
时间比例尺
时间比例尺适用于一些以时间为坐标轴的情况
1 2 3 4 5 6 7 8 9 |
this.now = new Date(); let nextDay = new Date(this.now.getTime() + 1000 * 3600 * 24); // 这里要搞成UTC时间 this.nowUTC = this.now.getUTCFullYear() + "-" + (this.now.getUTCMonth() + 1) + "-" + (this.now.getUTCDate() < 10 ? "0" : "") + this.now.getUTCDate() + "T00:00"; this.nextUTC = nextDay.getUTCFullYear() + "-" + (nextDay.getUTCMonth() + 1) + "-" + (nextDay.getUTCDate() < 10 ? "0" : "") + nextDay.getUTCDate() + "T00:00"; this.xScale = d3.time.scale() .domain([new Date(this.nowUTC), new Date(this.nextUTC)]) .range([0, this.svgWidth - this.padding.left - this.padding.right]); |
备注:比如这里是今天到明天,第二天nextDay的算法,就用秒,其他都不行。UTC时间一定要注意,所有时间都改成UTC时间,所有get一律要加UTC。
颜色比例尺
1 2 |
// 颜色比例尺 var color = d3.scale.category10() // 20 30 都有 |
这是个比较特殊的比例尺,不是用来画坐标轴的,而是把颜色均匀分割,category10,category20等等都有,填一个index,输出一个色值。在有些d3的版本中,颜色比例尺没用放在scale里面。
坐标轴
可能还有更多种类的比例尺,目前还没有接触,下面就开始生成坐标轴数据。
1 2 3 4 5 6 7 8 9 10 |
let gridXAxis = d3.svg.axis() .scale(this.xScale) .orient("top") .innerTickSize(this.svgHeight - this.padding.top - this.padding.bottom) .outerTickSize(0) .ticks(d3.time.hour, 1) .tickFormat(function (d, i) { return ""; }) .tickPadding(10); |
几个常用参数
scale,必要参数,把比例尺填进去
orient,非必填,刻度相对坐标轴的位置 top bottom left right
ticks,非必填,刻度个数 注:ticks只是参考,最终生成刻度个数以数据为准,比如数据需要25个刻度,但ticks填了17,那d3就没办法了,会生成一个d3生成的刻度个数中选跟17最接近的。
innerTickSize,非必填,内刻度高,默认是6。 注:这里设置了innerTickSize是整个svg的高度,和y轴innerTickSize设置整个svg的宽配合行程网格效果,正常的坐标轴就再画一个,注意高度计算时错开
1 2 3 4 5 6 7 |
let xAxis = d3.svg.axis() .scale(this.xScale) .orient("bottom") .ticks(d3.time.hour, 1) .outerTickSize(0) .tickFormat(function (d, i) { }); |
outerTickSize,非必填,外刻度高
tickFormat,非必填,刻度值,默认为空 注:这是一个数据绑定参数,即根据数据生成不同的值
tickPadding,非必填,刻度文字与坐标轴的间距
真正的绘制
1 2 3 4 5 6 7 8 |
this.svg.append("g") .attr("class", "x axis") .attr("transform", "translate(" + this.padding.left + "," + (this.svgHeight - this.padding.top + this.xPadding + this.doubleLinePadding) + ")") .call(xAxis); this.svg.append("g") .attr("class", "x axis grid") .attr("transform", "translate(" + this.padding.left + "," + (this.svgHeight - this.padding.top + this.xPadding) + ")") .call(gridXAxis); |
给svg中添加g元素,call绑定坐标轴数据,attr就是给g元素添加各种属性,class(是不是很熟悉),transform。 注:svg中没有top left margin等属性,大的位移使用transform。
曲线
坐标轴绘制完后,就是曲线的绘制,先生成曲线绘制函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let _line = d3.svg.line() .x(function (d) { let x = self.xScale(d.time); if (x < 0) { return 0; } return self.xScale(d.time); }) .y(function (d) { let x = self.xScale(d.time); if (x < 0) { return 0; } return self.yScale(d.uv); }) // .interpolate("cardinal"); .interpolate("monotone"); |
曲线绘制函数参数
x、y就是根据数据来计算坐标,这里是比例尺大显身手的地方。
interpolate,线段怎么弯曲,讲的最好的是这个地址
http://www.oxxostudio.tw/articles/201411/svg-d3-02-line.html
注:basis曲线保证了优美,没保证曲线与数据的一致,像我这种人是完全无法接受的。
绘制曲线
1 |
let paths = this.svg.selectAll(".data-line").data(data); |
这一行代码解读一下就是选择所有class是data-line的元素,然后给它绑定数据。data-line哪里来的?这里涉及到d3中一个概念,update enter exit
selectAll得到的元素跟数据相比,有三种可能,元素个数 大于/小于/等于数据个数。
数据超出元素个数的部分叫enter,比如第一次绘制时,所有元素都属于paths.enter
1 2 3 4 5 6 7 8 9 10 11 |
paths.enter() .append("path") .attr("class", "data-line gaussian-shadow") .attr("d", function (d) { return _line(d.data); }) .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")") .style("stroke", function (d, i) { return d3.scale.category10(i); }) .attr("fill", "none"); |
paths元素比较简单直接,基本上只需要给一个属性d就能画出来了,d可以用前面的曲线函数直接生成。
stroke,线条颜色,这里可以用颜色比例尺 d3.scale.category10(i)
fill就是填充颜色,paths填充的是一个面。
不超出的部分叫update,即需要更新的部分,paths.update可以看到,也可以直接更新数据,这里是数据驱动的最直观表现
1 2 3 4 5 |
paths.attr("d", function (d) { if (d && d.data) { return _line(d.data); } }); |
数据少于元素个数的部分叫exit,一般exit只有一个用法
1 2 |
// 去掉多余的曲线 paths.exit().remove(); |
到这里,一个坐标轴差不多出来了,但,是不是少了点啥,对,UED最喜欢的灵魂一击,动画。
曲线进入/更新的时候要动画怎么办
d3的动画很好写
1 2 3 4 5 6 7 8 |
paths.transition() .duration(2000) .ease("sin") .attr("d", function (d) { if (d && d.data) { return _line(d.data); } }); |
增加 transition ease 和 delay即可,transition前后是动画的起始和结束状态。
绘制一个圆
1 2 |
let circles = this.svg.selectAll("circle") .data(this.axisData); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
circles.attr("cx", this.xScale(this.now)) .attr("cy", function (d) { if (d && d.data) { return this.yScale(d.data) } }) } circles.exit().remove(); circles.enter() .append("circle") .attr("fill", function (d) { return d.color; }) .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")") // .attr("fill-opacity", "0.4") .attr("cx", this.xScale(this.now)) .attr("cy", function (d) { if (d && d.data) { return this.yScale(d.data) } }) .attr("r", 10); |
矩形
1 2 3 4 5 6 |
let rectContainers = this.svg.selectAll("item-title") .data(data) .enter() .append("g") .attr("class", "item-title") .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")"); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
rectContainers.append("rect") .attr("fill", function (d) { return d.color; }) .attr("x", this.xScale(this.now) + 30) .attr("y", function (d) { if (d && d.data) { return yScale(d.data); } }) .attr("width", function (d) { return 20; }) .attr("height", rectHeight) .attr("rx", rectRadius) .attr("ry", rectRadius) .attr("fill-opacity", 0.8); |
半圆角矩形是用path生成的,顶部圆角矩形
1 2 3 4 5 6 7 8 9 |
var topRoundedRect = function(x, y, width, height, radius) { return "M" + x + "," + y + "v" + ( radius - height) + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + -radius + "h" + (width - 2 * radius) + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius + "v" + (height - radius) + "z"; }; |
1 2 3 4 5 6 7 8 9 |
var rightRoundedRect = function(x, y, width, height, radius) { return "M" + x + "," + y + "h" + (width - radius) + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius + "v" + (height - 2 * radius) + "a" + radius + "," + radius + " 0 0 1 " + -radius + "," + radius + "h" + (radius - width) + "z"; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
this.svg.selectAll(".data-rect") .data(data) .enter() .append("path") .attr("class", "data-rect") .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")") .attr("fill", function(d, i) { return this.colors[i]; }.bind(this)) .attr("d", function (d, i) { let x = this.xScale(i) + this.rectPadding/2; let y = this.yScale(this.yScale.domain()[0]); let radius = 0; let width = this.xScale.rangeBand() - this.rectPadding; if (d.albm_cnt != 0) { radius = this.rectRadius; } let height = this.yScale(this.yScale.domain()[0]) - (this.yScale(d.albm_cnt)) + this.extraHeight; return topRoundedRect(x, y, width, height, radius); }.bind(this)); |
topRoundRect和rightRoundRect乍一看完全搞不懂,这里讲的比较详细https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths,仔细观察下,就是
M 起始x y坐标
h 纵移
v 横移
a 比较复杂 前两个参数是圆角的x y radius
中间三个参数 001是三个flag,只能是0/1 圆角矩形都是001
第一个代表弧线角度是否要大于180
第二个代表正角度还是负角度
第三个代表正方向还是反方向
z是合拢
最后两个参数是终止的x y位置
当前了,还有更多的L C S等等,这里不研究了
文字
1 2 3 4 5 6 7 8 9 10 11 12 |
rectContainers.append("text") .attr("class", "item-text") .attr("fill", "white") .attr("x", this.xScale(this.now) + 30 + 14) .attr("y", function (d) { if (d && d.dataSet) { return self.yScale(AxisView.getPvByTime(d.dataSet, self.now)) + 6; } }) .text(function (d) { return d.type; }); |
到这里,d3绘制的套路差不多都熟悉了,append data enter transform attr,拿来画个饼图吧!
生成饼图数据
1 2 |
let pie = d3.layout.pie().sort(null); this.pieData = pie(percentArray); |
查看一下this.pieData,你会发现数据增加了 startEngle 和 endEngle
注:饼图默认会按大小排序,如果不需要排序,增加一个sort(null)即可
弧线函数
1 2 3 |
this.arc = d3.svg.arc() .innerRadius(0) .outerRadius(self.innerRadius); |
1 2 3 4 5 6 |
let arcs = this.svg.selectAll(".time-pie") .data(this.pieData) .enter() .append("g") .attr("class", "time-pie") .attr("transform", "translate(" + this.centerX + "," + this.centerY + ")"); |
1 2 3 4 5 6 7 |
arcs.append("path") .attr("fill", function (d, i) { return this.colors[i]; }.bind(this)) .attr('d', function(d) { return this.arc(d); }.bind(this)); |
饼图动画不能使用d了,要使用attrTween
简单的饼图动画
1 2 3 4 5 6 7 8 9 10 11 12 |
arcs.append("path") .attr("fill", "#fff") .attr("fill-opacity", 0.2) .transition() .duration(this.fadeInTime) .attrTween('d', function(d) { var i = d3.interpolate(d.startAngle+0.1, d.endAngle); return function(t) { d.endAngle = i(t); return arc(d); }; }.bind(this)); |
高级饼图动画,自己计算delay的时间,让饼一点一点出来
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 |
arcs.append("path") .attr("fill", function (d, i) { return this.colors[i]; }.bind(this)) .transition() .delay(function (d, i) { if (data) { let _percent = 0; for (let j = 0; j < i; j++) { try { _percent += parseInt(data[j].percent) } catch (e) { _percent += 100 / data.length; } } return this.pieAnimTime * _percent / 100; } }.bind(this)) .duration(function (d) { let _time = this.pieAnimTime / data.length; if (d && d.data) { let percent = parseInt(d.data); _time = this.pieAnimTime * percent / 100; } this.lastAnimTime+=_time; return _time; }.bind(this)) .ease("linear") .attrTween('d', function(d) { var i = d3.interpolate(d.startAngle+0.1, d.endAngle); return function(t) { d.endAngle = i(t); return this.arc(d); }.bind(this); }.bind(this)); |
往饼图中间添加一个圆点
1 2 3 4 5 6 7 8 |
this.centriodData = Array.apply([0,0], this.pieData).map(function (val, index) { // return this.arc.centroid(val); return { right: (val.startAngle+val.endAngle)/2<Math.PI, centroidPos: this.arc.centroid(val) }; return this.arc.centroid(val); }.bind(this)); |
注:centriodData和Angle是钝/锐角的计算提出来,是因为在某些低配机子上,偶现绑定计算错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
this.svg.selectAll(".time-circle") .data(this.pieData) .enter().append("circle") .attr("class", "time-circle") .attr("transform", "translate(" + this.centerX + "," + this.centerY + ")") .attr("cx", function(d, i) { // var pos= this.arc.centroid(d); var pos= this.centriodData[i] && this.centriodData[i].centroidPos; return pos && pos[0] * 1.65; }.bind(this)) .attr("cy", function(d, i) { // var pos= this.arc.centroid(d); var pos= this.centriodData[i] && this.centriodData[i].centroidPos; return pos && pos[1] * 1.65; }.bind(this)) .attr("fill", "#fff") // .attr("fill-opacity", 0.1) // .transition() // .duration(this.fadeInTime) .attr("fill-opacity", 1) .attr("r", Ruler.size_1920(6)); |
画一个折线
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 |
let line = this.svg.selectAll(".line") //添加文字和弧之间的连线 .data(this.pieData) //返回是pie(data0) .enter().append("g") .attr("class", "line") .attr("transform", "translate(" + this.centerX + "," + this.centerY + ")") .append("polyline") .attr('points', function(d, i) { // var pos1= this.arc.centroid(d),pos2= this.arc.centroid(d),pos3= this.arc.centroid(d); var pos1=[this.centriodData[i].centroidPos[0], this.centriodData[i].centroidPos[1]] ,pos2= [this.centriodData[i].centroidPos[0], this.centriodData[i].centroidPos[1]],pos3= [this.centriodData[i].centroidPos[0], this.centriodData[i].centroidPos[1]]; pos1[0]*=1.65,pos1[1]*=1.65; pos2[0]*=2,pos2[1]= pos2[1] *2; pos3[0]=(this.centriodData[i].right?this.lineLen:-this.lineLen); pos3[1]= pos3[1]*2; //pos1表示圆弧的中心往上,pos2是圆弧边,pos3就是将pos2平移后得到的位置 //三点链接在一起就成了线段。 return [pos1,pos2,pos3]; }.bind(this)) .style('fill', 'none') .style('stroke', "#fff") .style('stroke-opacity', 0.6) .style('stroke-width', Ruler.size_1920(3) + "px"); // 这里是动画,直线慢慢生成的动画 // .attr("stroke-dasharray", totalLen + " " + totalLen) // .attr("stroke-dashoffset", totalLen) // .transition() // .duration(this.lineAnimTime) // .ease("linear") // .attr("stroke-dashoffset", 0); |
到这里,一般的坐标轴、饼图已经难不倒了,而且自己想添加什么就添加什么,无非就是计算x、坐标,d3就是一堆图形函数,帮你计算坐标而已,那还有什么新东西吗?假如UED说想要阴影,光效,这里介绍一下svg里面一个很牛叉的东西,filter
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 |
let defs = svg.append("defs"); let filter = defs.append("filter") .attr("id", "gaussianShadowFilter"); // append gaussian blur to filter // filter.append("feMorphology") // .attr("operator", "dilate") // .attr("radius", 3); filter.append( 'feGaussianBlur' ) .attr( 'in', 'SourceAlpha' ) .attr( 'stdDeviation', 2 ) // !!! important parameter - blur .attr( 'result', 'blur' ); // append offset filter to result of gaussion blur filter filter.append( 'feOffset' ) .attr( 'in', 'blur' ) .attr( 'dx', 10 ) // !!! important parameter - x-offset .attr( 'dy', 1 ) // !!! important parameter - y-offset .attr( 'result', 'offsetBlur' ); // merge result with original image let feMerge = filter.append( 'feMerge' ); // first layer result of blur and offset feMerge.append( 'feMergeNode' ) .attr( 'in", "offsetBlur' ); // original image on top feMerge.append( 'feMergeNode' ) .attr( 'in', 'SourceGraphic' ); |
实话说,这个东西我也只是到抄过来调参的地步,比如dx dy。理解上只知道特效输出为result,下一个特效的in是这个result,其他也是一问三不知,而且看了之后着实没有深入了解下去的想法,简直就是手写Photoshop。
下面是几个介绍filter的地址,留下以备不时之需
1.https://www.w3.org/TR/SVG/filters.html
2.https://www.smashingmagazine.com/2015/05/why-the-svg-filter-is-awesome/
3.https://jorgeatgu.github.io/svg-filters/
目前,维基百科已经被墙了,但是上面毕竟有很多有用的东西。目前找到比较好用的办法,就是下载zim格式的维基百科的离线文件。
首先访问开源免费软件Kiwix所在的网页,地址为: http://wiki.kiwix.org/wiki/Main_Page/zh-cn 在这个网址中下载阅读软件,也可以在本站下载Kiwix-Windows, Kiwix-Mac
其次,下载维基百科对应语言的快照,zim格式的文件,目前中文语言文件的大小是10GB左右的样子。可以直接在 http://wiki.kiwix.org/wiki/Main_Page/zh-cn 这个网站中下载,也可以访问 https://dumps.wikimedia.org/ 在这网页中选择"Kiwix files"这部分的内容去下载。注意里面包含了几乎所有的语言,只需要选择对应的语言即可。