snap.svg.js_高级Snap.svg

snap.svg.js

在较早的帖子中 ,我们已经了解了如何开始使用Snap.svg。 在本文中,我们将仔细研究第一篇文章中提到的新功能。

掩蔽

让我们从回忆起如何创建绘图表面,简单形状并加载图像开始:

var paper = Snap(800, 600),
    img = paper.image('bigImage.jpg', 10, 10, 300, 300),
    bigCircle = s.circle(150, 150, 100);

圆圈暂时覆盖了图像的中心。

可惜的是,您只能拥有矩形图像。 也许您的设计师创建了漂亮的圆形按钮或图像。 当然,有几种解决方案,但是它们都给您带来了另一个问题:最佳情况是,设计人员可以为您提供图像,使其外部与页面背景相匹配,从而使其看起来呈圆形。 但是,假设您的背景是纯色,则必须更改其颜色,则必须编辑图像。 您可以使用透明性,但要么需要PNG等较重的格式,要么需要使用GIF降低质量。 也许几年后,所有浏览器将完全支持WebP ,这将解决这个难题。 无论哪种方式,如果您需要图像的交互性,您都将陷入一个矩形,以响应诸如mouseentermouseoutclick等事件。

过去使用Flash已有很长时间,因此SVG中最令人沮丧的事情之一就是无法使用SVG 1.1中引入的masks )。 在Snap中,将遮罩应用于任何元素(包括图像)非常简单:

bigCircle.attr('fill', '#fff'); //This is IMPORTANT

img.attr({
    mask: bigCircle
});

基本上,我们只需要将mask属性分配给我们的元素。 我们必须小心用作实际遮罩的元素。 由于最终元素的不透明度将与遮罩元素中的白色水平成正比,因此,如果我们要使图像具有完全的不透明度,则必须用白色填充圆圈。 乍一看这很烦人,但它为惊人的效果提供了许多可能性,我们将在下一部分中看到。

您显然可以将不同的形状组合在一起以创建复杂的蒙版。 Snap提供了一些语法糖来帮助您:

var smallRect = paper.rect(180, 30, 50, 40),
    bigCircle = paper.circle(150, 150, 100),
    mask = paper.mask(bigCircle, smallRect);

mask.attr('fill', 'white');

img.attr({
    mask: mask
});

Paper.mask()方法等效于Paper.g() ,并且实际上可以用它无缝替换。

剪裁

剪切路径限制了可以应用绘画的区域,因此不会绘制当前受当前剪切路径限制的区域之外的图形的任何部分。 可以将剪切路径视为具有可见区域(在剪切路径内)的Alpha值为1,而隐藏区域的Alpha值为0的蒙版。一个区别是,尽管蒙版隐藏的区域仍会响应事件,剪裁区域不会。

Snap没有剪切的快捷方式,但是您可以使用attr()方法设置任何元素的clipclip-pathclip-route属性。

渐变色

SVG 1.1允许使用渐变填充形状。 当然,如果使用这些形状填充蒙版,则可以通过更改蒙版的填充来利用可能性来指定最终图形的Alpha级别,并创建惊人的效果。 Snap提供了创建渐变的快捷方式,以后可以将其分配给其他元素的fill属性。 如果我们稍微修改一下前面的代码,例如:

var gradient = paper.gradient('r()#fff-#000');
mask.attr('fill', gradient);

如果您测试此代码,最终效果将与您预期的完全不同。 那是因为我们使用了相对辐射梯度类型 ,由上面的小写字母“ r”表示。 为组中的每个元素分别创建相对梯度(作为复合蒙版)。 如果您希望整个组使用单个渐变,则可以使用命令的绝对版本。 'R()#fff-#000'是一个绝对辐射渐变,从中间的白色填充开始,到边界处的黑色退化。

通过为任何元素的fill属性指定SVG梯度,我们可以获得相同的结果:

mask.attr('fill', 'L(0, 0, 300, 300)#000-#f00:25-#fff');

在最后一个示例中,我们显示了更复杂的渐变。 除了不同的类型( 绝对线性 )外,此梯度从(0,0)到(300,300),从黑色到红色(25%)到白色。

gradient()方法接受一个字符串。 Snap的文档中说明了更多详细信息。

也可以使用页面中任何svg元素的现有渐变:


    
      
        
        
      
    
  
paper.circle(50, 50, 50, 50).attr('fill', Snap('#svg-test').select('#MyGradient'));

模式

通过重复出现其他svg形状,渐变或图像,图案可以填充形状。 Snap提供了Element.toPattern()方法(以前已弃用pattern() ,该方法可从任何Snap元素中创建图案。

创建模式并用它填充元素非常简单:

var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
                      fill: "none",
                      stroke: "#bada55",
                      strokeWidth: 5
                  }).toPattern(0, 0, 10, 10),
    c = paper.circle(200, 200, 100).attr({
                                            fill: p
                                        });

相反,如果我们想将渐变和图案结合起来,那就是另一回事了,而稍微复杂一点!
作为示例,让我们看一下如何创建一个结合了辐射渐变和类似于上面的图案的蒙版:

//assuming the shapes bigCircle and smallRect have already been defined, as well as 'paper'
var mask = paper.g(bigCircle, smallRect),
    gradient = paper.gradient("R()#fff-#000"),
    pattern = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
        fill: "none",
        stroke: "#bada55",
        strokeWidth: 5
    }).toPattern(0, 0, 10, 10);

mask.attr('fill', pattern); //we need to set this before calling clone!
mask.attr({
    mask: mask.clone()      //makes a deep copy of current mask
});

img.attr({
    mask: mask
});

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

免费获得这本书

我们基本上必须创建一个两级地图。 我们在图像上使用的最终地图(由渐变填充)本身具有由渐变填充的地图。 结果非常令人印象深刻! 事实证明,这也是向您介绍clone()方法的好机会,该方法可以实现您想像的效果–创建被调用元素的深层副本。

动画制作

动画是Snap.svg最好的制作功能之一。 有几种处理动画的方法,它们的行为略有不同。

Element.animate()

我们将从最简单的动画方法Element.animate() 。 此方法使用户可以为任意数量的元素属性设置动画,并且全部同步。 该属性的初始值当然是其当前值,而最后一个是在animate()的第一个参数中指定的。 除了要更改的属性外,还可以传递动画的持续时间,动画的易用性以及动画完成后将调用的回调。

一个例子将使一切变得更清晰:

bigCircle.animate({r: 10}, 2000);

这只会在两秒钟内将蒙版中的大圆圈缩小到较小的半径。

Set.animate()

您可以单独为组(集合)中的元素设置动画。 但是,如果要同步设置整个动画,该怎么办? 简单! 您可以使用Set.animate() 。 这将对集合中的所有元素应用相同的变换,从而确保各种动画之间的同步,并通过将所有更改集中在一起来增强性能。

mask.animate({'opacity': 0.1}, 1000);

您还可以独立但同步地设置集中的每个元素的动画。 Set.animate()接受可变数量的参数,因此您可以为需要设置动画的每个子元素传递带有参数的数组:

var set = mask.selectAll('circle');  //Create a set containing all the circle elements in mask's subtree (1 element)
paper.selectAll('rect')                //Select all the rect in the drawing surface (2 elements)
        .forEach(function(e) {set.push(e);}); //Add each of those rectangles to the set previously defined
set.animate([{r: 10}, 500], [{x: 20}, 1500, mina.easein], [{x: 20}, 1500, mina.easein]); //Animate the three elements in the set

假设到目前为止您已经正确地遵循了我们的示例代码(在CodePen上尝试 ),在浏览器控制台中运行上面的代码,您将看到三个元素如何同步但独立地动画化。 上面的代码也有机会介绍集合(作为select()selectAll()方法的结果)以及在其上定义的一些有用方法的机会。

创建集合的另一种方法是将元素数组传递给Snap构造函数方法:

var set2 = Snap([bigCircle, smallRect]);

Snap.animate()

您可以设置任何数字属性的animate() ,但是animate()在其他类型上不起作用,例如,如果尝试为其text属性设置动画,它将使text元素混乱。 还有另一种获得这种效果的方法,即在Snap中调用animate()的第三种方法。

通过调用Snap对象的animate方法,可以更详细地指定将在动画的每个步骤执行的动作。 这有助于将复杂的动画分组在一起并同步运行它们(尽管Set.animate()方法将是解决此问题的正确方法),以及对复杂的非数字属性进行动画处理。

例如,让我们创建一个文本元素并为其设置动画:

var labelEl = paper.text(300, 150, "TEST"),
    labels = ["TEST", "TETT","TEUT","TEVT","TEXT","TES-","TE--","T---", "----", "C---", "CH--", "CHE-", "CHEC-", "CHECK"];
Snap.animate(0, 13, function (val) {
    labelEl.attr({
        text: labels[Math.floor(val)]
    });
}, 1000);

事件处理

回到蒙版和图像之间的初始比较,您可以获得与上一节中显示的动画gif(类似)相同的效果。 但是,如果您想响应用户交互来重现此行为,则使用SVG进行的改进就更加重要。 您仍然可以找到一种使用多个gif使其工作的方法,但是,除了失去灵活性之外,您将无法通过很少的努力获得相同的质量:

img.click(function(evt) {
    this.minified = !this.minified;
    bigCircle.animate({
        r: !this.minified ? 100 : 10
    }, 1500);
});

以后可以使用Element.unclick()方法删除Click处理程序。

可以类似处理的其他事件包括dblclickmousedownmouseupmousemovemouseoutmouseover ,以及许多面向移动的事件,例如touchstarttouchend

对于那些习惯使用jQuery或D3接口的读者来说,Snap中没有on()方法可以手动处理其他事件。 如果您需要超越Snap提供的处理程序的自定义行为,则可以检索任何元素的node属性,而该属性又包含对关联的DOM元素的引用,并且(可以将其包装在jQuery中)可以添加处理程序及其属性直接:

img.node.onclick = function () {
    img.attr("opacity", 0.1);
};

拖放

Snap使使用Element.drag()方法为任何元素,组或集合激活拖放特别容易。 如果不需要任何自定义行为,则可以不带任何参数地调用它:

labelEl.drag();   //handle drag and drop for you

但是,如果您需要某些特殊行为,则可onmoveondragstartondragend事件传递自定义回调和上下文。 请注意,如果要传递下一个回调,则不能省略onmove回调。

添加拖动处理程序不会隐藏click事件,除非明确阻止,否则将在ondragend事件之后触发该ondragend

加载现有的SVG

这个功能强大的库最吸引人的地方之一就是它支持重用现有的SVG代码。 您可以将其“插入”为字符串,甚至更好的是,您可以读取现有文件,然后进行更改。

您可以自己尝试。 将此精美的svg图下载并保存到项目的根目录中。 接下来,将其加载到您的页面中,根据需要更改其样式或结构,甚至在将其添加到DOM树,添加事件处理程序等之前也是如此。

Snap.load('ringing-phone.svg', function (phone) {
    // Note that we traverse and change attr before SVG is even added to the page (improving performance)
    phone.selectAll("path[fill='#ff0000']").attr({fill: "#00ff00"});
    var g = phone.select("g");
    paper.append(g);    //Now we add the SVG element to the page
});

注意 :由于浏览器中的同源策略,您需要在本地服务器中运行示例以测试加载方法。

性能改进

处理DOM时提高性能的一种方法是使用DocumentFragments 。 片段是DOM节点的最小容器。 它们是几年前推出的,它们使您可以廉价地操作整个子树,然后使用2个方法调用(而不是n克隆并向我们的页面添加具有n节点的整个子树。 实际差异将在John Resig的博客中详细说明。

Snap还可以通过两种方法原生使用片段:

  1. Snap.parse(svg)接受一个参数,即带有SVG代码的字符串,对其进行解析并返回一个片段,该片段以后可以附加到任何绘图表面。

  2. Snap.fragment(varargs)接收可变数量的元素或字符串,并创建一个包含所有提供的元素的片段。

尤其是对于大型svg图纸,如果使用得当,碎片可能会大大节省性能。

结论

到此结束我们有关高级Snap.svg的文章。 现在,读者应该对使用此库可以做什么以及如何做有一个清晰的认识。 如果您有兴趣学习更多信息,则Snap文档是一个不错的起点。

几个有用的链接:

  • Snap.svg 教程 。
  • Snap.svg 文档 。

翻译自: https://www.sitepoint.com/advanced-snap-svg/

snap.svg.js

你可能感兴趣的:(java,python,javascript,js,面试,ViewUI)