尽管SVG已经存在了十多年,但由于一些出色的库使开发人员可以轻松使用精美的图表和图形,特别是D3 ,它在Web应用程序中作为一种绘制图表的方法在最近几年变得很流行。 js用于图表, Raphaël用于炫酷的SVG绘图和动画。
最近出现了新的杰出图书馆。 它们为前端开发人员和设计师提供了新的方法和惊人的新功能:
在本文的其余部分,我们将从基础知识开始对Snap.svg进行很好的了解。
如果您没有机会看看拉斐尔(Raphaël) ,那么您可能应该这样做。 这是Dmitry Baranovskiy作为一个单独项目创建的一个不错的JavaScript。 尽管它是作为一个个人项目开始的,但其结果在界面(非常清晰和一致),性能和外观(尤其是动画)方面都非常出色。 该库更侧重于“手绘”图形和动画,而不是图表。 gRaphaël扩展程序后来发布以解决此问题,但它没有像D3那样流行和广泛。
尽管领先于其他图书馆,但随着时间的流逝,Raphaël开始展现其极限。 例如,为了与旧版浏览器兼容,拉斐尔不支持所有会让您的动画脱颖而出的超酷新SVG功能。
这就是为什么其作者决定从一个新项目Snap.svg重新开始的原因,该项目当然会受益于Raphaël的设计经验。 Snap.svg还打破了过去,允许引入一种全新的特殊效果。
在深入研究Snap的语法并开始使用一些示例之前,让我们快速回顾一下该新库的优缺点:
优点:
缺点:
如前所述,Snap使用的是旧版浏览器不支持的功能。 尽管尚未提供完整的更新后的兼容性表,但该库至少应在以下浏览器版本(及更高版本)下正常运行:
从GitHub存储库下载源文件后,您可以将其解压缩并查找dist
文件夹,其中包含已构建的分发文件。 有关使用Grunt构建快照的详细说明,或用于检查最新版本,请在此处查看 。
将文件的缩小版本复制到新项目的js
文件夹中后,只需在HTML页面中包含脚本即可。 假设它位于项目的根目录中,则可以在页面的结束body
标签之前添加以下行:
<script src="/js/snap.svg-min.js"></script>
现在,我们准备为矢量图形创建绘图区域。 我们有两种方法可以做到这一点:
body
)中。 第一种方法允许您在创建JavaScript代码时显式设置表面区域的宽度和高度。 如果您想在表示和内容之间实现更大的分离,可以使用第二种方法,在CSS规则中指定值。 在较高的层次上,第一种方法允许您动态调整工程图表面的外观,但是如果不需要,第二种方法则更符合MVC。 此外,包装是允许您导入和修改使用外部工具创建的SVG工程图的方法,如简介部分所述。
因此,例如,要创建一个新的800 x 600像素的绘图区域,您只需要以下JavaScript行:
var s = Snap(800, 600);
相反,如果您想包装现有的包装,请说#complexSVGfromIllustrator
:
<svg id='complexSVGfromIllustrator' version="1.1" xmlns="http://www.w3.org/2000/svg">
...
</svg>
您仍然可以使用一行JavaScript来导入绘图图面:
var s = Snap('#complexSVGfromIllustrator');
旁注:对于好奇的读者:如果在创建后检查Snap对象,您会注意到它们具有paper
,证明了Raphaël的遗产。
一旦创建了绘图表面即Snap
包装器,就该在其上绘制一些形状了。 假设您想画一个圆:
var paper = Snap('#complexSVGfromIllustrator'),
circle = paper.circle(100, 50, 10);
从docs中可以看到, circle()
方法中的前两个参数是其中心的坐标,而第三个参数是圆的半径。 所有这些参数都是强制性的,如果不提供这些参数,将导致引发错误。 与所有其他绘制方法一样, circle()
方法将返回对对象的引用。
您也可以绘制椭圆 ,如下面的代码示例所示。 这次需要垂直和水平半径。 同样,所有参数都是必需的。
var ellipse = paper.ellipse(100, 50, 10, 20);
如果您想绘制一个矩形 ,请使用以下代码。 这将创建一个矩形,其左上角为(100px,100px),宽度为200px,高度为200px。
var r = paper.rect(100, 100, 200, 300);
rect()
方法的rect()
是它还接受两个可选参数,分别控制垂直轴和水平轴的圆角半径。 这些参数在未通过时默认为0,但是请注意,如果仅通过一个(水平半径),则第二个将不会设置为零,而是两个都将使用相同的值。
var rect = paper.rect(100, 100, 200, 300, 10); //equivalent to paper.rect(100, 100, 200, 300, 10, 10);
现在,如果您想从头开始,则可以创建另一个绘图表面,也可以只使用paper.clear()
方法从paper
擦除所有绘图。
为了涵盖更复杂的图形,我们需要退后一步,并讨论绘图线。 如您所料,该方法采用线端点的四个坐标,如下所示。
var line = paper.line(10, 100, 110, 200);
更有趣的是可以绘制复杂的折线 : var line = paper.polyline(10, 100, 110, 200);
原则上等同于上面的line()
方法,但您可能会对它的视觉效果感到惊讶。 要了解原因,请尝试一下
var p1 = paper.polyline(10, 10, 10, 100, 210, 20, 101, 120);
paper.polyline()
和paper.polygon()
是同一方法的别名,默认情况下,使用黑色填充绘制的(闭合)多边形(无笔画)。 这就是为什么您看不到上面用polyline()
绘制的线的原因(尽管您可以通过检查页面来检查其SVG代码确实已附加到其容器中)。
要更改此行为以及其他元素的外观,我们必须引入属性。
Snap元素的属性概念比平常更宽泛,这意味着它在同一接口下同时包含HTML属性和CSS属性(而大多数其他库在HTML属性的.attr()
方法和'.style()之间有所区别。 '(用于CSS)。 通过在Snap包装器对象上使用element.attr()
方法,可以设置其class
或id
以及其颜色或宽度。
如上所述,使用Snap可以通过两种方式将CSS属性分配给元素。 一种是将这些属性包含在单独的CSS文件中,然后将适当的类分配给您的元素:
.big-circle {
stroke: red;
stroke-width: 2;
fill: yellow;
}
circle.attr({class: 'big-circle'});
通过使用JavaScript分配这些属性,可以获得相同的结果:
circle.attr({
stroke: 'red';
stroke-width: 2;
fill: 'yellow';
});
再者,第一种方法可以更好地分隔内容和表示,而第二种方法则可以动态更改属性。 如果您正在考虑将两种策略混合使用,请记住,尽管CSS文件中的规则在时间上是按顺序分配的,但它们会比您通过element.attr()
分配的规则element.attr()
。
如果您尚未维护对要设置样式的元素的引用,请放心,您可以使用CSS选择器轻松地将其获取:
circle = paper.select('circle'); //First circle in paper's DOM tree
circle = paper.select('circle.big-circle'); //First circle in paper's DOM tree which has class 'big-circle'
circle = paper.select('circle:nth-child(3)'); //Third circle in paper's DOM tree
circle = paper.selectAll('circle.big-circle'); //All circles in paper's DOM tree with class 'big-circle'
可以对SVG元素进行分组,以便可以将公共转换和事件处理更轻松地应用于组中的所有元素。 创建组很容易:
var group = paper.g(circle, rect);
var g2 = paper.group(rect, circle, ellipse); //an alias for paper.g
注意:顺序或参数很重要! 其次,如果将元素分配给组,则该元素将从其可能已经属于的任何组中删除。
当然,元素也可以在创建后添加到现有组中:
group.add(circle);
Snap支持将光栅图像嵌套在SVG元素中,以异步方式加载并仅在加载完成时显示。
var img = paper.image('bigImage.jpg', x, y, width, height);
可以将生成的对象视为SVG元素。 请注意,如果您在图像上使用select()
以后再检索它们,则创建的包装器将是HTML元素的包装器,因此将不支持SVG元素可用的大多数方法。
我们已经看到了如何绘制不对称多边形,例如椭圆和矩形。 但是,基本方法限制我们绘制与笛卡尔坐标轴对齐的图形。 如果我们想绘制一个相对于xy轴旋转45°的椭圆,该怎么办? 我们不能在创建方法中指定此方法,但是可以使用转换来获得相同的结果。
同样,我们可能需要在创建图像后的某个时候旋转图像或移动元素(或组)。 transform()
方法允许我们通过传递SVG转换字符串来做到这一点:
var ellipse = paper.ellipse(100, 50, 10, 20);
ellipse.transform('r45');
此方法可以将字符串或对象作为输入。 我们还可以使用与元素关联的变换矩阵将相同的变换应用于另一个元素:
var e1 = paper.ellipse(100, 50, 10, 20),
e2 = paper.ellipse(200, 50, 12, 24);
e1.transform('r45');
e2.transform(e1.matrix);
请注意:第二个元素的变换中心仍然是第一个元素所使用的中心,因此最终效果可能会让您感到惊讶。
transform()
方法还可以用于检索其调用元素的变换描述符对象-只需不带参数即可调用它。 在嵌套元素的情况下,此描述符可用于检索局部变换矩阵和差异矩阵:
var g1 = paper.group(),
e1 = paper.ellipse(200, 50, 12, 24);
g1.add(e1);
g1.transform('r30');
e1.transform('t64.6447,-56.066r45,0,0');
console.log(e1.transform());
本文介绍了Snap.svg的基础知识。 如果您有兴趣查看最酷的内容,请继续关注,我们将很快发布高级跟踪。
如果您想了解有关数据可视化和Snap的更多信息,这里有一些有用的资源: