Search code examples
csshtmlwebkit

Display arrow head for an animated line


I am animating a line from Point 1 to Point 2 with https://jsfiddle.net/arungeorgez/r9x6vhcb/4/. How do I add an arrowhead to the same line?

<style>
  .key-anim1 {
    -webkit-animation: Drawpath 1s linear forwards;
    animation: Drawpath 1s linear forwards;
    stroke-dasharray: 0, 600;
  }

  @-webkit-keyframes Drawpath {
    from {
      stroke-dasharray: 0, 600;
    }
    to {
      stroke-dasharray: 600, 600;
    }
  }
  @keyframes Drawpath {
    from {
      stroke-dasharray: 0, 600;
    }
    to {
      stroke-dasharray: 600, 600;
    }
  }
  </style>

Solution

  • <animate attributeType="XML" 
             attributeName="opacity" 
             from="0" to="1" 
             dur=".08s" begin=".23s" 
             repeatCount="0" fill="freeze" 
    />
    

    seems to produce a decent effect for your case, since the arrowhead is small. However, for a more fine-tuned solution one could use <animateTransform> or <animateMotion>, instead of <animate>, depending on case.

    Here's the spec for SMIL Animations.

    While the effect is easily achievable with CSS animations (in the end, I'm only animating opacity above), I tend to recommend SMIL Animations for <svg>s, as they provide a lot more options for controlling the different aspects of animations, far superior to CSS options, IMHO.

    function makeSVG(tag, attrs) {
      var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
      for (var k in attrs)
        el.setAttribute(k, attrs[k]);
      return el;
    }
    
    $(document).ready(function() {
      var line = makeSVG('line', {
        id: "-",
        class: "key-anim1",
        x1: 20,
        y1: 20,
        x2: 120,
        y2: 120,
        stroke: 'black',
        'stroke-width': 2,
        'marker-end': "url(#arrow)"
      });
      document.getElementById("svg").appendChild(line);
    });
    .key-anim1 {
        -webkit-animation: Drawpath 1s linear forwards;
        animation: Drawpath 1s linear forwards;
        stroke-dasharray: 0, 600;
      }
    
      @-webkit-keyframes Drawpath {
        from {
          stroke-dasharray: 0, 600;
        }
        to {
          stroke-dasharray: 600, 600;
        }
      }
      @keyframes Drawpath {
        from {
          stroke-dasharray: 0, 600;
        }
        to {
          stroke-dasharray: 600, 600;
        }
      }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <svg height="600" width="600" id="svg">
      <defs>
        <marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth" opacity="0">
          <path d="M0,0 L0,6 L9,3 z" fill="#000" />
          <animate attributeType="XML" attributeName="opacity" from="0" to="1" dur=".08s" begin=".23s" repeatCount="0" fill="freeze" />
        </marker>
      </defs>
    </svg>

    Note: the easy way to fine-tune any animation is to decrease the speed 10 times. This way you'll be able to make it perfect, and to increase it back after you're happy with how it performs 10 times slower. Sometimes, when speeding it back up you need to make minor adjustments from how it would be "technically correct" to counterbalance optical illusions (but this is often times far into the land of "invisible details").


    If you want the maker to be visible and move along with the line, you need to drop the dasharray (because now your line has the same length from start to end of animation, but it's drawn with a dashed line and you're simply moving the gaps in that dashed line so it looks like it's growing). Instead you need to make it short initially and animate it towards being longer.

    Here's an example:

    <svg height="600" width="600" id="svg">
      <defs>
        <marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
          <path d="M0,0 L0,6 L9,3 z" fill="#000" opacity="0">
            <animate attributeType="XML" attributeName="opacity" from="0" to="1" dur=".1s" repeatCount="0" fill="freeze" />
          </path>
        </marker>
      </defs>
      <line id="-" x1="20" y1="20" x2="21" y2="21" stroke="black" stroke-width="2" marker-end="url(#arrow)">
          <animate attributeType="XML" attributeName="x2" from="21" to="120" dur="1s" repeatCount="0" fill="freeze" />
          <animate attributeType="XML" attributeName="y2" from="21" to="120" dur="1s" repeatCount="0" fill="freeze" />
      </line>
    </svg>