I am just starting to learn to do SVG Animations, and I'm trying to make a command-line style 'typewriter' effect, typing out several different lines of text, one after the other.
I would like the finished animation to do the following:
- Show a prompt with a blinking Full Block cursor, blinking 3 times. (fig. 1)
- Type out a line of text, letter by letter, with the cursor following at the end (not blinking) like an actual CLI. (fig. 2 but with cursor following)
- Show the full line with the cursor at the end, blinking 3 times immediately after the full line is typed out.
- 'Delete' the line, letter by letter. (fig. 3 but cursor following)
- Repeat with a different line of text, and so forth.
Fig. 4 is what I have so far, but as you can see, there are a couple of problems:
- No following cursor.
- There's a pause after the line is fully typed and before the cursor starts blinking. Also after having blinked, before starting the 'delete' part.
- It seems that there's a slight apparent movement of the text, when hiding the typed out text and simultaneously showing the text used for the 'delete' part.
- I haven't figured out how to conveniently make the whole animation loop with different lines of text. I have tried to just copy-paste it and change
id
attributes, but the XML is getting very large and confusing, so I haven't had any luck.
Fig. 1:
<svg
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
viewBox='0 0 1152 50'
style='background-color: #000000;'
width='1152px' height='50px'>
<!-- Show prompt -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
<!-- 1 non-breaking space followed by '❯' -->
 ❯
</text>
<!-- Blink cursor -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
<!-- 3 non-breaking spaces followed by '█' -->
   █
<animate id="blinker" attributeName="opacity" dur="1s" values="1;0" repeatCount="3" begin="0s" calcMode="discrete"/>
<set attributeName="opacity" to="0" begin="blinker.end"></set>
</text>
</svg>
Demo: https://jsfiddle.net/ja0x5rs8/
Fig. 2:
<svg
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
viewBox='0 0 1152 50'
style='background-color: #000000;'
width='1152px' height='50px'>
<style>
@font-face {
font-family: 'Inconsolata';
font-style: normal;
font-weight: 400;
font-stretch: normal;
font-display: fallback;
}
</style>
<!-- Show prompt -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
<!-- 1 non-breaking space followed by '❯' -->
 ❯
</text>
<path id='typer_path'>
<animate id='d1' attributeName='d' begin='0s'
dur='3500ms' fill='remove'
values='m0,25 h0; m0,25 h1152'
keyTimes='0; 1' />
</path>
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle'
x='0%' text-anchor='start'>
<animate id="show1" attributeName="opacity" values="100" begin="0s" dur="500" calcMode="discrete"></animate>
<textPath xlink:href='#typer_path'>
   hashcat -m 0 511c04a3d19150f1903ef5b92d748a65 -a 3 -1 aeiou -2 bcdfghjklmnpqrstvwxz '?1?2?1?2?s?d?d?d?d' -w 4 -O█
</textPath>
</text>
</svg>
Demo: https://jsfiddle.net/u3ypnwrm/
Fig. 3:
<svg
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
viewBox='0 0 1152 50'
style='background-color: #000000;'
width='1152px' height='50px'>
<style>
@font-face {
font-family: 'Inconsolata';
font-style: normal;
font-weight: 400;
font-stretch: normal;
font-display: fallback;
}
</style>
<!-- Show prompt -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
<!-- 1 non-breaking space followed by '❯' -->
 ❯
</text>
<path id='typer_path'>
<animate id='d1' attributeName='d' begin='0s'
dur='1500ms' fill='remove'
values='m0,25 h1152; m0,25 h0'
keyTimes='0; 1' />
</path>
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle'
x='0%' text-anchor='start'>
<animate id="show1" attributeName="opacity" values="100" begin="0s" dur="500" calcMode="discrete"></animate>
<textPath xlink:href='#typer_path'>
 ❯ hashcat -m 0 511c04a3d19150f1903ef5b92d748a65 -a 3 -1 aeiou -2 bcdfghjklmnpqrstvwxz '?1?2?1?2?s?d?d?d?d' -w 4 -O█
</textPath>
</text>
</svg>
Demo: https://jsfiddle.net/L0tdsu1w/
Fig. 4:
<svg xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
viewBox='0 0 1152 50'
style='background-color: #000000;'
width='1152px' height='50px'>
<!-- PROMPT START -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
<!-- 1 non-breaking space followed by '❯' -->
 ❯
</text>
<!-- PROMPT END -->
<!-- CURSOR BLINK START -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
<!-- 3 non-breaking spaces followed by '█' -->
   █
<animate id="blinker" attributeName="opacity" dur="1s" values="1;0" repeatCount="3" begin="0s" calcMode="discrete"/>
<set attributeName="opacity" to="0" begin="blinker.end"></set>
</text>
<!-- CURSOR BLINK END -->
<!-- TYPER START -->
<path id='path0'>
<animate id='d1' attributeName='d' begin='blinker.end'
dur='3500ms' fill='remove'
values='m0,25 h0 ; m0,25 h1152' keyTimes='0; 1' />
</path>
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle'
x='0%' text-anchor='start'>
<animate id="show" attributeName="opacity" values="100" begin="blinker.end" dur="500" calcMode="discrete"></animate>
<textPath xlink:href='#path0'>
   hashcat -m 0 511c04a3d19150f1903ef5b92d748a65 -a 3 -1 aeiou -2 bcdfghjklmnpqrstvwxz '?1?2?1?2?s?d?d?d?d' -w 4 -O█
</textPath>
</text>
<!-- TYPER END -->
<!-- POST PROMPT START -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14' opacity="0"
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
   hashcat -m 0 511c04a3d19150f1903ef5b92d748a65 -a 3 -1 aeiou -2 bcdfghjklmnpqrstvwxz '?1?2?1?2?s?d?d?d?d' -w 4 -O
<animate id="post_hider" attributeName="opacity" dur="1s" values="0;1" repeatCount="3" begin="d1.end" calcMode="discrete"/>
</text>
<!-- POST PROMPT END -->
<!-- POST CURSOR BLINK START -->
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14' opacity="0"
dominant-baseline='middle' x='0%' y='21' text-anchor='start'>
   hashcat -m 0 511c04a3d19150f1903ef5b92d748a65 -a 3 -1 aeiou -2 bcdfghjklmnpqrstvwxz '?1?2?1?2?s?d?d?d?d' -w 4 -O█
<animate id="post_blinker" attributeName="opacity" dur="1s" values="1;0" repeatCount="3" begin="d1.end" calcMode="discrete"/>
<set attributeName="opacity" to="0" begin="post_blinker.end"></set>
</text>
<!-- POST CURSOR BLINK END -->
<!-- DELETER START -->
<path id='path1'>
<animate id='d1_1' attributeName='d' begin='post_blinker.end'
dur='3500ms' fill='remove'
values='m0,25 h1152; m0,25 h0' keyTimes='0; 1' />
</path>
<text font-family='"Inconsolata", monospace' fill='#126D08' font-size='14'
dominant-baseline='middle'
x='0%' text-anchor='start'>
<animate id="show_1" attributeName="opacity" values="100" begin="post_blinker.end" dur="500" calcMode="discrete"></animate>
<textPath xlink:href='#path1'>
   hashcat -m 0 511c04a3d19150f1903ef5b92d748a65 -a 3 -1 aeiou -2 bcdfghjklmnpqrstvwxz '?1?2?1?2?s?d?d?d?d' -w 4 -O█
</textPath>
</text>
<!-- DELETER END -->
</svg>