SOURCE

console 命令行工具 X clear

                    
>
console
let font_metrics = document.querySelector('.font-metrics');
let text = document.querySelector('.text');
let lines = text.textContent.split('\n');
let cursor = document.querySelector('.cursor');
let markers = document.querySelector('.markers');
let lineHeight = 20;
let font_size = 13;
let anchor = {
    row: 0,
    col: 0
}
let pointer = {
    row: 0,
    col: 0
}
let selectStart = false;
let anchorColumn = 0;
function debounce(callback, time) {
    return function () {
        clearTimeout(callback.timer);
        callback.timer = setTimeout(callback, time);
    }
}
let restartBlink = debounce(function () {
    cursor.classList.add('cursor-blink');
}, 800);
function measureText(fontMetrics, text) {
    if (!text) {
        return 0;
    }
    fontMetrics.textContent = text;
    return fontMetrics.getBoundingClientRect().width;
}
function cursorTranslate(x, y) {
    cursor.style.left = x + 'px';
    cursor.style.top = y + 'px';
}
function onCursorActive() {
    let line = lines[pointer.row];
    let left = measureText(font_metrics, line.slice(0, pointer.col));
    let top = pointer.row * lineHeight;
    cursorTranslate(left, top);
    cursor.classList.remove('cursor-blink');
    restartBlink();
}
function onSelect() {
    updateSelection();
}
function updateSelection() {
    let rows = Math.abs(pointer.row - anchor.row) + 1;
    let _isBackwards = isBackwards();
    let isSameRow = anchor.row == pointer.row;

    clearSelection();

    if (_isBackwards) {
        for (let i = pointer.row; i <= anchor.row; ++i) {
            let marker = document.createElement('div');
            let line = lines[i];
            let isStartRow = i == pointer.row;
            let isEndRow = i == anchor.row;
            let isSameRow = pointer.row == anchor.row;
            let left = 0;
            let width = 0;
            let top = i * lineHeight;
            let eof = false;
            if (isSameRow) {
                left = measureText(font_metrics, line.slice(0, pointer.col));
                width = measureText(font_metrics, line.slice(pointer.col, anchor.col));
                eof = anchor.col === line.length;
            } else if (isStartRow) {
                left = measureText(font_metrics, line.slice(0, pointer.col));
                width = measureText(font_metrics, line.slice(pointer.col));
                eof = true;

            } else if (isEndRow) {
                left = 0;
                width = measureText(font_metrics, line.slice(0, anchor.col));
                eof = false;
            } else {
                left = 0;
                width = measureText(font_metrics, line);
                eof = true;
            }
            if (eof) {
                width += 5;
            }
            marker.style.position = 'absolute';
            marker.style.left = left + 'px';
            marker.style.top = top + 'px';
            marker.style.width = width + 'px';
            marker.style.height = lineHeight + 'px';
            marker.style.background = '#ddd';
            if (!markers.contains(marker)) {
                markers.appendChild(marker);
            }
        }
    } else {
        for (let i = anchor.row; i < rows + anchor.row; ++i) {
            let marker = document.createElement('div');
            let line = lines[i];
            let isEndRow = i == pointer.row;
            let isStartRow = i == anchor.row;
            let left = 0;
            let width = 0;
            let top = i * lineHeight;
            let eof = false;
            if (isSameRow) {
                left = measureText(font_metrics, line.slice(0, anchor.col));
                width = measureText(font_metrics, line.slice(anchor.col, pointer.col));
                eof = line.length && pointer.col == line.length;
            } else if (isStartRow) {
                left = measureText(font_metrics, line.slice(0, anchor.col));
                width = measureText(font_metrics, line.slice(anchor.col));
                eof = true;
            } else if (isEndRow) {
                left = 0;
                width = measureText(font_metrics, line.slice(0, pointer.col));
                eof = false;
            } else {
                left = 0;
                width = measureText(font_metrics, line);
                eof = true;
            }
            if (eof) {
                width += 5;
            }
            marker.style.position = 'absolute';
            marker.style.left = left + 'px';
            marker.style.top = top + 'px';
            marker.style.width = width + 'px';
            marker.style.height = lineHeight + 'px';
            marker.style.background = '#ddd';
            if (!markers.contains(marker)) {
                markers.appendChild(marker);
            }
        }
    }
}
function clearSelection() {
    markers.innerHTML = "";
}
function isBackwards() {
    return pointer.row < anchor.row || (pointer.row == anchor.row && pointer.col < anchor.col);
}
function moveTo(row, col) {
    let lastRow = pointer.row;
    let lastCol = pointer.col;
    if (row >= lines.length) {
        row = Math.max(0, lines.length - 1);
        col = pointer.col = lines[pointer.row].length;
    } else if (row < 0) {
        row = 0;
        col = 0;
    } else {
        pointer.row = row;
        pointer.col = Math.min(lines[row].length, Math.max(0, col));
    }
    if (lastRow != pointer.row || lastCol != pointer.col) {
        onCursorActive();
    }
}
function moveCursorRight() {
    let line = lines[pointer.row];
    if (pointer.col >= line.length) {
        moveTo(pointer.row + 1, 0);
    } else {
        moveTo(pointer.row, pointer.col + 1);
    }
    anchorColumn = pointer.col;
}
function moveCursorLeft() {
    let line = lines[pointer.row - 1];
    if (pointer.col == 0) {
        moveTo(pointer.row - 1, line ? line.length : 0);
    } else {
        moveTo(pointer.row, pointer.col - 1);
    }
    anchorColumn = pointer.col;
}
function moveCursorUp() {
    moveTo(pointer.row - 1, anchorColumn);
}
function moveCursorDown() {
    moveTo(pointer.row + 1, anchorColumn);
}
function findCoordsFromPoints(x, y) {
    let row = Math.floor(y / lineHeight);
    let col = 0;
    let line = lines[row] || lines[lines.length - 1] || [];
    let start = Math.floor(x / font_size);
    let left = 0
    for (; start <= line.length; ++start) {
        left = measureText(font_metrics, line.slice(0, start));
        if (left >= x) {
            break;
        }
    }
    col = start;
    return {
        col, row
    }
}
function fromPoints(x, y) {
    let pos = findCoordsFromPoints(x, y);
    moveTo(pos.row, pos.col);
    anchorColumn = pos.col;
}
function setSelectStart() {
    selectStart = true;
    setTimeout(() => {
        anchor.row = pointer.row;
        anchor.col = pointer.col;
    }, 0)
}
function isPointInRange(x, y) {
    let pos = findCoordsFromPoints(x, y);
    let row = pos.row;
    let col = pos.col;
    if (isBackwards()) {

    } else {
        return (row >= anchor.row && row <= pointer.row);
    }
}
document.onkeydown = function (e) {
    let k = e.key;
    if (k == 'ArrowRight') {
        moveCursorRight();
    } else if (k == 'ArrowLeft') {
        moveCursorLeft();
    } else if (k == 'ArrowDown') {
        moveCursorDown();
    } else if (k == 'ArrowUp') {
        moveCursorUp();
    }
}
text.onmousedown = function (e) {
    if (e.which == 1) {
        let textBound = text.getBoundingClientRect();
        let x = e.clientX - textBound.left;
        let y = e.clientY - textBound.top;
        fromPoints(x, y);
        setSelectStart();
    }
}
text.oncontextmenu = function (e) {
    e.preventDefault();
}
document.onmouseup = function () {
    selectStart = false;
}
document.onmousemove = function (e) {
    if (selectStart) {
        let textBound = text.getBoundingClientRect();
        let x = e.clientX - textBound.left;
        let y = e.clientY - textBound.top;
        fromPoints(x, y);
        onSelect();
    }
}
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=, initial-scale=">
	<meta http-equiv="X-UA-Compatible" content="">
	<title></title>
</head>
<body>
	<div class="line">
        <div class="markers"></div>
        <pre class="text">var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;

var Anchor = exports.Anchor = function (doc, row, column) {
    this.$onChange = this.onChange.bind(this);
    this.attach(doc);

    if (typeof column == "undefined")
    this.setPosition(row.row, row.column);
    else
        this.setPosition(row, column);
};

(function () {
    oop.implement(this, EventEmitter);
    this.isEmpty = function () {
        return this.$isEmpty || (
            this.anchor.row == this.lead.row &&
            this.anchor.column == this.lead.column
        );
    };
    this.isMultiLine = function () {
        return !this.$isEmpty && this.anchor.row != this.cursor.row;
    };
    this.getCursor = function () {
        return this.lead.getPosition();
    };
    this.setSelectionAnchor = function (row, column) {
        this.$isEmpty = false;
        this.anchor.setPosition(row, column);
    };
}).call(Anchor.prototype);</pre>
<div class="cursor cursor-blink"></div>
<div class="font-metrics"></div>
    </div>
</body>
</html>
* {
    margin: 0;
    padding: 0;
}
@keyframes blink {
    0% {
        opacity: 0;
    }
    49% {
        opacity: 0;
    }
    50% {
        opacity: 1;
    }
    99% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}
.line .text {
    position: relative;
    font-size: 13px;
    font-family: Consolas;
    user-select: none;
    cursor: text;
    line-height: 20px;
    font-weight: normal;
    z-index: 1;
    background: transparent;
}
.line {
    position: relative;
    height: auto;
    background: #fff;
}
.line .cursor {
    position: absolute;
    left: 0;
    top: 0;
    height: 20px;
    width: 0;
    border-right: 1px solid #000;
    cursor: text;
    z-index: 2;
}
.line .cursor.cursor-blink {
    animation: blink 800ms infinite;
}
.line .font-metrics {
    position: absolute;
    left: 0;
    top: 0;
    width: auto;
    height: auto;
    padding: 0;
    margin: 0;
    border: none;
    font-size: 13px;
    font-family: Consolas;
    font-weight: normal;
    z-index: -1;
    white-space: pre;
}
.line .markers {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
}