如何为网页上的文本绘图设置动画?
How can I animate the drawing of text on a web page?
我想要一个以一个单词为中心的网页。
我希望用动画绘制这个词,这样页面 "writes" 这个词就可以像我们想要的那样显示出来,即它从一个点开始,随着时间的推移绘制直线和曲线,这样最终结果是一个字形。
我不关心这是用 <canvas>
还是 DOM 完成的,我也不关心它是用 JavaScript 还是 CSS 完成的。没有 jQuery 会很好,但不是必需的。
我该怎么做?我已经 穷尽地搜索了 但没有成功。
编辑 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 for documentation and examples. And 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>
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版本
这将绘制单个字符,更像是手写。它使用长破折号模式,其中每个字符的 on/off 顺序随时间交换。它还有一个速度参数。
示例动画(见下面的演示)
为了增加真实感和有机感,我添加了随机字母间距、y 增量偏移、透明度、非常微妙的旋转,最后使用了已经 "handwritten" 的字体。这些可以包装为动态参数以提供范围广泛的 "writing styles".
要获得更真实的外观,需要路径数据,但默认情况下不需要。但这是一段简短而高效的代码,类似于手写行为,并且易于实现。
工作原理
通过定义破折号图案,我们可以创建行军蚂蚁、虚线等。通过为 "off" 点定义一个非常长的点并逐渐增加 "on" 点来利用这一点,它会在动画点长度时给人一种在描边时画线的错觉。
由于关闭点太长,重复的图案将不可见(长度会随着所用字体的大小和特性而变化)。字母的路径会有一个长度,所以我们需要确保每个点至少覆盖这个长度。
对于由多个路径组成的字母(f.ex.O、R、P等),一个用于轮廓,一个用于空心部分,线条将呈现为绘制同时。我们不能用这种技术做太多事情,因为它需要访问每个路径段来单独描边。
兼容性
对于不支持 canvas 元素的浏览器,可以在标签之间放置另一种显示文本的方式,例如样式文本:
<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)}
<canvas width=630></canvas>
只有 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>
经过多次测试,这里有一些注意事项。目标是在需要用户交互的 DOM 繁重页面 上以最少的阻塞方式显示快速文本数据。
当然有很多方法可以实现同一件事。在这个例子中,差异可能并不明显,它确实适用于复杂的接口。
最慢:innerHTML
和内联样式。 DOM 在每次迭代时重新计算。浏览器正在努力保持火车。它会很快失败,导致内存泄漏和冻结:
setInterval(function(){
out.innerHTML = `<span style="position:fixed;top:${~~(Math.random() * 220)}px">${Math.random() * 1000}<span>`
},1)
<h1 id="out"></h1>
更好:使用textContent
、requestAnimationFrame
和网络动画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>
在上面的例子中,DOM 仍在为文本溢出重新计算。我们可以看到调试器在用力闪烁。这对级联元素真的很重要!这仍然会减慢 javascript 和用户滚动。
全能:可以单独使用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>
我的测试显示出很大的改进,javascript 引擎正在快速跳过其他任务。有时它的启动速度可能比上面的例子慢一点。但除此之外,这不会阻止用户滚动,调试器也很喜欢,不再跳转。
我想要一个以一个单词为中心的网页。
我希望用动画绘制这个词,这样页面 "writes" 这个词就可以像我们想要的那样显示出来,即它从一个点开始,随着时间的推移绘制直线和曲线,这样最终结果是一个字形。
我不关心这是用 <canvas>
还是 DOM 完成的,我也不关心它是用 JavaScript 还是 CSS 完成的。没有 jQuery 会很好,但不是必需的。
我该怎么做?我已经 穷尽地搜索了 但没有成功。
编辑 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 for documentation and examples. And 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>
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版本
这将绘制单个字符,更像是手写。它使用长破折号模式,其中每个字符的 on/off 顺序随时间交换。它还有一个速度参数。
示例动画(见下面的演示)
为了增加真实感和有机感,我添加了随机字母间距、y 增量偏移、透明度、非常微妙的旋转,最后使用了已经 "handwritten" 的字体。这些可以包装为动态参数以提供范围广泛的 "writing styles".
要获得更真实的外观,需要路径数据,但默认情况下不需要。但这是一段简短而高效的代码,类似于手写行为,并且易于实现。
工作原理
通过定义破折号图案,我们可以创建行军蚂蚁、虚线等。通过为 "off" 点定义一个非常长的点并逐渐增加 "on" 点来利用这一点,它会在动画点长度时给人一种在描边时画线的错觉。
由于关闭点太长,重复的图案将不可见(长度会随着所用字体的大小和特性而变化)。字母的路径会有一个长度,所以我们需要确保每个点至少覆盖这个长度。
对于由多个路径组成的字母(f.ex.O、R、P等),一个用于轮廓,一个用于空心部分,线条将呈现为绘制同时。我们不能用这种技术做太多事情,因为它需要访问每个路径段来单独描边。
兼容性
对于不支持 canvas 元素的浏览器,可以在标签之间放置另一种显示文本的方式,例如样式文本:
<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)}
<canvas width=630></canvas>
只有 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>
经过多次测试,这里有一些注意事项。目标是在需要用户交互的 DOM 繁重页面 上以最少的阻塞方式显示快速文本数据。
当然有很多方法可以实现同一件事。在这个例子中,差异可能并不明显,它确实适用于复杂的接口。
最慢:innerHTML
和内联样式。 DOM 在每次迭代时重新计算。浏览器正在努力保持火车。它会很快失败,导致内存泄漏和冻结:
setInterval(function(){
out.innerHTML = `<span style="position:fixed;top:${~~(Math.random() * 220)}px">${Math.random() * 1000}<span>`
},1)
<h1 id="out"></h1>
更好:使用textContent
、requestAnimationFrame
和网络动画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>
在上面的例子中,DOM 仍在为文本溢出重新计算。我们可以看到调试器在用力闪烁。这对级联元素真的很重要!这仍然会减慢 javascript 和用户滚动。
全能:可以单独使用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>
我的测试显示出很大的改进,javascript 引擎正在快速跳过其他任务。有时它的启动速度可能比上面的例子慢一点。但除此之外,这不会阻止用户滚动,调试器也很喜欢,不再跳转。