├── .gitignore ├── typewrite.gif ├── package.json ├── LICENSE ├── index.html ├── dist └── typewrite.min.js ├── README.md └── src └── typewrite.js /.gitignore: -------------------------------------------------------------------------------- 1 | typeout.gif 2 | /node_modules -------------------------------------------------------------------------------- /typewrite.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrvautin/typewrite/HEAD/typewrite.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "type-write", 3 | "version": "1.0.2", 4 | "description": "A javascript typewriter library which animates the typing, deleting, and selecting of text on a page", 5 | "main": "src/typewrite.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "uglify": "uglifyjs src/typewrite.js -o dist/typewrite.min.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/mrvautin/typewrite.git" 13 | }, 14 | "keywords": [ 15 | "typewrite", 16 | "typewriter", 17 | "jquery", 18 | "animate", 19 | "typing", 20 | "type", 21 | "text", 22 | "delete text", 23 | "animate typing", 24 | "select text" 25 | ], 26 | "author": "Mark Moffat (https://markmoffat.com)", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/mrvautin/typewrite/issues" 30 | }, 31 | "homepage": "https://github.com/mrvautin/typewrite#readme", 32 | "dependencies": { 33 | "uglify-js": "^2.7.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Mark Moffat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | typewrite example 5 | 6 | 7 | 8 | 12 | 33 | 34 | 35 |
36 | 37 | -------------------------------------------------------------------------------- /dist/typewrite.min.js: -------------------------------------------------------------------------------- 1 | (function($){$.fn.typewrite=function(options){var settings=$.extend({speed:12,blinkSpeed:2,showCursor:true,blinkingCursor:true,cursor:"|",selectedBackground:"#F1F1F1",selectedText:"#333333",continuous:false},options);settings.blinkSpeed=1e3/settings.blinkSpeed;if(settings.showCursor){$(this).html(''+settings.cursor+"");if(settings.blinkingCursor){var $cursor=$(".blinkingCursor");setInterval(function(){if(settings.blinkingCursor==="opacity"){if($cursor.css("opacity")==="1"){$cursor.css({opacity:0})}else{$cursor.css({opacity:1})}}else{$cursor.toggle()}},settings.blinkSpeed)}}else{$(this).html("")}settings.mainEl=this;settings.el=$(this).children("span")[0];settings.speed=1e3/settings.speed;var actions=options.actions;settings.queue=actions.length;$(settings.mainEl).trigger("typewriteStarted");processActions();function processActions(){actions.forEach(function(element,index){if(Object.keys(element).includes("speed")){settings.speed=1e3/element.speed}if(!Object.keys(element).includes("speed")){removeSelection()}if(Object.keys(element).includes("delay")){delay(element.delay)}if(Object.keys(element).includes("remove")){remove(element.remove)}if(Object.keys(element).includes("select")){select(element.select)}if(Object.keys(element).includes("type")){if(element.type==="
"){newLine()}else{var text=$("
").html(element.type).text();typeText(text,settings)}}})}var done=setInterval(function(){if(settings.queue===0){clearInterval(done);$(settings.mainEl).trigger("typewriteComplete");if(settings.continuous){$(settings.el).empty();processActions()}}},500);function select(action,callback){var charLen=action.to-action.from;var spanOpen='';var blankstr=new Array(charLen+1).join(" ");var chars=blankstr.split("");chars.forEach(function(char,index){$(settings.el).delay(settings.speed).queue(function(next){var newIndex=index+1;var newTo=action.to-newIndex;$(settings.el).html($(settings.el).html().replace(//g," \n "));var currentString=$(settings.el).text();var firstPart=currentString.slice(0,newTo);var selectPart=currentString.slice(newTo,action.to);var lastPart=currentString.slice(action.to,currentString.length);var newString=firstPart+spanOpen+selectPart+""+lastPart;$(this).html(newString.replace(/ \n /g,"
"));next();if(index===chars.length-1){settings.queue=settings.queue-1;$(settings.mainEl).trigger("typewriteSelected",action)}})})}function delay(time){$(settings.el).delay(time).queue(function(next){next();settings.queue=settings.queue-1;$(settings.mainEl).trigger("typewriteDelayEnded")})}function remove(remove){var blankstr=new Array(remove.num+1).join(" ");var chars=blankstr.split("");var removeType=typeof remove.type!=="undefined"?remove.type:"stepped";if(removeType!=="stepped"&&removeType!=="whole"){removeType="stepped"}if(removeType==="stepped"){chars.forEach(function(char,index){$(settings.el).delay(settings.speed).queue(function(next){$(settings.el).html($(settings.el).html().replace(//g," \n "));var currText=$(this).text().substring(0,$(this).text().length-1);$(this).html(currText.replace(/ \n /g,"
"));next();if(index===chars.length-1){settings.queue=settings.queue-1;$(settings.mainEl).trigger("typewriteRemoved",remove)}})},this)}if(removeType==="whole"){$(settings.el).delay(settings.speed).queue(function(next){$(settings.el).html($(settings.el).html().replace(//g," \n "));var currText=$(this).text().substring(0,$(this).text().length-remove.num);$(this).html(currText.replace(/ \n /g,"
"));next();settings.queue=settings.queue-1;$(settings.mainEl).trigger("typewriteRemoved",remove)})}}function typeText(text){var chars=text.split("");chars.forEach(function(char,index){$(settings.el).delay(settings.speed).queue(function(next){var text=$(this).html()+char;$(this).html(text);next();if(index===chars.length-1){settings.queue=settings.queue-1;$(settings.mainEl).trigger("typewriteTyped",text)}})},this)}function newLine(){$(settings.el).delay(settings.speed).queue(function(next){var currTextNoCurr=$(this).html().substring(0,$(this).html().length);$(this).html(currTextNoCurr+"
");next();settings.queue=settings.queue-1;$(settings.mainEl).trigger("typewriteNewLine")})}function removeSelection(){if($(".typewriteSelected").length>0){var selectionText=$(".typewriteSelected").text();$(".typewriteSelected").replaceWith(selectionText)}}}})(jQuery); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typewrite 2 | 3 | A javascript typewriter library which animates the **typing**, **deleting**, and **selecting** of text on a page 4 | 5 | 6 | 7 | ### Demo 8 | See [here](https://rawgit.com/mrvautin/typewrite/master/index.html "Demo"). 9 | 10 | ### Installation 11 | 12 | `typewrite` is a jQuery plugin, so it needs to be included in your HTML after jQuery. e.g: 13 | 14 | From repo: 15 | 16 | ``` javascript 17 | 18 | 19 | ``` 20 | 21 | From CDN: 22 | ``` javascript 23 | 24 | 25 | ``` 26 | 27 | Setup your target element to type into: 28 | 29 | ``` html 30 |
31 | ``` 32 | 33 | Some `typewrite` demo actions with default settings: 34 | 35 | ``` javascript 36 | $(document).ready(function(){ 37 | $('#typewriteText').typewrite({ 38 | actions: [ 39 | {type: 'Hello. '}, 40 | {type: '
'}, 41 | {type: 'Weclome '}, 42 | {delay: 1500}, 43 | {remove: {num: 1, type: 'stepped'}}, 44 | {select: {from: 11, to: 16}}, 45 | {delay: 2000}, 46 | {remove: {num: 5, type: 'whole'}}, 47 | {delay: 300}, 48 | {type: 'lcome to typewrite. '}, 49 | {type: '
'}, 50 | {type: 'It\'s just so easy to setup and use.'} 51 | ] 52 | }); 53 | }); 54 | ``` 55 | 56 | ### Using typewrite 57 | 58 | `typewrite` works on actions. You pass an array of actions which will be executed in order. See example above. 59 | 60 | ### actions 61 | 62 | `typewrite` can add text, delay (pause), remove text and even select text. 63 | 64 | #### Typing 65 | 66 | Adding text is done by passing an object with a key of `type` and a value of the text you would like typed. e.g: 67 | 68 | ``` javascript 69 | {type: 'Hello.'} 70 | ``` 71 | 72 | #### Remove 73 | 74 | Removing text is done by passing a nested object with a key of `remove` and a nested object with the number of characters and the type of remove. 75 | 76 | To remove 5 characters, one character at a time: 77 | 78 | ``` javascript 79 | {remove: {num: 5, type: 'stepped'}} 80 | ``` 81 | 82 | To remove 5 characters, in one whole remove: 83 | 84 | ``` javascript 85 | {remove: {num: 5, type: 'whole'}} 86 | ``` 87 | 88 | Note: Generally you might want to use the `whole` remove after you have selected some text 89 | 90 | #### Select 91 | 92 | Selecting text is done by passing a nested object with a key of `select` and a nested object with the index of characers you want to select. 93 | 94 | The following will select from the 11th character to the 16th: 95 | 96 | ``` javascript 97 | {select: {from: 11, to: 16}} 98 | ``` 99 | 100 | #### Delay (pause) 101 | 102 | Delay (pause) is done by passing an object with a key of `delay` and a value with the amount of time in milliseconds to delay or pause. 103 | 104 | The following will delay for 1500 milliseconds (1.5 seconds). 105 | 106 | ``` javascript 107 | {delay: 1500} 108 | ``` 109 | 110 | #### Speed 111 | 112 | You can change the typing speed midway through the actions by passing an object with a key of `speed` and a value with the amount of characters per second. 113 | 114 | The following will change the typing speed to 22 characters per second. 115 | 116 | ``` javascript 117 | {speed: 22} 118 | ``` 119 | 120 | ### Options 121 | 122 | **speed** {integer}: Characters per second - Default: `12` 123 | 124 | **blinkSpeed** {integer}: Blinks per second of cursor - Default: `2` 125 | 126 | **showCursor** {boolean}: Whether to show the cursor - Default: `true` 127 | 128 | **blinkingCursor** {boolean}: Whether the cursor blinks - Default: `true` 129 | 130 | **cursor** {string}: The cursor character - Default: `'|'` 131 | 132 | **selectedBackground** {string}: The Hex color value of the selected background - Default: `'#F1F1F1'` 133 | 134 | **selectedText** {string}: The Hex color value of the selected text - Default: `'#333333'` 135 | 136 | **continuous** {boolean}: Whether to run on continuous loop - Default: `false` 137 | 138 | Providing option are done by setting the object with the actions. Eg: 139 | 140 | ``` javascript 141 | $('#typewriteText').typewrite({ 142 | blinkSpeed: 15, 143 | cursor: '@', 144 | actions: [ 145 | {type: 'Hello. '}, 146 | {type: '
'}, 147 | {type: 'Weclome '}, 148 | {delay: 1500}, 149 | {remove: {num: 1, type: 'stepped'}}, 150 | {select: {from: 11, to: 16}}, 151 | {delay: 2000}, 152 | {remove: {num: 5, type: 'whole'}}, 153 | {delay: 300}, 154 | {type: 'lcome to `typewrite`. '}, 155 | {type: '
'}, 156 | {type: 'It\'s so easy to setup and use.'} 157 | ] 158 | }); 159 | ``` 160 | 161 | ### Styling 162 | 163 | If you want to add additional CSS to further style `typewrite`, please use the following CSS classes: 164 | 165 | `.blinkingCursor` is the class applied to the blinking cursor (if enabled) 166 | 167 | `.typewriteSelected` is the class applied to the selected text. You may want to add CSS rather then setting the `selectedBackground` and `selectedText` values. 168 | 169 | ### Events 170 | 171 | `typewrite` supports the use of events for all the actions. Some actions trigger returned data and some don't, see below for examples: 172 | 173 | ``` javascript 174 | $('#typewriteText') 175 | .on('typewriteStarted', function() { 176 | console.log('typewrite has started'); 177 | }) 178 | .on('typewriteComplete', function() { 179 | console.log('typewrite has complete'); 180 | }) 181 | .on('typewriteTyped', function(event, data) { 182 | console.log('typewrite has typed', data); 183 | }) 184 | .on('typewriteRemoved', function(event, data) { 185 | console.log('typewrite has removed', data); 186 | }) 187 | .on('typewriteNewLine', function() { 188 | console.log('typewrite has added a new line'); 189 | }) 190 | .on('typewriteSelected', function(event, data) { 191 | console.log('typewrite has selected text', data); 192 | }) 193 | .on('typewriteDelayEnded', function() { 194 | console.log('typewrite delay has ended'); 195 | }) 196 | .typewrite({ 197 | actions: [ 198 | {type: 'Hello. '}, 199 | {type: '
'}, 200 | {type: 'Weclome '}, 201 | {delay: 1500}, 202 | {remove: {num: 1, type: 'stepped'}}, 203 | {select: {from: 11, to: 16}}, 204 | {delay: 2000}, 205 | {remove: {num: 5, type: 'whole'}}, 206 | {delay: 300}, 207 | {type: 'lcome to typewrite. '}, 208 | {type: '
'}, 209 | {type: 'It\'s just so easy to setup and use.'} 210 | ] 211 | }); 212 | ``` -------------------------------------------------------------------------------- /src/typewrite.js: -------------------------------------------------------------------------------- 1 | (function ($){ 2 | $.fn.typewrite = function(options){ 3 | // setup defaults 4 | var settings = $.extend({ 5 | speed: 12, 6 | blinkSpeed: 2, 7 | showCursor: true, 8 | blinkingCursor: true, 9 | cursor: '|', 10 | selectedBackground: '#F1F1F1', 11 | selectedText: '#333333', 12 | continuous: false 13 | }, options); 14 | 15 | // set the blink speed of the cursor 16 | settings.blinkSpeed = 1000 / settings.blinkSpeed; 17 | 18 | // add cursor if set to true. 19 | if(settings.showCursor){ 20 | $(this).html('' + settings.cursor + ''); 21 | if(settings.blinkingCursor){ 22 | // cache cursor object 23 | var $cursor = $('.blinkingCursor'); 24 | setInterval(function(){ 25 | // check if blinkingCursor is set to opacity 26 | if(settings.blinkingCursor === 'opacity'){ 27 | // toggle cursor opacity 28 | if($cursor.css('opacity') === '1'){ 29 | $cursor.css({'opacity': 0}); 30 | }else{ 31 | $cursor.css({'opacity': 1}); 32 | } 33 | }else{ 34 | // default to show/hide 35 | $cursor.toggle(); 36 | } 37 | }, settings.blinkSpeed); 38 | } 39 | }else{ 40 | $(this).html(''); 41 | } 42 | 43 | // set the main element, not the span 44 | settings.mainEl = this; 45 | 46 | // add a span to hold our text 47 | settings.el = $(this).children('span')[0]; 48 | 49 | // set the typing speed 50 | settings.speed = 1000 / settings.speed; 51 | 52 | // holds the tags in an array 53 | var actions = options.actions; 54 | 55 | // holds the queue of actions 56 | settings.queue = actions.length; 57 | 58 | // trigger the 'typewriteStarted' event 59 | $(settings.mainEl).trigger('typewriteStarted'); 60 | 61 | // execute the actions 62 | processActions(); 63 | 64 | function processActions(){ 65 | actions.forEach(function(element, index){ 66 | // changes the typing speed 67 | if(Object.keys(element).includes('speed')){ 68 | settings.speed = 1000 / element.speed; 69 | } 70 | 71 | // removes any previous selections 72 | if(!Object.keys(element).includes('speed')){ 73 | removeSelection(); 74 | } 75 | 76 | // adds a delay to the sequence 77 | if(Object.keys(element).includes('delay')){ 78 | delay(element.delay); 79 | } 80 | 81 | // removes characters 82 | if(Object.keys(element).includes('remove')){ 83 | remove(element.remove); 84 | } 85 | 86 | // adds a span which selects the text 87 | if(Object.keys(element).includes('select')){ 88 | select(element.select); 89 | } 90 | 91 | // types out text 92 | if(Object.keys(element).includes('type')){ 93 | if(element.type === '
'){ 94 | newLine(); 95 | }else{ 96 | var text = $('
').html(element.type).text(); 97 | typeText(text, settings); 98 | } 99 | } 100 | }); 101 | } 102 | 103 | var done = setInterval(function(){ 104 | if(settings.queue === 0){ 105 | clearInterval(done); 106 | $(settings.mainEl).trigger('typewriteComplete'); 107 | if(settings.continuous){ 108 | $(settings.el).empty(); 109 | processActions(); 110 | } 111 | } 112 | }, 500); 113 | 114 | // adds a wrapper span around given index of characters to mimick selecting the text 115 | function select(action, callback){ 116 | var charLen = action.to - action.from; 117 | var spanOpen = ''; 118 | var blankstr = new Array(charLen + 1).join(' '); 119 | var chars = blankstr.split(''); 120 | chars.forEach(function(char, index){ 121 | $(settings.el).delay(settings.speed).queue(function (next){ 122 | var newIndex = index + 1; 123 | var newTo = action.to - newIndex; 124 | $(settings.el).html($(settings.el).html().replace(//g, ' \n ')); 125 | var currentString = $(settings.el).text(); 126 | var firstPart = currentString.slice(0, newTo); 127 | var selectPart = currentString.slice(newTo, action.to); 128 | var lastPart = currentString.slice(action.to, currentString.length); 129 | var newString = firstPart + spanOpen + selectPart + '' + lastPart; 130 | $(this).html(newString.replace(/ \n /g, '
')); 131 | next(); 132 | 133 | // we are done, remove from queue 134 | if(index === chars.length - 1){ 135 | settings.queue = settings.queue - 1; 136 | $(settings.mainEl).trigger('typewriteSelected', action); 137 | } 138 | }); 139 | }); 140 | } 141 | 142 | // pauses/delay 143 | function delay(time){ 144 | $(settings.el).delay(time).queue(function (next){ 145 | next(); 146 | 147 | // we are done, remove from queue 148 | settings.queue = settings.queue - 1; 149 | $(settings.mainEl).trigger('typewriteDelayEnded'); 150 | }); 151 | } 152 | 153 | // removes text. Can be stepped (one character at a time) or all in one hit 154 | function remove(remove){ 155 | var blankstr = new Array(remove.num + 1).join(' '); 156 | var chars = blankstr.split(''); 157 | 158 | // default to stepped 159 | var removeType = typeof remove.type !== 'undefined' ? remove.type : 'stepped'; 160 | 161 | // if invalid, set to stepped 162 | if(removeType !== 'stepped' && removeType !== 'whole'){ 163 | removeType = 'stepped'; 164 | } 165 | 166 | if(removeType === 'stepped'){ 167 | chars.forEach(function(char, index){ 168 | $(settings.el).delay(settings.speed).queue(function (next){ 169 | $(settings.el).html($(settings.el).html().replace(//g, ' \n ')); 170 | var currText = $(this).text().substring(0, $(this).text().length - 1); 171 | $(this).html(currText.replace(/ \n /g, '
')); 172 | next(); 173 | 174 | // we are done, remove from queue 175 | if(index === chars.length - 1){ 176 | settings.queue = settings.queue - 1; 177 | $(settings.mainEl).trigger('typewriteRemoved', remove); 178 | } 179 | }); 180 | }, this); 181 | } 182 | if(removeType === 'whole'){ 183 | $(settings.el).delay(settings.speed).queue(function (next){ 184 | $(settings.el).html($(settings.el).html().replace(//g, ' \n ')); 185 | var currText = $(this).text().substring(0, $(this).text().length - remove.num); 186 | $(this).html(currText.replace(/ \n /g, '
')); 187 | next(); 188 | 189 | // we are done, remove from queue 190 | settings.queue = settings.queue - 1; 191 | $(settings.mainEl).trigger('typewriteRemoved', remove); 192 | }); 193 | } 194 | } 195 | 196 | // types out the given text one character at a time 197 | function typeText(text){ 198 | var chars = text.split(''); 199 | chars.forEach(function(char, index){ 200 | $(settings.el).delay(settings.speed).queue(function (next){ 201 | var text = $(this).html() + char; 202 | $(this).html(text); 203 | next(); 204 | 205 | // we are done, remove from queue 206 | if(index === chars.length - 1){ 207 | settings.queue = settings.queue - 1; 208 | $(settings.mainEl).trigger('typewriteTyped', text); 209 | } 210 | }); 211 | }, this); 212 | } 213 | 214 | // adds a new line
to the html 215 | function newLine(){ 216 | $(settings.el).delay(settings.speed).queue(function (next){ 217 | var currTextNoCurr = $(this).html().substring(0, $(this).html().length); 218 | $(this).html(currTextNoCurr + '
'); 219 | next(); 220 | 221 | // we are done, remove from queue 222 | settings.queue = settings.queue - 1; 223 | $(settings.mainEl).trigger('typewriteNewLine'); 224 | }); 225 | } 226 | 227 | function removeSelection(){ 228 | // check selection exists 229 | if($('.typewriteSelected').length > 0){ 230 | // removes selection 231 | var selectionText = $('.typewriteSelected').text(); 232 | $('.typewriteSelected').replaceWith(selectionText); 233 | } 234 | } 235 | }; 236 | }(jQuery)); 237 | --------------------------------------------------------------------------------