Nodejs命令行光標位置的設置

引子

不少使用nodejs開發的工具都會提供一個命令行操做界面(CLI),這些工具在執行的過程當中會實時更新執行進度或數據的下載解壓縮進度等,例如10%,20%,30%...
這些信息會再終端的同一行的同一位置顯示,而不是一行一行逐次打印顯示......
那麼其是怎麼實現的?nodejs有沒有相應地模塊提供這個功能呢?node

嗯,NodeJS中有的,它就是readline模塊,這個模塊提供了正行數據讀取寫入,終端提示符位置控制等API工具

實現的功能

本文章提供一個樣例代碼,主要說明終端提示符位置控制問題,把須要注意的點都列在其中了ui

終端字符顯示寬度問題
終端界面可以顯示的字符區域大小問題
終端當前提示符的位置問題
終端相同位置上內容實時更新的問題命令行

這些問題在樣例中都有涉及及說明到code

使用nodejs的readline模塊對命令行控制終端的提示符位置進行控制
模擬詢問用戶是否啓動應用
啓動應用後實時更新運行進度信息從1%~100%,此信息顯示同一行的同一個位置orm

代碼實現

var readline = require('readline');
    var util=require('util');
    var inputStream=process.stdin;
    var outputStream=process.stdout;
    var rl = readline.createInterface({
        input: inputStream,
        output: outputStream,
        terminal:true
    });
    var promptStr="MyApp> ";
    
   
    //得到字符串實際長度,中文2,英文1
    //控制檯中中文佔用2個英文字符的寬度
    var getDisplayLength=function(str) {
        var realLength = 0, len = str.length, charCode = -1;
        for (var i = 0; i < len; i++) {
            charCode = str.charCodeAt(i);
            if (charCode >= 0 && charCode <= 128) realLength += 1;
            else realLength += 2;
        }
        return realLength;
    };
    
   
    //計算一個字符串在當前控制檯中佔用的行數和列數信息
    //outputStream.rows及outputStream.columns屬性爲當前控制檯的顯示的窗口的大寫
    var getStrOccRowColumns=function(str){
        //str=promptStr+str;
        var consoleMaxRows=outputStream.rows;
        var consoleMaxColumns=outputStream.columns;
        var strDisplayLength=getDisplayLength(str);
        var rows=parseInt(strDisplayLength/consoleMaxColumns,10);
        var columns=parseInt(strDisplayLength-rows*consoleMaxColumns,10);
    
        return {
            rows:rows,
            columns:columns
        }
    
    };
    
    //console.log(getDisplayLength(promptStr));
    rl.setPrompt(promptStr);
    rl.prompt();
    
    rl.question("你想要個啓動應用處理嗎?", function(answer) {
        rl.prompt();
        rl.write(util.format('啓動應用獲得的回覆爲:%s\r\n', answer));
        //更新同一個位置顯示的字符信息,每1秒更新1一次,一直到100%
        var k= 0,max=100,prevOutputContent,outputContent,
        cursorDx=0,cursorDy= 0,dxInfo;
        //計算
        rl.prompt();
        var interval=setInterval(function(){
            if(k<max){
                k++;
                outputContent=util.format('%d% done!', k);
                  //將光標移動到已經寫入的字符前面,             
          readline.moveCursor(outputStream,cursorDx*-1,cursorDy*-1);
              //清除當前光標後的全部文字信息,以便接下來輸出信息能寫入到控制檯
                readline.clearScreenDown(outputStream);
                outputStream.write(outputContent);
                //不要使用這個方法,此方法中寫入的數據會被做爲line事件的輸入源讀取
                //rl.write(outputContent);
                dxInfo=getStrOccRowColumns(outputContent);
    
                cursorDx=dxInfo.columns;
                cursorDy=dxInfo.rows;
    
            }else{
                outputStream.write(util.format('\r\n'));
                rl.prompt();
                outputStream.write(util.format('%s\r\n',"執行完成"));
                clearInterval(interval);
                rl.close();
    
            }
        },100);
    
    });
    
    
    rl.on('close',function(){
       process.exit(0);
    });
相關文章
相關標籤/搜索