How can I animate the drawing of text on a web page?

前端 未结 4 1403
情深已故
情深已故 2020-12-02 03:08

I want to have a web page which has one centered word.

I want this word to be drawn with an animation, such that the page \"writes\" the word out the same way that

相关标签:
4条回答
  • 2020-12-02 03:46

    I want this word to be drawn with an animation, such that the page "writes" the word out the same way that we would

    Canvas version

    This will draw single chars more like one would write by hand. It uses a long dash-pattern where the on/off order is swapped over time per char. It also has a speed parameter.

    Snapshot
    Example animation (see demo below)

    To increase realism and the organic feel, I added random letter-spacing, an y delta offset, transparency, a very subtle rotation and finally using an already "handwritten" font. These can be wrapped up as dynamic parameters to provide a broad range of "writing styles".

    For a even more realistic look the path data would be required which it isn't by default. But this is a short and efficient piece of code which approximates hand-written behavior, and easy to implement.

    How it works

    By defining a dash pattern we can create marching ants, dotted lines and so forth. Taking advantage of this by defining a very long dot for the "off" dot and gradually increase the "on" dot, it will give the illusion of drawing the line on when stroked while animating the dot length.

    Since the off dot is so long the repeating pattern won't be visible (the length will vary with the size and characteristics of the typeface being used). The path of the letter will have a length so we need to make sure we are having each dot at least covering this length.

    For letters that consists of more than one path (f.ex. O, R, P etc.) as one is for the outline, one is for the hollow part, the lines will appear to be drawn simultaneously. We can't do much about that with this technique as it would require access to each path segment to be stroked separately.

    Compatibility

    For browsers that don't support the canvas element an alternative way to show the text can be placed between the tags, for example a styled text:

    <canvas ...>
        <div class="txtStyle">STROKE-ON CANVAS</div>
    </canvas>
    

    Demo

    This produces the live animated stroke-on (no dependencies) -

    var ctx = document.querySelector("canvas").getContext("2d"),
        dashLen = 220, dashOffset = dashLen, speed = 5,
        txt = "STROKE-ON CANVAS", x = 30, i = 0;
    
    ctx.font = "50px Comic Sans MS, cursive, TSCu_Comic, sans-serif"; 
    ctx.lineWidth = 5; ctx.lineJoin = "round"; ctx.globalAlpha = 2/3;
    ctx.strokeStyle = ctx.fillStyle = "#1f2f90";
    
    (function loop() {
      ctx.clearRect(x, 0, 60, 150);
      ctx.setLineDash([dashLen - dashOffset, dashOffset - speed]); // create a long dash mask
      dashOffset -= speed;                                         // reduce dash length
      ctx.strokeText(txt[i], x, 90);                               // stroke letter
    
      if (dashOffset > 0) requestAnimationFrame(loop);             // animate
      else {
        ctx.fillText(txt[i], x, 90);                               // fill final letter
        dashOffset = dashLen;                                      // prep next char
        x += ctx.measureText(txt[i++]).width + ctx.lineWidth * Math.random();
        ctx.setTransform(1, 0, 0, 1, 0, 3 * Math.random());        // random y-delta
        ctx.rotate(Math.random() * 0.005);                         // random rotation
        if (i < txt.length) requestAnimationFrame(loop);
      }
    })();
    canvas {background:url(http://i.imgur.com/5RIXWIE.png)}
    <canvas width=630></canvas>

    0 讨论(0)
  • 2020-12-02 03:52

    Following many tests, here is some notes. The goal is to display fast text data in the least blocking way, on DOM heavy pages requiring users interactions.

    There is of course many ways to achieve the same thing. On this example, the differences might not be obvious, it really apply to complex interfaces.

    Slowest: innerHTML and inline styling. The DOM is recalculated at each iterations. The browser is working hard to keep the train. It will fails quickly, causing memory leaks and freezes:

    setInterval(function(){
      out.innerHTML = `<span style="position:fixed;top:${~~(Math.random() * 220)}px">${Math.random() * 1000}<span>`
    },1)
    <h1 id="out"></h1>

    Way better: Using textContent, requestAnimationFrame and the web animation api. This goes way smoother, it's obvious on DOM heavy pages. The user interactions won't blocks the repaints. Some repaints might be skipped, to keep the interface well responsive.

    let job
    const paint = () => {
      job = requestAnimationFrame(paint)
      out.textContent = Math.random() * 1000
      out.animate([{top: ~~(Math.random() * 220)+"px"},{top:0}],{duration: 1,iterations: 1})
    }
    
    /* Start looping -----------------------------------------*/
    requestAnimationFrame(paint)
    #out{
    position: fixed}
    <h1 id="out"></h1>

    On the above example, the DOM is still being recalculated for the text overflow.. We can see the debugger blinking hard. This really matter on cascading elements! This can still slows down javascript and user scrollings.

    Full power: It's possible to use css alone to refresh the data with the css content rule and css variables. The text won't then be selectable.

    let job
    const paint = () => {
      job = requestAnimationFrame(paint)
      out.setAttribute('data-before', Math.random() * 1000)
      out.animate([{top: ~~(Math.random() * 220)+"px"},{top:0}],{duration: 1,iterations: 1})
    }
    
    /* Start looping -----------------------------------------*/
    requestAnimationFrame(paint)
    #out{
      position: fixed
      
      }
    #out:before {
       content: attr(data-before)
     }
    <h1 id="out"></h1>

    My tests showns great improvements, the javascript engine is skipping quickly on other tasks. Sometimes it can starts a bit slower than the above example. But beside that,this doesn't blocks users scrolls, and the debugger is also liking, no more jumpings.

    0 讨论(0)
  • 2020-12-02 03:57

    Edit 2019


    I created a javascript library that can create realistic animations. It's easy to use and requires a special JSON file that acts as font.

    var vara = new Vara("#container", "https://rawcdn.githack.com/akzhy/Vara/ed6ab92fdf196596266ae76867c415fa659eb348/fonts/Satisfy/SatisfySL.json", [{
      text: "Hello World!!",
      fontSize: 48,
      y:10
    }, {
      text: "Realistic Animations",
      fontSize: 34,
      color:"#f44336"
    }], {
      strokeWidth: 2,
      textAlign:"center"
    });
    #container {
      padding: 30px;
    }
    <script src="https://rawcdn.githack.com/akzhy/Vara/16e30acca2872212e28735cfdbaba696a355c780/src/vara.min.js"></script>
    <div id="container"></div>

    Checkout the Github page for documentation and examples. And Codepen


    Previous Answer

    The below example uses snap.js to dynamically create tspan elements and then animate each of their stroke-dashoffset.

    var s = Snap('svg');
    var text = 'Some Long Text'
    var len = text.length;
    var array = [];
    for (var x = 0; x < len; x++) {
      var t = text[x]
      array.push(t);
    }
    var txt = s.text(50, 50, array)
    $('tspan').css({
      'font-size': 50,
      fill: 'none',
      stroke: 'red',
      "stroke-width":2,
      'stroke-dasharray': 300,
      'stroke-dashoffset': 300
    })
    
    $('tspan').each(function(index) {
      $(this).stop(true, true).delay(300 * index).animate({
        'stroke-dashoffset': 0,
      }, 300, function() {
        $(this).css('fill', 'red')
      })
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <svg width="500" height="500">
    </svg>

    Previous Answer


    You can do something like this using svg's stroke-dasharray

    text {
      stroke-dasharray: 1000;
      stroke-dashoffset: 1000;
      -webkit-animation: draw 8s forwards;
    }
    @-webkit-keyframes draw {
      100% {
        stroke-dashoffset: 0;
      }
    }
    text {
      stroke-dasharray: 1000;
      stroke-dashoffset: 1000;
      -webkit-animation: draw 8s forwards;
      -moz-animation: draw 8s forwards;
      -o-animation: draw 8s forwards;
      -ms-animation: draw 8s forwards;
      animation: draw 8s forwards;
    }
    @-webkit-keyframes draw {
      100% {
        stroke-dashoffset: 0;
      }
    }
    @-moz-keyframes draw {
      100% {
        stroke-dashoffset: 0;
      }
    }
    @-o-keyframes draw {
      100% {
        stroke-dashoffset: 0;
      }
    }
    @-ms-keyframes draw {
      100% {
        stroke-dashoffset: 0;
      }
    }
    @keyframes draw {
      100% {
        stroke-dashoffset: 0;
      }
    }
    <svg width="500" height="500">
      <text x="100" y="80" fill="none" stroke="black" stroke-width="1" font-size="50">Some text</text>
    </svg>

    Without keyframes animation you can do something like this

    <svg width="500" height="500">
      <text x="100" y="80" fill="none" stroke="black" stroke-width="5" font-size="50"  stroke-dasharray="1000"
      stroke-dashoffset="1000">Some text
      <animate attributeName="stroke-dashoffset"
        from="1000"
        to="0" 
        dur="8s"
          fill="freeze">
              
          </animate> </text>
    </svg>

    And for IE support you can use jquery/javascript

    $('text').animate({
        'stroke-dashoffset':'0'
    },8000)
    text {
      stroke-dasharray: 1000;
      stroke-dashoffset: 1000;
      }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <svg width="500" height="500">
      <text x="100" y="80" fill="none" stroke="black" stroke-width="1" font-size="50" 
     >Some text
      </text>
    </svg>

    0 讨论(0)
  • 2020-12-02 04:01

    Only CSS :

    @keyframes fadein_left {
      from {
        left: 0;
      }
      to {
        left: 100%;
      }
    }
    
    #start:before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0%;
      opacity: 0.7;
      height: 25px;
      background: #fff;
      animation: fadein_left 3s;
    }
    <div id="start">
      some text some text some text some text some text
    </div>

    0 讨论(0)
提交回复
热议问题