一键修改b站评论样式,从横向文字改为近似的竖向文字,仅是为了有趣。
- // ==UserScript==
- // @name 评论魔术师
- // @namespace http://tampermonkey.net/
- // @version 0.3
- // @description 一键修改b站评论样式,从横向文字改为近似的竖向文字,仅是为了有趣。
- // @author thunder-sword
- // @match https://www.bilibili.com/video/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
- // @license MIT
- // @grant none
- // ==/UserScript==
- // 更新日志:
- /*
- v0.3:使点击回复时出现的评论框也加上魔术变化按钮,此版本会将页面中的所有评论框都加上按钮
- v0.2:添加“还原”按钮,还原文本为修改前文本
- */
- //默认设置
- var settings={
- "padChar": "_", //填充字符串,半角和空白会用此字符填充
- "row": 6, //分组的行数,手机上不展开默认显示6行
- "col": 19, //手机上默认显示19列,但为了避免溢出实际显示效果会-1
- };
- //变量作用:用于输入文本
- var event = document.createEvent('HTMLEvents');
- event.initEvent("input", true, true);
- event.eventType = 'message';
- //变量作用:用于检验是否已经转换过
- var lastNewStr='';
- //变量作用:记录上次原文本,方便还原
- var lastRawStr='';
- //作用:获取文本实际宽度
- function getByteLength(str) {
- let byteLength = 0;
- for (let i = 0; i < str.length; i++) {
- const c = str.charCodeAt(i);
- if (c > 255) {
- byteLength += 2;
- } else {
- byteLength += 1;
- }
- }
- return byteLength;
- }
- function verticalText(str, numRows, padChar='_') {
- // 将字符串转换成字符数组,每个元素为一个字符
- const chars = str.split('');
- const numCols = Math.ceil(chars.length / numRows); // 计算列数
- const r###lt = []; // 存储竖直排版后的字符数组
- let i, j;
- for (i = 0; i < numRows; i++) {
- // 初始化每行的字符数组
- const row = [];
- for (j = 0; j < numCols; j++) {
- // 计算字符在原始字符串中的索引
- const index = j * numRows + i;
- // 将索引越界的字符用填充字符代替
- const char = chars[index] || padChar;
- // 如果是单字节字符,则在其后面填充一个字符
- if (char.charCodeAt(0) < 256) {
- row.push(char, padChar);
- } else {
- row.push(char);
- }
- }
- // 将当前行的字符数组添加到结果数组中
- r###lt.push(row);
- }
- //console.log(r###lt);
- // 将竖直排版后的字符数组转换成字符串
- return r###lt.map(row => row.join('')).join('\n');
- }
- //作用:多行结果时将其分为多列来打印
- function multColVerticalText(str, numRows, padChar='_') {
- const lines = str.split('\n');
- const rows = Array.from({length: numRows}, () => []);
- for (let i = 0; i < lines.length; i++) {
- const chars = lines[i].split('');
- const numCols = Math.ceil(chars.length / numRows); // 计算列数
- for (let j = 0; j < numRows; j++) {
- for (let k = 0; k < numCols; k++) {
- // 计算字符在原始字符串中的索引
- const index = k * numRows + j;
- // 将索引越界的字符用填充字符代替
- const char = chars[index] || padChar;
- // 如果是单字节字符,则在其后面填充一个字符
- if (char.charCodeAt(0) < 256) {
- rows[j].push(char, padChar);
- } else {
- rows[j].push(char);
- }
- }
- }
- }
- return rows.map(row => row.join('')).join('\n');
- }
- //作用:规范化,两种想法:
- //1.将其6行6行的展示,每6行之间加一个换行,感觉挺规整的
- //2.自动计算,将其以最多38B列(19个汉字)的策略自动计算行数,难以实现的同时也不太方便观看,就用第一种吧
- //另测试发现当填充符为空格时较小,无法对整,故改为下划线,但其实下划线也无法准确对齐
- function normalization(str, padChar='_', row=6, col=19){
- //threshold是分组阈值,即每组字符串数,我手机上不展开默认显示的最多字数是6行19列,故采用此方式
- //但是测试发现,如果出现半角字符,直接满列有可能超出,即两个半角字符宽度>一个全角字符,为了显示正常,干脆将列数-1,给它留些空余
- let threshold=row*(col-1);
- let groups=[];
- let count=0;
- let tmp='';
- for(let i = 0; i < str.length; i++){
- tmp+=str[i];
- if('\n'===str[i]){
- let t=count%row;
- if(0!==t){
- count+=row-t;
- }
- }else{
- count+=1;
- }
- if(threshold===count){
- //console.log(tmp);
- //console.log(count);
- groups.push(tmp);
- tmp='';
- count=0;
- }
- }
- if(''!==tmp){
- groups.push(tmp);
- }
- //console.log(groups);
- return groups.map(i => multColVerticalText(i, row, padChar)).join('\n\n');
- }
- //作用:根据settings向评论页面插入选项和按钮
- function insertButton(box){
- //如果已经有了则不需要再添加
- if(box.querySelector('div#ths_magic_convert')){
- return;
- }
- var div=document.createElement('div');
- div.id="ths_magic_convert";
- div.setAttribute('style',"display: flex; align-items: center; margin-left: 80px; margin-top: 5px;");
- //为按钮绑定事件
- var button=document.createElement('button');
- button.innerText="魔术变化";
- button.style.marginRight = "3px";
- button.addEventListener('click', function() {
- convert_comment(this);
- });
- div.appendChild(button);
- button=document.createElement('button');
- button.innerText="还原";
- button.style.marginRight = "3px";
- button.addEventListener('click', function() {
- revert_comment(this);
- });
- div.appendChild(button);
- var label=document.createElement('label');
- label.innerText="填充字符:";
- div.appendChild(label);
- var input=document.createElement('input');
- input.id="padChar";
- input.value=settings["padChar"];
- div.appendChild(input);
- label=document.createElement('label');
- label.innerText="行数:";
- div.appendChild(label);
- input=document.createElement('input');
- input.id="row";
- input.value=settings["row"];
- div.appendChild(input);
- label=document.createElement('label');
- label.innerText="列数:";
- div.appendChild(label);
- input=document.createElement('input');
- input.id="col";
- input.value=settings["col"];
- div.appendChild(input);
- //添加对象到页面中
- box.appendChild(div);
- }
- //作用:点击按钮时处理评论区内文本
- //autoCheck:自动检验是否已修改过,修改过则不再更改
- function convert_comment(e, autoCheck=true){
- //console.log(e);
- //console.log('ok');
- // 每次点击按钮都更新settings
- // 获取按钮的父元素
- var div = e.parentNode;
- if(!div){
- alert("未找到div,执行失败");
- throw Error("未找到div,执行失败");
- }
- var in1=div.querySelector('input#padChar');
- //然后获取参数值并修改参数值
- if(!in1){
- alert("未找到in1!将忽略更改padChar");
- }else{
- settings["padChar"]=in1.value;
- }
- var in2=div.querySelector('input#row');
- if(!in1){
- alert("未找到in2!将忽略更改row");
- }else{
- settings["row"]=parseInt(in2.value);
- }
- var in3=div.querySelector('input#col');
- if(!in3){
- alert("未找到in3!将忽略更改col");
- }else{
- settings["col"]=parseInt(in3.value);
- }
- //获取text中文本
- //先获取父结点
- var box = div.parentNode;
- if(!box){
- alert("未找到box,执行失败");
- throw Error("未找到box,执行失败");
- }
- var text=box.querySelector("div.box-normal textarea");
- if(!text){
- alert("未找到text,执行失败");
- throw Error("未找到text,执行失败");
- }
- //执行格式化函数,获取格式化后文本
- var rawStr=text.value;
- if(autoCheck && rawStr===lastNewStr){
- //说明修改过了,不再修改
- return;
- }
- //记录原文本,方便还原
- lastRawStr=rawStr;
- var newStr=normalization(rawStr, settings["padChar"], settings["row"], settings["col"]);
- lastNewStr=newStr;
- //修改评论区内字符串
- text.value=newStr;
- text.dispatchEvent(event);
- }
- //作用:点击按钮时还原评论区内文本
- function revert_comment(e){
- //console.log(e);
- //console.log('ok');
- // 每次点击按钮都更新settings
- // 获取按钮的父元素
- var div = e.parentNode;
- if(!div){
- alert("未找到div,执行失败");
- throw Error("未找到div,执行失败");
- }
- //获取text中文本
- //先获取父结点
- var box = div.parentNode;
- if(!box){
- alert("未找到box,执行失败");
- throw Error("未找到box,执行失败");
- }
- var text=box.querySelector("div.box-normal textarea");
- if(!text){
- alert("未找到text,执行失败");
- throw Error("未找到text,执行失败");
- }
- //如果上一次的原文本存在,则赋值
- if(lastRawStr){
- text.value=lastRawStr;
- text.dispatchEvent(event);
- }
- }
- //作用:为每个评论框添加按钮
- function insertAll(){
- //如果存在浮动评论窗
- // var box=document.querySelector('div.fixed-reply-box');
- // if(box){
- // insertButton(box);
- // }
- //浮动评论窗也包含其中了,所以不需要额外的判断
- document.querySelectorAll('div.reply-box').forEach( (box) => { insertButton(box); });
- }
- (function() {
- 'use strict';
- setInterval(insertAll, 500);
- })();