├── .gitignore ├── LICENSE ├── README.md ├── converting.mp4 ├── demo.gif ├── icon.png ├── index.css ├── index.html ├── index.js ├── nu_chi_1080p.mp4 ├── nu_chi_1080p_poster.jpg └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xmader 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🐸ASCII字符画视频流 2 | 3 | > 自动实时转换视频到ASCII字符画视频 4 | 5 | ![](demo.gif) 6 | 7 | 只是一个小demo 8 | 9 | 灵感来自 https://github.com/HFO4/plus1s.live 10 | 11 | [演示地址:https://xmader.github.io/ascii_live/](https://xmader.github.io/ascii_live/) 12 | 13 | * 点一下字符画就能像视频一样播放/暂停 14 | 15 | * 字符画可以像正常的文本一样复制/粘贴 16 | 17 | * 滋磁调整ASCII字符画视频的字号 (字号越小分辨率越高,占用的CPU百分比也就越高) 18 | 19 | * 滋磁播放本地视频 (转换完全在本地进行,视频不会上传到任何服务器,所以不必担心隐私问题) 20 | 21 | 22 | * 滋磁在ASCII字符画视频的左侧/上方同时显示转换前的原始视频 23 | 24 | * 全屏观看效果更佳 25 | 26 | * **v1.3.0新增** 打开控制台也能看到ASCII字符画视频 (目前只在字号为16px时可用) (仅在Firefox中测试通过) 27 | 28 | ## 这是如何实现的? 29 | 30 | 使用了canvas作为过渡,具体实现可以查看[源码](https://github.com/Xmader/ascii_live/blob/master/index.js)。 31 | 32 | ``` 33 | 原始视频 => canvas => ASCII字符画视频 34 | ``` 35 | 36 | 转换视频不依赖任何服务器,完全在你的浏览器中进行,离线也可以使用,具体转换效率取决于你的CPU性能。 37 | 38 | ## License 39 | 40 | MIT 41 | -------------------------------------------------------------------------------- /converting.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xmader/ascii_live/438bb7766e48809d889555f7b8266b5598d1f3cd/converting.mp4 -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xmader/ascii_live/438bb7766e48809d889555f7b8266b5598d1f3cd/demo.gif -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xmader/ascii_live/438bb7766e48809d889555f7b8266b5598d1f3cd/icon.png -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.loli.net/css?family=Roboto+Mono:300"); 2 | video { 3 | width: 99.5%; 4 | } 5 | 6 | @media screen and (min-width: 767px) { 7 | body { 8 | margin-left: 5px; 9 | margin-right: 10px; 10 | } 11 | video { 12 | width: 50%; 13 | float: left; 14 | margin-right: 5px; 15 | } 16 | #txt { 17 | position: absolute; 18 | } 19 | } 20 | 21 | #txt { 22 | font-family: "Roboto Mono", "宋体", "simsun", monospace; 23 | font-size: 10px; 24 | line-height: 10px; 25 | width: 0px; 26 | transform-origin: top left; 27 | left: -5px; 28 | right: 0px; 29 | top: 0px; 30 | } 31 | 32 | canvas { 33 | display: none; 34 | } 35 | 36 | .clear { 37 | clear: both; 38 | } 39 | 40 | .range_div { 41 | float: left; 42 | } 43 | 44 | .upload-div { 45 | margin-right: 10px; 46 | margin-bottom: 10px; 47 | } 48 | 49 | @media screen and (min-width: 767px) { 50 | .upload-div { 51 | float: right; 52 | } 53 | } 54 | 55 | #upload-button, 56 | #text-button { 57 | height: 27px; 58 | width: 101px; 59 | } 60 | 61 | .upload-div p { 62 | margin-bottom: 3px; 63 | } 64 | 65 | #upload-button { 66 | position: absolute; 67 | cursor: pointer; 68 | opacity: 0; 69 | } 70 | 71 | .hidden { 72 | position: absolute; 73 | visibility: hidden; 74 | width: 99%; 75 | top: 0; 76 | } 77 | 78 | @media screen and (max-width: 767px) { 79 | .hidden { 80 | width: 96%; 81 | } 82 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ASCII字符画视频流 - 自动实时转换视频到ASCII字符画视频 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

19 |     
20 | 21 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ASCII字符画视频流 (https://www.xmader.com/ascii_live/) 3 | * 4 | * Copyright (c) 2018 Xmader 5 | * Released under the MIT license 6 | * 7 | * Source Code: https://github.com/Xmader/ascii_live 8 | * 9 | * 部分代码参考了 https://gist.github.com/justjavac/6696499 10 | * 11 | * 为了更好的兼容性,不使用任何ES6特性和新增语法 12 | * 13 | * 如果你在这里发现了bug,请多多包涵,欢迎使用 https://github.com/Xmader/ascii_live/issues 向我提出。 14 | * 15 | */ 16 | 17 | var video = document.getElementById("video1"); 18 | var cv = document.getElementById('cv'); 19 | var ctx = cv.getContext('2d'); 20 | var txtDiv = document.getElementById('txt'); 21 | var uploadBtn = document.getElementById("upload-button"); 22 | 23 | var font_size_range = document.getElementById("font_size_range") 24 | var font_size_span = document.getElementById("font_size") 25 | var footer = document.getElementById("footer") 26 | 27 | var isFirefox = navigator.userAgent.indexOf("Firefox") > -1 28 | var isWindows = navigator.userAgent.indexOf("Windows") > -1 29 | 30 | if (isWindows) { // 中易宋体只在Windows系统中可用 31 | var scale_x = isFirefox ? 0.0895 : 0.1 32 | } 33 | else { 34 | var scale_x = 0.0835 35 | } 36 | 37 | var video_show = false 38 | 39 | var font_size = 2 // ASCII字符画的字号,数值越小分辨率越高,占用的CPU百分比也就越高 (和占用的内存没有关系),必须是2的倍数,单位: px (像素) 40 | 41 | // 根据调整字号滑动条动态地改变ASCII字符画的字号 42 | function change_font_size() { 43 | var n = +font_size_range.value // 获取的原始值是一个字符串,用"+"号将它转换成一个数字 44 | font_size_span.innerText = n 45 | 46 | txtDiv.style["transform"] = "scale(" + (n * scale_x) + "," + (n / 10) + ")" 47 | 48 | font_size = n 49 | 50 | convert() // 实时重绘,不需要等待额外的20毫秒 51 | 52 | font_size_range.scrollIntoView() // 调整字号结束后页面自动回到滑动条所在的位置 53 | } 54 | 55 | // 根据灰度生成相应字符 56 | function toText(g) { 57 | if (g <= 30) { 58 | return '#'; 59 | } else if (g > 30 && g <= 60) { 60 | return '&'; 61 | } else if (g > 60 && g <= 120) { 62 | return '$'; 63 | } else if (g > 120 && g <= 150) { 64 | return '*'; 65 | } else if (g > 150 && g <= 180) { 66 | return 'o'; 67 | } else if (g > 180 && g <= 210) { 68 | return '!'; 69 | } else if (g > 210 && g <= 240) { 70 | return ';'; 71 | } else { 72 | return ' '; 73 | } 74 | } 75 | 76 | // 根据rgb值计算灰度 77 | function getGray(r, g, b) { 78 | return 0.299 * r + 0.578 * g + 0.114 * b; 79 | } 80 | 81 | // 转换 82 | function convert() { 83 | var video_width = video.clientWidth 84 | var video_height = video.clientHeight 85 | var txtDiv_height = txtDiv.clientHeight 86 | 87 | cv.width = video_width 88 | cv.height = video_height 89 | 90 | txtDiv.style.left = video_show ? 91 | video_width + 10 + 'px' 92 | : "5px" 93 | 94 | footer.style["margin-top"] = video_show ? 95 | "0px" 96 | : (txtDiv_height * (font_size / 10)) + 'px' 97 | 98 | if (document.body.clientWidth < 767) { // 小屏幕设备 99 | var footer_margin_top = -txtDiv_height * (1 - font_size / 10) // (1 - font_size / 10) 可能会是负数 100 | 101 | if (footer_margin_top > 0) { 102 | footer_margin_top = (font_size - 10) * 12 103 | } 104 | 105 | footer.style["margin-top"] = footer_margin_top + "px" 106 | } 107 | 108 | ctx.drawImage(video, 0, 0, video_width, video_height) 109 | 110 | var imgData = ctx.getImageData(0, 0, video_width, video_height); 111 | var imgDataArr = imgData.data; 112 | var imgDataWidth = imgData.width; 113 | var imgDataHeight = imgData.height; 114 | var html = ''; 115 | for (h = 0; h < imgDataHeight; h += font_size) { 116 | var p = ''; 117 | for (w = 0; w < imgDataWidth; w += (font_size / 2)) { 118 | var index = (w + imgDataWidth * h) * 4; 119 | var r = imgDataArr[index + 0]; 120 | var g = imgDataArr[index + 1]; 121 | var b = imgDataArr[index + 2]; 122 | var gray = getGray(r, g, b); 123 | p += toText(gray); 124 | } 125 | p += '\n'; 126 | html += p; 127 | } 128 | txtDiv.innerHTML = html; 129 | 130 | if (font_size >= 16) { 131 | console.clear() 132 | console.log(html) 133 | } 134 | 135 | } 136 | 137 | // 获取上传的视频文件 138 | function getFile() { 139 | var reader = new FileReader(); 140 | reader.readAsDataURL(uploadBtn.files[0]); 141 | 142 | video.src = "converting.mp4" 143 | video.poster = null 144 | 145 | reader.onload = function () { 146 | video.src = reader.result; // 是一个base64 Data URL字符串 147 | video.play() 148 | } 149 | } 150 | 151 | // 显示/隐藏原始视频 152 | function toggle_video() { 153 | video.classList.toggle('hidden') 154 | video_show = !video_show 155 | } 156 | 157 | window.onload = function () { 158 | change_font_size() 159 | window.setInterval(convert, 20); 160 | } 161 | 162 | uploadBtn.onchange = getFile 163 | 164 | txtDiv.onclick = function () { 165 | video.paused ? video.play() : video.pause() 166 | } 167 | -------------------------------------------------------------------------------- /nu_chi_1080p.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xmader/ascii_live/438bb7766e48809d889555f7b8266b5598d1f3cd/nu_chi_1080p.mp4 -------------------------------------------------------------------------------- /nu_chi_1080p_poster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xmader/ascii_live/438bb7766e48809d889555f7b8266b5598d1f3cd/nu_chi_1080p_poster.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ascii_live", 3 | "version": "1.3.2", 4 | "description": "ASCII字符画视频流", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/Xmader/ascii_live" 8 | }, 9 | "author": "xmader", 10 | "scripts": { 11 | "web-service": "python -m SimpleHTTPServer 3010" 12 | }, 13 | "license": "MIT" 14 | } 15 | --------------------------------------------------------------------------------