Javascript 如何为网页上的文本绘制设置动画?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29911143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:18:22  来源:igfitidea点击:

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

javascriptcsscanvashtml5-canvascss-shapes

提问by strugee

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 we would, i.e. it starts at one point and draws lines and curves over time such that the end result is a glyph.

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

I do not care if this is done with <canvas>or the DOM, and I don't care whether it's done with JavaScript or CSS. The absence of jQuery would be nice, but not required.

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

How can I do this? I've searched exhaustivelywith no luck.

我怎样才能做到这一点?我没有运气就进行了详尽的搜索。

回答by

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

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

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.

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

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 元素的浏览器,可以在标签之间放置另一种显示文本的方式,例如样式文本:

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

回答by Akshay

Edit 2019

编辑 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.

我创建了一个可以创建逼真动画的 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>

Checkout the Github pagefor documentation and examples. And Codepen

查看Github 页面以获取文档和示例。和代码笔



Previous Answer

上一个答案

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

下面的示例使用 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>

Previous Answer

上一个答案



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

你可以使用 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 keyframesanimation you can do something like this

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

And for IE support you can use jquery/javascript

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

回答by zloctb

Only CSS :

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

回答by NVRM

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

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

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

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

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, requestAnimationFrameand 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.

更好的方法:使用textContent,requestAnimationFrame和 web 动画 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}
<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.

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

enter image description here

在此处输入图片说明

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

全能:可以单独使用 css 来刷新带有 csscontent规则和 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)
 }
<h1 id="out"></h1>

enter image description here

在此处输入图片说明

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.

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