Same hover function for multiple paths in Raphael

后端 未结 4 1053
情书的邮戳
情书的邮戳 2021-02-06 11:27

So I\'ve got my canvas and my paths:

var paper1 = Raphael(10, 50, 960, 560);

var mapShape1 = paper1.path(\"M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.4         


        
4条回答
  •  野趣味
    野趣味 (楼主)
    2021-02-06 11:46

    As said by lib3d, you should use a Set. However, rather than using forEach to loop over the set contents and apply attributes/functionality, shared attributes/functionality can be added on the set itself, which will apply it on the contents of the set. More on that later, first look at how you can create sets.

    Set Handling

    There are two ways to create a set and add elements to it: explicit and implicit.

    Explicit

    This means that you manage the set yourself, and add elements to the set yourself`

    var paper, shapeA, shapeB, shapeC, elementSet;
    
    paper = Raphael(10, 50, 960, 560);
    elementSet = paper.set();
    
    shapeA = paper.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z");
    shapeB = paper.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z");
    shapeC = paper.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z");
    
    // now add A and C to the set, as well as a rectangle
    elementSet.push(
        shapeA,
        shapeC,
        paper.rect(10, 10, 10, 10, 2)
    );
    

    This way you are in full control over what enters the set and what not.

    Implicit

    You also have the ability to mark start and endpoints when drawing elements. Any element that is drawn between the start and endpoint, is added to the set.

    var paper, shapA, shapeB, shapeC, elementSet;
    
    paper = Raphael(10, 50, 960, 560);
    
    paper.setStart();
    
    shapeA = paper.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z");
    shapeB = paper.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z");
    shapeC = paper.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z");
    
    paper.rect(10, 10, 10, 10, 2);
    
    elementSet = paper.setFinish();
    

    The variable elementSet now contains the shapes A, B and C as well as a rectangle.

    Explicit or Implicit?

    Personally I would advice to always use the explicit method. That way you have 100% control over what enters your set and what not. Also, I find the setStart() and setFinish() to be named backwards, we're "starting" with a "set", we're not "setting" a "start". This might be obvious if you now the intents, but that is exactly the danger of ambiguous naming - the next dev might not know and assume something different.

    More usages

    For an application we created, we had to draw, remove, update and reposition complex groups of elements. In order to achieve this, we made heavy use of sets. Bar the fact that sets allow you to apply attributes on every element in a set, a set also allows you to use it as a DTO.

    For instance the following works:

    var elementSet = paper.set();
    
    elementSet.push(elemA, elemB, elemC);
    elementSet.myApp.someDTO = {
        property: value,
        something: else
    };
    

    I tend to use the myApp as a namespace, for consistency and clarity. The beauty of it is that even if someDTO contains Raphael elements, anything you apply on the set, will not be applied on the elements in the DTO. This makes it really usable to pass around context, coordinates, etc should you need to.

    Using sets

    Now back to the benefit of using sets. Let us review your usecase here: you want to apply attributes and hover to an arbitrary amount of paths.

    If we create a set as in the explicit example above, we end up with the following:

    var paper, elementSet;
    paper = Raphael(10, 50, 960, 560);
    elementSet = paper.set();
    elementSet.push(
        paper.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z"),
        paper.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z"),
        paper.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z"),
    );
    

    Now apply the styling on the set:

    elementSet.attr({
        fill: '#33CCFF',
        stroke: '#000000',
        'stroke-width': 5
    });
    

    And then add the hover:

    elementSet.hover(
        function(){
            this.animate({
                "fill": "#FF3300"
            }, 500);
        },
        function(){
            this.animate({
                "fill": "#33CCFF"
            }, 500)
        }
    );
    

    Sets also support chaining, as elements do:

    elementSet.push(
        /* elements */
    ).attr({
        /* attributes */
    }).hover(
        /* hover fn's
    );
    

    To view the final result, there's a fiddle here

    Expanded hover functionality

    Should you want to apply the onhover highlight to all elements, you could just apply the attributes on the set again:

    onMouseOver: function () {
        elementSet.animate({
            fill: '#FF3300'
        }, 500);
    };
    onMouseOut: function () {
        elementSet.animate({
            fill: '#33CCFF'
        }, 500);
    };
    
    elementSet.hover(onMouseOver, onMouseOut);
    

    A fiddle to view this can be found here

    Using jQuery

    In order to be able to bind hover functionality through jQuery, one must access the nodes of the elements. The elements themselves are not DOM nodes, rather Raphael objects. Through using the element.node one can use jquery on that node to add behaviour. My personal experience is that this works decent, however you never ever want to modify the node through jquery as that can lead to really unexpected behavior. Raphael provides about all the functionality you need, using jquery shouldn't be needed.

提交回复
热议问题