【用JS自制表格软件玩数据】4. 行列计数器的实现

本文介绍了一种用于Excel表格渲染的列计数器原理及其实现方法,包括ASCII码表示、步进器设计及字符运算表的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当写完本系列后,我会把源代码分享出来给大家。本课程也会持续更新与矫正。欢迎留言指正!

前面已经设计了一个底层渲染类,现在准备在这个底层类的基础上,构建一个渲染单元格的模块。

列计数器

通常在 Excel 表格中,我们会看到横向的标尺使用了:A,B,C,D之类的字母作为标记。
使用了:A,B,C,D之类的字母作为标记

原理

字母的显示,一般都是使用了ASCII码进行表示的,所以,大写字母的 A-Z 是从 65 开始的,到 90 结束。

二进制十进制十六进制缩写/字符解释
00000000000 NUL(null)空字符
00000001101SOH(start of headling)标题开始
00000010202STX (start of text)正文开始
00000011303ETX (end of text)正文结束
00000100404EOT (end of transmission)传输结束
00000101505ENQ (enquiry)请求
00000110606ACK (acknowledge)收到通知
00000111707BEL (bell)响铃
00001000808BS (backspace)退格
000010019 09HT (horizontal tab)水平制表符
0000101010 0A LF (NL line feed, new line)换行键
0000101111 0B VT (vertical tab)垂直制表符
0000110012 0C FF (NP form feed, new page)换页键
00001101130D CR (carriage return) 回车键
00001110140E SO (shift out)不用切换
00001111150F SI (shift in)启用切换
0001000016 10DLE (data link escape) 数据链路转义
000100011711 DC1 (device control 1) 设备控制1
000100101812 DC2 (device control 2)设备控制2
0001001119 13DC3 (device control 3) 设备控制3
000101002014 DC4 (device control 4)设备控制4
000101012115 NAK (negative acknowledge) 拒绝接收
000101102216 SYN (synchronous idle) 同步空闲
0001011123 17ETB (end of trans. block)传输块结束
0001100024 18CAN (cancel) 取消
0001100125 19EM (end of medium)介质中断
0001101026 1ASUB (substitute)替补
0001101127 1BESC (escape) 溢出
0001110028 1CFS (file separator) 文件分割符
0001110129 1DGS (group separator) 分组符
0001111030 1E RS (record separator) 记录分离符
0001111131 1FUS (unit separator)单元分隔符
0010000032 20(space)空格
0010000133 21 ! 
001000103422 " 
0010001135 23# 
0010010036 24$ 
0010010137 25% 
0010011038 26& 
0010011139 27 ' 
0010100040 28( 
001010014129) 
0010101042 2A* 
00101011432B+ 
0010110044 2C , 
0010110145 2D- 
0010111046 2E . 
0010111147 2F/  
0011000048 300 
0011000149 311  
0011001050 32 2 
0011001151 333  
0011010052 34 4 
0011010153 355  
0011011054366 
0011011155 37 7  
0011100056 388  
0011100157 39 9 
0011101058 3A:  
00111011593B; 
0011110060 3C<  
00111101613D= 
0011111062 3E>  
0011111163 3F ? 
0100000064 40@  
010000016541A 
0100001066 42 B  
010000116743C 
0100010068 44 D  
010001016945E 
010001107046 F 
010001117147G 
0100100072 48H  
010010017349I 
0100101074 4AJ  
01001011754BK 
0100110076 4C L  
01001101774DM 
0100111078 4EN  
01001111794FO 
0101000080 50P  
010100018151Q 
010100108252 R 
010100118353S 
0101010084 54T  
0101010185 55 U 
0101011086 56V  
0101011187 57W 
0101100088 58X  
010110018959 Y 
0101101090 5AZ  
0101101191 5B[  
01011100925C\ 
0101110193 5D]  
01011110945E ^ 
0101111195 5F_ 
0110000096 60`  
011000019761 a 
0110001098 62b  
0110001199 63c  
0110010010064d 
01100101101 65e  
01100110102 66f 
01100111103 67g  
01101000104 68 h 
01101001105 69i  
011010101066Aj 
01101011107 6Bk  
01101100108 6C l 
01101101109 6D m 
011011101106En 
01101111111 6Fo  
01110000112 70 p 
01110001113 71q  
01110010114 72 r 
01110011115 73s  
01110100116 74 t 
01110101117 75 u 
01110110118 76v 
01110111119 77w  
01111000120 78x 
01111001121 79y  
01111010122 7A z 
01111011123 7B{ 
01111100124 7C| 
01111101125 7D} 
01111110126 7E ~ 
01111111127 7F DEL (delete)删除

这个就要做一个表格坐标计数器来做统计。

步进器

首先设计一个步进器:

/**
* @property {Function} excel_Row_Increase 行移动定位
* @param {String} chr 例如:A1
* @param {String} num 例如:步进
* @returns {Array} 返回字符串数组,["A","1"]
*/
excel_Row_Increase(chr,num){
	this.caculate = function(chr,num){
		var carry = num; // 进位标志
		let temp = chr.split("");
		var count = temp.length-1;
		while(!(count < 0)) {
			let code = this.charmap[temp[count]] + carry;
			carry = 0;

			if (code > 64 && code < 91){
				temp[count] = String.fromCharCode(code);
			}

			while(code < 65){
				carry--;
				code = code + 26;
				if(code > 64){
					temp[count] = String.fromCharCode(code);
					break;
				}
			}

			while(code > 90){
				carry++;
				code = code - 26;
				if(code < 91){
					temp[count] = String.fromCharCode(code);
					break;
				}
			}
			count--;
		}
		var chr = temp.join("");
		if(chr == "A" && carry < 0){
			return {
				"carry":0,
				"char": chr
			}
		}else{
			return {
				"carry":carry,
				"char":chr
			}
		}
	}

	var result = this.caculate(chr,num);
	var ttt;
	while(result["carry"]!=0){
		ttt = this.caculate("A",--result["carry"]);
		result["char"] = ttt["char"]+result["char"];
		result["carry"] = ttt["carry"];
	}
	return result["char"];

}

字符的运算表

接着,使用步进器创建了一个字符的运算表,这样它就可以快速运算了。

/**
	* @property {Function} setcaculatemap 创建字母运算表
	* @returns {NaN} 无返回
	*/
	setcaculatemap(){
		var begin = "A";
		var mapsize = this.mapsize;
		this.caculatemap = [""];
		
		while(mapsize){
			this.caculatemap.push(begin);
			var begin = this.excel_Row_Increase(begin,1)
			mapsize--;
		}
	}

源码

直接上全部代码:

/**
 * 字符转编码
 */

class charcode{
	/**
	* 创建字符映射集
	*/
	constructor(){
		this.charmap = {};
		var ord = 65;
		var ordmax = ord+26;
		while(ord < ordmax){
			this.charmap[String.fromCharCode(ord)] = ord;
			ord++;
		}
		ord = 65+32;
		ordmax = ord+26;
		while(ord < ordmax){
			this.charmap[String.fromCharCode(ord)] = ord-32;
			ord++;
		}
		this.caculatemap = [];
		this.mapsize = 20;
		this.setcaculatemap();
		// console.log(this.caculatemap);
		
		this.RowColcountMap = {};
	}

	/**
	* @property {Function} setcaculatemap 创建字母运算表
	* @returns {NaN} 无返回
	*/
	setcaculatemap(){
		var begin = "A";
		var mapsize = this.mapsize;
		this.caculatemap = [""];
		
		while(mapsize){
			this.caculatemap.push(begin);
			var begin = this.excel_Row_Increase(begin,1)
			mapsize--;
		}
	}
	
	/**
	* @property {Function} countelementToindex 计算坐标在单元格的位置
	* @param {String} x 输入编码,表格的横向尺寸
	* @param {Number} y 输入编码,表格的纵向尺寸
	* @param {String} elementIndex 输入编码
	* @returns {Number} 返回数字坐标
	*/
	countelementToindex(x,y,elementIndex){
		var count = this.CharToDecimal(x); // 字符转坐标
		var titlecount = count+y+1; // 标头长度
		// var max = count*(y); // 表格总尺寸
		var ttt = this.getRowCol(elementIndex); // 分离行列
		var currentX = this.caculatemap.indexOf(ttt[0]) // 获取行的坐标

		return count*(ttt[1]-1)+currentX+titlecount-1;
	}

	/**
	* @property {Function} countindexToelement 将当前位置转化为坐标
	* @param {String} x 输入编码,表格的横向尺寸
	* @param {Number} y 输入编码,表格的纵向尺寸
	* @param {Number} index 输入位置
	* @returns {Number} 返回坐标
	*/
	countindexToelement(x,y,index){
		var count = this.CharToDecimal(x); // 字符转坐标,获得横向的宽度
		var titlecount = count+y+1; // 标头总数量
		var max = count*(y); // 表格总尺寸
		
		var A = titlecount; // 获取A的位置
		var ttt = index-A+1; // 获取输入框的总个数
		if(ttt >= count){
			var mod = ttt%count;
			if(mod != 0){
				return this.caculatemap[mod]+(parseInt(ttt/count)+1)
			}else{
				return this.caculatemap[count]+(parseInt(ttt/count))
			}
		}else{
			return this.caculatemap[ttt]+1
		}
	}

	/**
	* @property {Function} CHR 编码转字符
	* @param {Number} ord 输入编码
	* @returns {String} 返回字符编码
	*/
	CHR(ord){
		return String.fromCharCode(ord);
	}

	/**
	* @property {Function} ORD 字符转编码
	* @param {String} chr 输入字符串
	* @returns {Number} 返回字符编码
	*/
	ORD(chr){
		return chr.charCodeAt(0);
	}

	/**
	* @property {Function} CharToDecimal 进制字符转十进制
	* @param {String} chr 输入字符串
	* @returns {Number} 返回十进制编码
	*/
	CharToDecimal(Char){
		var Decimal = this.caculatemap.indexOf(Char);
		while(Decimal < 0){
			this.mapsize = this.mapsize*10;
			this.setcaculatemap();
			Decimal = this.caculatemap.indexOf(Char);
		}
		return Decimal;
	}

	/**
	* @property {Function} DecimalToChar 十进制转字符进制
	* @param {Number} Decimal 输入十进制
	* @returns {String} 返回字符进制
	*/
	DecimalToChar(Decimal){
		var length = this.caculatemap.length-1;
		var balance = Decimal - length;
		if(balance > 0){
			this.mapsize = this.mapsize+balance+1;
			this.setcaculatemap();
		}
		return this.caculatemap[Decimal];
	}

	/**
	* @property {Function} CharAdd 字母相加
	* @param {String} CharA 输入字符
	* @param {String} CharB 输入字符
	* @returns {Number} 返回十进制编码
	*/
	CharAdd(CharA,CharB){ // 字母相加
		return this.CharToDecimal(CharA)+this.CharToDecimal(CharB)
	}
	
	/**
	* @property {Function} CharMinus 字母相减
	* @param {String} CharA 输入字符
	* @param {String} CharB 输入字符
	* @returns {Number} 返回十进制编码
	*/
	CharMinus(CharA,CharB){ // 字母相减
		return this.CharToDecimal(CharA)-this.CharToDecimal(CharB)
	}

	/**
	* @property {Function} getRowCol 从行列串中分离出行与列
	* @param {String} RC 行列串,例如:A1
	* @returns {Array} 返回字符串数组,["A","1"]
	*/
	getRowCol(RC){
		var Row = [];
		var Col = [];

		for(var i in RC){
			this.charmap.hasOwnProperty(RC[i])?Col.push(RC[i]):Row.push(RC[i]);
		}
		return [Col.join(""),Row.join("")]
	}

	/**
	* @property {Function} setRectanglecorner 设置四个坐标角落
	* @param {String} a 坐标,例如:A1
	* @param {String} b 坐标,例如:B2
	* @returns {Array} 返回字符串数组,["A","1"]
	*/
	setRectanglecorner(a,b){
		var RSort = [];
		var CSort = [];
		var aRC = this.getRowCol(a);
		var bRC = this.getRowCol(b);
		var Rdistance = this.CharMinus(bRC[0],aRC[0]);
		if(Rdistance < 0){
			RSort = [bRC[0],aRC[0]];
		}else{
			RSort = [aRC[0],bRC[0]];
		}
		if(bRC[1]-aRC[1]<0){
			CSort = [bRC[1],aRC[1]];
		}else{
			CSort = [aRC[1],bRC[1]];
		}
		
		return [[RSort[0],CSort[0]],[RSort[1],CSort[1]]]
	}

	/**
	* @property {Function} GetRectangle 计算出框选区域
	* @param {String} from 行列串,例如:A1
	* @param {String} to 行列串,例如:B2
	* @returns {Array} 返回字符串数组,["A","1"]
	*/
	GetRectangle(from,to){
		var t = this.setRectanglecorner(from,to);
		var fromRC = t[0];
		var toRC = t[1];

		var distance =
		[
			this.CharMinus(toRC[0],fromRC[0]),
			toRC[1]-fromRC[1]
		]
		// console.log(fromRC);
		// console.log(toRC);
		// console.log(distance);
		var Rowcount = parseInt(fromRC[1])+distance[1]+1;
		var ColList = this.caculatemap.slice(this.CharToDecimal(fromRC[0]),this.CharToDecimal(fromRC[0])+distance[0]+1);
		var length = ColList.length;
		var Rectangle = [];
		for(var i = 0;i<length;i++){
			for(var j = fromRC[1];j<Rowcount;j++){
				Rectangle.push(ColList[i]+j);
			}
		}
		return {
			"Rowlength":Rowcount-parseInt(fromRC[1]),
			"Collength":length,
			"Rectangle":Rectangle
		};
	}

	/**
	* @property {Function} excel_Row_Increase 行移动定位
	* @param {String} chr 例如:A1
	* @param {String} num 例如:步进
	* @returns {Array} 返回字符串数组,["A","1"]
	*/
	excel_Row_Increase(chr,num){
		this.caculate = function(chr,num){
			var carry = num; // 进位标志
			let temp = chr.split("");
			var count = temp.length-1;
			while(!(count < 0)) {
				let code = this.charmap[temp[count]] + carry;
				carry = 0;

				if (code > 64 && code < 91){
					temp[count] = String.fromCharCode(code);
				}

				while(code < 65){
					carry--;
					code = code + 26;
					if(code > 64){
						temp[count] = String.fromCharCode(code);
						break;
					}
				}

				while(code > 90){
					carry++;
					code = code - 26;
					if(code < 91){
						temp[count] = String.fromCharCode(code);
						break;
					}
				}
				count--;
			}
			var chr = temp.join("");
			if(chr == "A" && carry < 0){
				return {
					"carry":0,
					"char": chr
				}
			}else{
				return {
					"carry":carry,
					"char":chr
				}
			}
		}

		var result = this.caculate(chr,num);
		var ttt;
		while(result["carry"]!=0){
			ttt = this.caculate("A",--result["carry"]);
			result["char"] = ttt["char"]+result["char"];
			result["carry"] = ttt["carry"];
		}
		return result["char"];

	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妇男主任

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值