参考:https://baijiahao.baidu.com/s?id=1591174316579198069&wfr=spider&for=pc
Python绘图库Matplotlib,如何绘制箭头?
参考:http://www.jb51.net/article/132422.htm | 注释文本箭头
代码
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5, 5))
ax.set_aspect(1)
x1 = -1 + np.random.randn(100)
y1 = -1 + np.random.randn(100)
x2 = 1. + np.random.randn(100)
y2 = 1. + np.random.randn(100)
ax.scatter(x1, y1, color="r")
ax.scatter(x2, y2, color="g")
bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9)
ax.text(-2, -2, "Sample A", ha="center", va="center", size=20,
bbox=bbox_props)
ax.text(2, 2, "Sample B", ha="center", va="center", size=20,
bbox=bbox_props)
bbox_props = dict(boxstyle="rarrow", fc=(0.8, 0.9, 0.9), ec="b", lw=2)
t = ax.text(0, 0, "Direction", ha="center", va="center", rotation=45,
size=15,
bbox=bbox_props)
bb = t.get_bbox_patch()
bb.set_boxstyle("rarrow", pad=0.6)
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
plt.show()
------------------------------------------------------------------------------
----------------------------------------------------------------------------
效果
代码
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
#licensing {
fill: green;
}
.link.licensing {
stroke: green;
}
.link.resolved {
stroke-dasharray: 0,2 1;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
font: 12px Microsoft YaHei;
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
.linetext {
font-size: 12px Microsoft YaHei;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// http://blog.thomsonreuters.com/index.php/mobile-patent-suits-graphic-of-the-day/
var links = [
{source: "同花顺", target: "IFIND", type: "resolved", rela:"主营产品"},
{source: "同花顺", target: "手机金融信息服务", type: "resolved", rela:"主营产品"},
{source: "同花顺", target: "互联网金融信息服务", type: "resolved", rela:"主营产品"},
{source: "同花顺", target: "网上行情交易系统", type: "resolved", rela:"主营产品"},
{source: "同花顺", target: "金融资讯及数据服务", type: "resolved", rela:"主营产品"},
{source: "同花顺", target: "互联网金融", type: "resolved",rela:"主营产品"},
{source: "同花顺", target: "金融服务", type: "resolved",rela:"主营产品"},
{source: "手机金融信息服务", target: "金融信息服务", type: "resolved", rela:"上位产品"},
{source: "互联网金融信息服务", target: "金融信息服务", type: "resolved", rela:"上位产品"},
{source: "二三四五002195", target: "金融信息服务", type: "resolved", rela:"主营产品"},
{source: "大智慧601519", target: "金融信息服务", type: "resolved", rela:"主营产品"},
{source: "大智慧601519", target: "互联网金融信息服务", type: "resolved", rela:"主营产品"},
{source: "金融服务", target: "移动互联网", type: "resolved", rela:"上游"},
{source: "金融服务", target: "互联网金融服务", type: "resolved", rela:"下位产品"},
{source: "金融服务", target: "综合金融服务", type: "resolved", rela:"下位产品"}
];
var nodes = {};
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var width = 1560,
height = 800;
var force = d3.layout.force()//layout将json格式转化为力学图可用的格式
.nodes(d3.values(nodes))//设定节点数组
.links(links)//设定连线数组
.size([width, height])//作用域的大小
.linkDistance(180)//连接线长度
.charge(-1500)//顶点的电荷数。该参数决定是排斥还是吸引,数值越小越互相排斥
.on("tick", tick)//指时间间隔,隔一段时间刷新一次画面
.start();//开始转换
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//箭头
var marker=
svg.append("marker")
//.attr("id", function(d) { return d; })
.attr("id", "resolved")
//.attr("markerUnits","strokeWidth")//设置为strokeWidth箭头会随着线的粗细发生变化
.attr("markerUnits","userSpaceOnUse")
.attr("viewBox", "0 -5 10 10")//坐标系的区域
.attr("refX",32)//箭头坐标
.attr("refY", -1)
.attr("markerWidth", 12)//标识的大小
.attr("markerHeight", 12)
.attr("orient", "auto")//绘制方向,可设定为:auto(自动确认方向)和 角度值
.attr("stroke-width",2)//箭头宽度
.append("path")
.attr("d", "M0,-5L10,0L0,5")//箭头的路径
.attr('fill','#000000');//箭头颜色
/* 将连接线设置为曲线
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.style("stroke",function(d){
//console.log(d);
return "#A254A2";//连接线的颜色
})
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
*/
//设置连接线
var edges_line = svg.selectAll(".edgepath")
.data(force.links())
.enter()
.append("path")
.attr({
'd': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
'class':'edgepath',
//'fill-opacity':0,
//'stroke-opacity':0,
//'fill':'blue',
//'stroke':'red',
'id':function(d,i) {return 'edgepath'+i;}})
.style("stroke",function(d){
var lineColor;
//根据关系的不同设置线条颜色
if(d.rela=="上位产品" || d.rela=="上游" || d.rela=="下位产品"){
lineColor="#A254A2";
}else if(d.rela=="主营产品"){
lineColor="#B43232";
}
return lineColor;
})
.style("pointer-events", "none")
.style("stroke-width",0.5)//线条粗细
.attr("marker-end", "url(#resolved)" );//根据箭头标记的id号标记箭头
var edges_text = svg.append("g").selectAll(".edgelabel")
.data(force.links())
.enter()
.append("text")
.style("pointer-events", "none")
//.attr("class","linetext")
.attr({ 'class':'edgelabel',
'id':function(d,i){return 'edgepath'+i;},
'dx':80,
'dy':0
//'font-size':10,
//'fill':'#aaa'
});
//设置线条上的文字
edges_text.append('textPath')
.attr('xlink:href',function(d,i) {return '#edgepath'+i})
.style("pointer-events", "none")
.text(function(d){return d.rela;});
//圆圈
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())//表示使用force.nodes数据
.enter().append("circle")
.style("fill",function(node){
var color;//圆圈背景色
var link=links[node.index];
if(node.name==link.source.name && link.rela=="主营产品"){
color="#F6E8E9";
}else{
color="#F9EBF9";
}
return color;
})
.style('stroke',function(node){
var color;//圆圈线条的颜色
var link=links[node.index];
if(node.name==link.source.name && link.rela=="主营产品"){
color="#B43232";
}else{
color="#A254A2";
}
return color;
})
.attr("r", 28)//设置圆圈半径
.on("click",function(node){
//单击时让连接线加粗
edges_line.style("stroke-width",function(line){
console.log(line);
if(line.source.name==node.name || line.target.name==node.name){
return 4;
}else{
return 0.5;
}
});
//d3.select(this).style('stroke-width',2);
})
.call(force.drag);//将当前选中的元素传到drag函数中,使顶点可以被拖动
/*
circle.append("text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")//在圆圈内添加文字
.text(function(d) {
//console.log(d);
return d.name;
}); */
//圆圈的提示文字
circle.append("svg:title")
.text(function(node) {
var link=links[node.index];
if(node.name==link.source.name && link.rela=="主营产品"){
return "双击可查看详情"
}
});
/* 矩形
var rect=svg.append("rect")
.attr({"x":100,"y":100,
"width":100,"height":50,
"rx":5,//水平圆角
"ry":10//竖直圆角
})
.style({
"stroke":"red",
"stroke-width":1,
"fill":"yellow"
});*/
var text = svg.append("g").selectAll("text")
.data(force.nodes())
//返回缺失元素的占位对象(placeholder),指向绑定的数据中比选定元素集多出的一部分元素。
.enter()
.append("text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")//在圆圈中加上数据
.style('fill',function(node){
var color;//文字颜色
var link=links[node.index];
if(node.name==link.source.name && link.rela=="主营产品"){
color="#B43232";
}else{
color="#A254A2";
}
return color;
}).attr('x',function(d){
// console.log(d.name+"---"+ d.name.length);
var re_en = /[a-zA-Z]+/g;
//如果是全英文,不换行
if(d.name.match(re_en)){
d3.select(this).append('tspan')
.attr('x',0)
.attr('y',2)
.text(function(){return d.name;});
}
//如果小于四个字符,不换行
else if(d.name.length<=4){
d3.select(this).append('tspan')
.attr('x',0)
.attr('y',2)
.text(function(){return d.name;});
}else{
var top=d.name.substring(0,4);
var bot=d.name.substring(4,d.name.length);
d3.select(this).text(function(){return '';});
d3.select(this).append('tspan')
.attr('x',0)
.attr('y',-7)
.text(function(){return top;});
d3.select(this).append('tspan')
.attr('x',0)
.attr('y',10)
.text(function(){return bot;});
}
//直接显示文字
/*.text(function(d) {
return d.name; */
});
/* 将文字显示在圆圈的外面
var text2 = svg.append("g").selectAll("text")
.data(force.links())
//返回缺失元素的占位对象(placeholder),指向绑定的数据中比选定元素集多出的一部分元素。
.enter()
.append("text")
.attr("x", 150)//设置文字坐标
.attr("y", ".50em")
.text(function(d) {
//console.log(d);
//return d.name;
//return d.rela;
console.log(d);
return '1111';
});*/
function tick() {
//path.attr("d", linkArc);//连接线
circle.attr("transform", transform1);//圆圈
text.attr("transform", transform2);//顶点文字
//edges_text.attr("transform", transform3);
//text2.attr("d", linkArc);//连接线文字
//console.log("text2...................");
//console.log(text2);
//edges_line.attr("x1",function(d){ return d.source.x; });
//edges_line.attr("y1",function(d){ return d.source.y; });
//edges_line.attr("x2",function(d){ return d.target.x; });
//edges_line.attr("y2",function(d){ return d.target.y; });
//edges_line.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; });
//edges_line.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; });
edges_line.attr('d', function(d) {
var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
return path;
});
edges_text.attr('transform',function(d,i){
if (d.target.x<d.source.x){
bbox = this.getBBox();
rx = bbox.x+bbox.width/2;
ry = bbox.y+bbox.height/2;
return 'rotate(180 '+rx+' '+ry+')';
}
else {
return 'rotate(0)';
}
});
}
//设置连接线的坐标,使用椭圆弧路径段双向编码
function linkArc(d) {
//var dx = d.target.x - d.source.x,
// dy = d.target.y - d.source.y,
// dr = Math.sqrt(dx * dx + dy * dy);
//return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
//打点path格式是:Msource.x,source.yArr00,1target.x,target.y
return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y
}
//设置圆圈和文字的坐标
function transform1(d) {
return "translate(" + d.x + "," + d.y + ")";
}
function transform2(d) {
return "translate(" + (d.x) + "," + d.y + ")";
}
</script>
------------------------------------------
效果
代码
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
def f1(t):
return np.exp(-t)*np.cos(2*np.pi*t)
def f2(t):
return np.sin(2*np.pi*t)*np.cos(3*np.pi*t)
t = np.arange(0.0,5.0,0.02)
plt.figure(figsize=(8,7),dpi=98)
p1 = plt.subplot(211)
p2 = plt.subplot(212)
label_f1 = "$f(t)=e^{-t} \cos (2 \pi t)$"
label_f2 = "$g(t)=\sin (2 \pi t) \cos (3 \pi t)$"
p1.plot(t,f1(t),"g-",label=label_f1)
p2.plot(t,f2(t),"r-.",label=label_f2,linewidth=2)
p1.axis([0.0,5.01,-1.0,1.5])
p1.set_ylabel("v",fontsize=14)
p1.set_title("A simple example",fontsize=18)
p1.grid(True)
#p1.legend()
tx = 2
ty = 0.9
p1.text(tx,ty,label_f1,fontsize=15,verticalalignment="top",horizontalalignment="right")
p2.axis([0.0,5.01,-1.0,1.5])
p2.set_ylabel("v",fontsize=14)
p2.set_xlabel("t",fontsize=14)
#p2.legend()
tx = 2
ty = 0.9
p2.text(tx,ty,label_f2,fontsize=15,verticalalignment="bottom",horizontalalignment="left")
p2.annotate('',xy=(1.8,0.5),xytext=(tx,ty),arrowprops=dict(arrowstyle="->",connectionstyle="arc3"))
plt.show()
-------------------------------------------------------------------------------
效果
代码
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>test4</title>
<style>
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
marker#licensing {
fill: green;
}
path.link.licensing {
stroke: green;
}
path.link.resolved {
stroke-dasharray: 0,2 1;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
</style>
</head>
<body>
<div id="chart"></div>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript">
var index = 0;
var width = 1140,
height = 1000;
var links = [
// {source: "Microsoft", target: "Amazon", type: "suit"},
// {source: "Microsoft", target: "Amazon", type: "suit"},
// {source: "Microsoft", target: "Amazon", type: "suit"},
// {source: "Samsung", target: "Apple", type: "suit"},
// {source: "Samsung", target: "Samsung", type: "suit"},
{source: "a", target: "b", type: "suit"},
{source: "a", target: "b", type: "suit"},
{source: "a", target: "b", type: "suit"},
{source: "a", target: "b", type: "suit"},
{source: "a", target: "a", type: "suit"},
{source: "a", target: "a", type: "suit"},
{source: "b", target: "a", type: "suit"},
{source: "b", target: "a", type: "suit"},
// {source: "Amazon", target: "Microsoft", type: "suit"}
];
//关系分组
var linkGroup = {};
//对连接线进行统计和分组,不区分连接线的方向,只要属于同两个实体,即认为是同一组
var linkmap = {}
for(var i=0; i<links.length; i++){
var key = links[i].source<links[i].target?links[i].source+':'+links[i].target:links[i].target+':'+links[i].source;
if(!linkmap.hasOwnProperty(key)){
linkmap[key] = 0;
}
linkmap[key]+=1;
if(!linkGroup.hasOwnProperty(key)){
linkGroup[key]=[];
}
linkGroup[key].push(links[i]);
}
//为每一条连接线分配size属性,同时对每一组连接线进行编号
for(var i=0; i<links.length; i++){
var key = links[i].source<links[i].target?links[i].source+':'+links[i].target:links[i].target+':'+links[i].source;
links[i].size = linkmap[key];
//同一组的关系进行编号
var group = linkGroup[key];
var keyPair = key.split(':');
var type = 'noself';//标示该组关系是指向两个不同实体还是同一个实体
if(keyPair[0]==keyPair[1]){
type = 'self';
}
//给节点分配编号
setLinkNumber(group,type);
}
console.log(links);
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(600)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height);
// Per-type markers, as they don't inherit styles.
svg.append("svg:defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 30)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 20)
.call(force.drag);
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter().append("svg:g");
// A copy of the text with a thick white stroke for legibility.
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.attr("class", "shadow")
.text(function(d) { return d.name; });
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; });
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function(d) {
//如果连接线连接的是同一个实体,则对path属性进行调整,绘制的圆弧属于长圆弧,同时对终点坐标进行微调,避免因坐标一致导致弧无法绘制
if(d.target==d.source){
dr = 30/d.linknum;
return"M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 1,1 " + d.target.x + "," + (d.target.y+1);
}else if(d.size%2!=0 && d.linknum==1){//如果两个节点之间的连接线数量为奇数条,则设置编号为1的连接线为直线,其他连接线会均分在两边
return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
}
//根据连接线编号值来动态确定该条椭圆弧线的长半轴和短半轴,当两者一致时绘制的是圆弧
//注意A属性后面的参数,前两个为长半轴和短半轴,第三个默认为0,第四个表示弧度大于180度则为1,小于则为0,这在绘制连接到相同节点的连接线时用到;第五个参数,0表示正角,1表示负角,即用来控制弧形凹凸的方向。本文正是结合编号的正负情况来控制该条连接线的凹凸方向,从而达到连接线对称的效果
var curve=1.5;
var homogeneous=1.2;
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx*dx+dy*dy)*(d.linknum+homogeneous)/(curve*homogeneous);
//当节点编号为负数时,对弧形进行反向凹凸,达到对称效果
if(d.linknum<0){
dr = Math.sqrt(dx*dx+dy*dy)*(-1*d.linknum+homogeneous)/(curve*homogeneous);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,0 " + d.target.x + "," + d.target.y;
}
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
function setLinkNumber(group,type){
if(group.length==0) return;
//对该分组内的关系按照方向进行分类,此处根据连接的实体ASCII值大小分成两部分
var linksA = [], linksB = [];
for(var i = 0;i<group.length;i++){
var link = group[i];
if(link.source < link.target){
linksA.push(link);
}else{
linksB.push(link);
}
}
//确定关系最大编号。为了使得连接两个实体的关系曲线呈现对称,根据关系数量奇偶性进行平分。
//特殊情况:当关系都是连接到同一个实体时,不平分
var maxLinkNumber = 0;
if(type=='self'){
maxLinkNumber = group.length;
}else{
maxLinkNumber = group.length%2==0?group.length/2:(group.length+1)/2;
}
//如果两个方向的关系数量一样多,直接分别设置编号即可
if(linksA.length==linksB.length){
var startLinkNumber = 1;
for(var i=0;i<linksA.length;i++){
linksA[i].linknum = startLinkNumber++;
}
startLinkNumber = 1;
for(var i=0;i<linksB.length;i++){
linksB[i].linknum = startLinkNumber++;
}
}else{//当两个方向的关系数量不对等时,先对数量少的那组关系从最大编号值进行逆序编号,然后在对另一组数量多的关系从编号1一直编号到最大编号,再对剩余关系进行负编号
//如果抛开负号,可以发现,最终所有关系的编号序列一定是对称的(对称是为了保证后续绘图时曲线的弯曲程度也是对称的)
var biggerLinks,smallerLinks;
if(linksA.length>linksB.length){
biggerLinks = linksA;
smallerLinks = linksB;
}else{
biggerLinks = linksB;
smallerLinks = linksA;
}
var startLinkNumber = maxLinkNumber;
for(var i=0;i<smallerLinks.length;i++){
smallerLinks[i].linknum = startLinkNumber--;
}
var tmpNumber = startLinkNumber;
startLinkNumber = 1;
var p = 0;
while(startLinkNumber<=maxLinkNumber){
biggerLinks[p++].linknum = startLinkNumber++;
}
//开始负编号
startLinkNumber = 0-tmpNumber;
for(var i=p;i<biggerLinks.length;i++){
biggerLinks[i].linknum = startLinkNumber++;
}
}
}
</script>
</body>
</html>
------------------------------------------------------------------------
参考
http://wow.techbrood.com/fiddle/33779
效果
代码
js
let c = document.getElementById("theCanvas");
let ctx = c.getContext("2d"),
halfWidth = window.innerWidth / 2,
halfHeight = window.innerHeight / 2;
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
c.addEventListener('click', addDot);
let prevX = 0,
prevY = 0,
cx = 0,
cy = 0;
function addDot(e) {
let size = 0;
ctx.fillStyle = 'white';
ctx.strokeStyle = 'white';
ctx.beginPath();
if (e.x && e.y) {
drawDot(e.x, e.y, size);
} else {
drawDot(e.clientX, e.clientY, size);
}
}
function drawDot(x, y, s) {
if (s !== 10) {
ctx.arc(x, y, s, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
s++;
requestAnimationFrame(function() {
drawDot(x, y, s);
});
} else {
if (prevX !== 0 && prevY !== 0) {
addLine(x, y, prevX, prevY);
} else {
prevX = x;
prevY = y;
}
}
}
function addLine(x, y, px, py) {
let goingForward = false,
goingUp = false;
ctx.beginPath();
ctx.moveTo(px, py);
cx = px;
cy = py;
if (cx < x) {
goingForward = true;
}
if (cy > y) {
goingUp = true;
}
drawLine(x, y, goingForward, goingUp);
}
function drawLine(x, y, gf, gu) {
let metX = false,
metY = false;
c.removeEventListener('click', addDot);
ctx.lineTo(cx, cy);
ctx.stroke();
if (gf) {
if (cx >= x) {
metX = true;
}
if (cx < x && !metX) {
cx += 4;
}
} else {
if (cx <= x) {
metX = true;
}
if (cx > x && !metX) {
cx -= 4;
}
}
if (gu) {
if (cy <= y) {
metY = true;
}
if (cy > y && !metY) {
cy -= 4;
}
} else {
if (cy >= y) {
metY = true;
}
if (cy < y && !metY) {
cy += 4;
}
}
if (!metY || !metX) {
requestAnimationFrame(function() {
drawLine(x, y, gf, gu);
});
} else {
prevX = x;
prevY = y;
c.addEventListener('click', addDot);
}
}
css
@import url("https://fonts.googleapis.com/css?family=Karla");
body {
width: 100%;
height: 100%;
background: #2575f7;
}
#theCanvas {
width: 100%;
height: 100%;
}
div#footer {
position: fixed;
width: 100%;
bottom: 0;
left: 0;
color: white;
}
h3 {
text-align: center;
font-family: 'Karla', sans-serif;
}
html
<canvas id="theCanvas"></canvas>
<div id="footer">
<h3>点击开始连线动画</h3>
</div>