Jak animować rysowanie tekstu na stronie internetowej?

229

Chcę mieć stronę internetową, która ma jedno wyśrodkowane słowo.

Chcę, aby to słowo zostało narysowane za pomocą animacji, tak aby strona „zapisała” to słowo w taki sam sposób, jak my, tzn. Zaczyna się w jednym punkcie i rysuje linie i krzywe w czasie, tak aby końcowy wynik był glifem.

Nie dbam o to, czy jest to zrobione za <canvas>pomocą DOM, czy też o to, czy robi się to za pomocą JavaScript czy CSS. Brak jQuery byłby miły, ale nie wymagany.

W jaki sposób mogę to zrobić? Szukałem wyczerpująco bez powodzenia.

strugee
źródło
2
Zastanawiałem się, jak właściwie
napisać
Jest coś bardzo podobnego w artykule o kodopach (z demonstracją w tympanonie )
Francisco Presencia
1
Kiedyś robiłem tę animację we Flashu, używając animowanych masek sprite. Potrzebujesz animacji maski, co oznacza, że ​​stopniowo odsłania tekst. Animacja byłaby wykonana z ramek maski.
Tiberiu-Ionuț Stan
Oczywiście korzyścią byłoby dzielenie tekstu na krzywe. Trzeba to zrobić, używając ręcznie SVG i edytora SVG (Illustrator lub cokolwiek innego, co może utworzyć SVG tekstu). Nie wiem, czy SVG obsługują maski, ale jeśli tak, to znacznie łatwiej byłoby je animować.
Tiberiu-Ionuț Stan
Użyj SVG i manipuluj kodem SVG za pomocą JavaScript, aby utworzyć animację.
The Demz

Odpowiedzi:

264

Chcę, aby to słowo zostało narysowane za pomocą animacji, tak aby strona „zapisała” to słowo w taki sam sposób, jak my

Wersja na płótnie

Spowoduje to narysowanie pojedynczych znaków bardziej niż pisanie ręcznie. Wykorzystuje długi wzorzec myślowy, w którym kolejność włączania / wyłączania jest zmieniana w czasie na znak. Ma również parametr prędkości.

Migawka
Przykładowa animacja (patrz demo poniżej)

Aby zwiększyć realizm i wrażenia organiczne, dodałem losowe odstępy między literami, przesunięcie delta y, przezroczystość, bardzo subtelny obrót i na koniec używając już „ręcznie napisanej” czcionki. Można je zawrzeć jako parametry dynamiczne, aby zapewnić szeroki zakres „stylów pisania”.

Aby uzyskać jeszcze bardziej realistyczny wygląd, wymagane byłyby dane ścieżki, które nie są domyślnie. Jest to jednak krótki i wydajny fragment kodu, który aproksymuje zachowanie napisane odręcznie i jest łatwy do wdrożenia.

Jak to działa

Definiując wzór kreski, możemy tworzyć marszowe mrówki, linie kropkowane i tak dalej. Wykorzystując to, definiując bardzo długą kropkę dla „wyłączonej” kropki i stopniowo zwiększając „włączoną” kropkę, da to złudzenie rysowania linii podczas głaskania podczas animacji długości kropki.

Ponieważ kropka wyłączona jest tak długa, powtarzający się wzór nie będzie widoczny (długość będzie się różnić w zależności od rozmiaru i właściwości używanego kroju pisma). Ścieżka litery będzie miała długość, więc musimy upewnić się, że każda kropka pokrywa przynajmniej tę długość.

W przypadku liter, które składają się z więcej niż jednej ścieżki (np. O, R, P itd.), Tak jak jedna dla konturu, jedna dla pustej części, linie będą wyglądały na narysowane jednocześnie. Dzięki tej technice nie możemy wiele na to poradzić, ponieważ wymagałoby to dostępu do każdego segmentu ścieżki, aby był głaskany osobno.

Zgodność

W przypadku przeglądarek, które nie obsługują elementu canvas, można umieścić alternatywny sposób wyświetlania tekstu między tagami, na przykład tekst w stylu:

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

Próbny

Powoduje to animowane uderzenie na żywo ( bez zależności ) -

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>


źródło
21
Czy tylko ja wariuje na to? Wygląda tak realnie, przynajmniej znacznie lepiej niż pierwsza odpowiedź i najbliżej pytania pytającego.
KhoPhi
5
Skończyłem z inną odpowiedzią, ponieważ potrzebowałem jej od razu jako szybki i brudny hack, którego użyłem następnego dnia, a potem nigdy więcej jej nie dotknąłem, ale akceptuję tę, ponieważ jest ona znacznie bliższa temu, co szukałem dla.
strugee
Jak możemy zrobić to dla wielu linii i długiego bloku tekstu?
Saad Farooq
1
@ K3N Tak, to był naprawdę miły akcent: p Świetna robota.
keyser
1
@ AliAl-arnous możesz ustawić x na przeciwny koniec, odjąć szerokość znaku zamiast dodawać, przekształcić z zanegowaną ujemną spacją i zmienić clearRect na clear po drugiej stronie znaku.
216

Edytuj 2019


Stworzyłem bibliotekę javascript, która może tworzyć realistyczne animacje. Jest łatwy w użyciu i wymaga specjalnego pliku JSON, który działa jak czcionka.

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 strona GitHub dla dokumentacji i przykładów. I Codepen


Poprzednia odpowiedź

Poniższy przykład wykorzystuje plik snap.js do dynamicznego tworzenia tspanelementów, a następnie animowania każdego z nich stroke-dashoffset.

Poprzednia odpowiedź


Możesz zrobić coś takiego za pomocą svg's stroke-dasharray

Bez keyframesanimacji możesz zrobić coś takiego

A do obsługi IE możesz użyć jquery / javascript

Akshay
źródło
4
Wow, to naprawdę interesujące. Wziąłem twój oryginalny fragment kodu i nieco go poprawiłem, usuwając zduplikowane właściwości CSS, używając przesunięć procentowych i wartości dasharray, a także zmieniając szerokość obrysu, aby bardziej pokazać, jak działa kod: jsfiddle.net/Ajedi32/gdc4azLn / 1 Jeśli chcesz, edytuj dowolne z tych ulepszeń w swojej odpowiedzi.
Ajedi32
2
jest to jedno epickie rozwiązanie tego pytania, SVG jest lepszy niż canvas (+1), o ile w przypadku, gdy przeglądarka go nie obsługuje, zamiast tego wyświetli tekst.
Jeffery ThaGintoki
3
@JefferyThaGintoki możesz to zrobić również z płótnem, po prostu umieść tekst między znacznikami płótna, jeśli płótno nie jest obsługiwane, zamiast niego pojawi się tekst (lub animowany gif, jak w innych odpowiedziach wyświetlanych w tekście). Jeśli przeglądarka nie obsługuje kanwy, prawdopodobnie również nie będzie obsługiwać svg.
torox
1
Myślę, że użycie tekstu SVG jest świetne, jednak zastanawiam się, czy w pewnym momencie można dodać wypełnienie? sam zarys liter nie wygląda ładnie we wszystkich projektach, na zdrowie! i oczywiście +1 do tej odpowiedzi
randomguy04
1
@ randomguy04 Sprawdź pierwszy fragment, zmodyfikowałem go, aby dodać efekt wypełnienia. Odbywa się to poprzez dodanie $(this).css('fill', 'red')jako oddzwaniania do animacji
Akshay
2

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

zloctb
źródło
0

Po wielu testach oto kilka uwag. Celem jest wyświetlanie szybkich danych tekstowych w najmniej blokujący sposób, na ciężkich stronach DOM wymagających interakcji użytkowników.

Istnieje oczywiście wiele sposobów na osiągnięcie tego samego. W tym przykładzie różnice mogą nie być oczywiste, tak naprawdę dotyczą złożonych interfejsów.

Najwolniejszy : innerHTMLi wbudowany styl. DOM jest ponownie obliczany przy każdej iteracji. Przeglądarka ciężko pracuje, aby utrzymać pociąg. Szybko zawiedzie, powodując wycieki pamięci i zawieszanie się:

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

Lepiej : Użycie textContent, requestAnimationFramea API animacji internetowych. To idzie znacznie płynniej, jest oczywiste na ciężkich stronach DOM. Interakcje użytkownika nie blokują odświeżania. Niektóre odświeżenia mogą zostać pominięte, aby interfejs dobrze reagował.

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>

W powyższym przykładzie DOM jest wciąż ponownie obliczany pod kątem przepełnienia tekstu. Widzimy, że debugger mocno mruga. To naprawdę ma znaczenie dla elementów kaskadowych! Może to nadal spowalniać javascript i przewijanie użytkownika.

wprowadź opis zdjęcia tutaj

Pełna moc : możliwe jest użycie samego css do odświeżania danych za pomocą contentreguły css i zmiennych css. Tekst nie będzie wtedy możliwy do wybrania.

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>

wprowadź opis zdjęcia tutaj

Moje testy wykazały wielkie ulepszenia, silnik javascript szybko pomija inne zadania. Czasami może zacząć się nieco wolniej niż w powyższym przykładzie. Ale oprócz tego nie blokuje to zwojów użytkowników, a debugger też lubi, nie ma już przeskoków.

NVRM
źródło