【问题标题】:How can I animate the drawing of text on a web page?如何为网页上的文本绘制动画?
【发布时间】:2015-07-06 19:33:57
【问题描述】:

我想要一个有一个居中单词的网页。

我希望这个词用动画来绘制,这样页面“写”出这个词的方式和我们一样,即它从一个点开始,随着时间的推移绘制直线和曲线,最终结果是一个字形。

我不关心这是用<canvas> 还是DOM 完成的,我也不关心它是用JavaScript 还是CSS 完成的。没有 jQuery 会很好,但不是必需的。

我该怎么做?我已经详尽地搜索了,但没有运气。

【问题讨论】:

  • 我对如何真正“手写”字符进行了一些思考,并在这里发表了我的想法:stackoverflow.com/questions/12700731/…
  • codrops article 中有一些非常相似的东西(demo in tympanus
  • 过去,我在 Flash 中使用动画精灵蒙版制作这个动画。您需要为蒙版设置动画,这意味着让它逐渐显示文本。动画将由蒙版帧组成。
  • 当然,您将受益于能够将文本分解成曲线。您必须先使用 SVG 和一些 SVG 编辑器(Illustrator 或任何其他可以创建文本 SVG 的工具)来执行此操作。我不知道 SVG 是否支持蒙版,但如果支持,这将变得更容易制作动画。
  • 使用 SVG 并使用 JavaScript 操作 SVG 代码来制作动画。

标签: javascript css canvas html5-canvas css-shapes


【解决方案1】:

编辑 2019


我创建了一个可以创建逼真动画的 javascript 库。它易于使用,并且需要一个特殊的 JSON 文件作为字体。

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>

查看 Github page 以获取文档和示例。还有Codepen


上一个答案

以下示例使用 snap.js 动态创建 tspan 元素,然后为每个 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>

上一个答案


你可以使用 svg 的 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>

没有keyframes 动画你可以做这样的事情

<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>

对于 IE 支持,您可以使用 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>

【讨论】:

  • 哇,这真的很有趣。我采用了您的原始代码 sn-p 并通过删除重复的 CSS 属性、使用基于百分比的偏移量和 dasharray 值以及更改笔画宽度以使其更清楚代码的工作方式来稍微改进它:jsfiddle.net/Ajedi32/gdc4azLn/1随意如果您愿意,可以将这些改进中的任何一个编辑到您的答案中。
  • 这是解决这个问题的一个史诗般的解决方案,SVG 比 canvas (+1) 要好,如果浏览器不支持它,它将显示文本。
  • @JefferyThaGintoki 您也可以使用画布执行此操作,只需将文本放在画布标签之间,如果不支持画布,则文本(或其他答案中显示的动画 gif 在正文中显示) 将出现。如果浏览器不支持画布,它可能也不支持 svg。
  • 我认为 SVG 文本的使用很棒,但我想知道,是否可以在某些时候添加填充?只是字母的轮廓在所有项目中都不好看,干杯!当然还有这个答案的 +1
  • @randomguy04 检查第一个 sn-p,我已经对其进行了编辑以添加填充效果。这是通过添加$(this).css('fill', 'red') 作为动画回调来完成的
【解决方案2】:

我想用动画来绘制这个词,这样页面 以与我们相同的方式“写”出单词

画布版

这将绘制单个字符,更像是手写。它使用长破折号模式,其中每个字符随时间交换开/关顺序。它还有一个速度参数。


示例动画(参见下面的演示)

为了增加真实感和有机感,我添加了随机字母间距、y 增量偏移、透明度、非常微妙的旋转,最后使用了已经“手写”的字体。这些可以封装为动态参数,以提供广泛的“写作风格”。

为了更逼真的外观,默认情况下不需要路径数据。但这是一段简短而高效的代码,它近似于手写行为,并且易于实现。

工作原理

通过定义虚线图案,我们可以创建行军蚂蚁、虚线等。通过为“off”点定义一个非常长的点并逐渐增加“on”点来利用这一点,它会在为点长度设置动画时产生在描边时画线的错觉。

由于离点太长,重复图案将不可见(长度会随所使用字体的大小和特性而变化)。字母的路径会有一个长度,所以我们需要确保每个点至少覆盖这个长度。

对于由多个路径组成的字母(例如 O、R、P 等),因为一个用于轮廓,一个用于空心部分,线条将看起来是同时绘制的。我们不能用这种技术做太多的事情,因为它需要访问每个路径段来单独描边。

兼容性

对于不支持画布元素的浏览器,可以在标签之间放置另一种显示文本的方式,例如样式文本:

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

演示

这会产生实时动画描边(无依赖关系)-

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)}
&lt;canvas width=630&gt;&lt;/canvas&gt;

【讨论】:

  • 只有我一个为此疯狂吗?看起来如此真实,至少比第一个答案要好得多,并且最接近提问者的问题。
  • 我最终使用了另一个答案,因为我立即需要它作为我第二天使用的快速n-dirty hack,然后再也没有碰过,但我接受了这个,因为它更接近我想要的东西。
  • 我们怎样才能让它多行,长文本块?
  • @K3N 是的,这实际上是一个很好的接触:p 伟大的工作。
  • @AliAl-arnous 您可以将 x 设置为另一端,减去字符宽度而不是添加,使用负负空间进行转换并将 clearRect 更改为在字符的另一侧清除。
【解决方案3】:

只有 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>

【讨论】:

    【解决方案4】:

    经过多次测试,这里有一些注意事项。目标是在需要用户交互的 DOM 繁重页面上以最少阻塞的方式显示快速文本数据。

    当然有很多方法可以实现相同的目标。在这个例子中,差异可能并不明显,它确实适用于复杂的接口。

    最慢​​innerHTML 和内联样式。每次迭代都会重新计算 DOM。浏览器正在努力保持火车。它会很快失败,导致内存泄漏和冻结:

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

    更好:使用textContentrequestAnimationFrame 和网络动画API。这更顺畅,在 DOM 重页上很明显。用户交互不会阻止重绘。一些重绘可能会被跳过,以保持界面响应良好。

    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}
    &lt;h1 id="out"&gt;&lt;/h1&gt;

    在上面的例子中,DOM 仍在为文本溢出重新计算。我们可以看到调试器在剧烈闪烁。这对级联元素非常重要!这仍然会减慢 javascript 和用户滚动的速度。

    全能:可以单独使用 css 刷新数据,使用 css content 规则和 css 变量。文本将无法选择。

    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)
     }
    &lt;h1 id="out"&gt;&lt;/h1&gt;

    我的测试显示了很大的改进,javascript 引擎在其他任务上快速跳过。有时它的启动速度可能比上面的例子慢一些。但除此之外,这并没有阻止用户滚动,而且调试器也喜欢,不再跳跃。

    【讨论】:

      猜你喜欢
      • 2015-12-14
      • 1970-01-01
      • 2016-10-13
      • 2010-12-20
      • 2020-10-22
      • 2014-01-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多