标签归档:javascript

[WebGL]使用javascript控制模型形变

此博文由一个话题引出,原话题是通过滑条控制页面上显示3D模型的某个部分,于是我就来尝试了一下。

由于Three.js我也已经2年没碰了,而且在项目里只用过一次,所以并不是很清楚它是否能通过本身的功能达到这个效果,甚至一开始我已经在想手搓点坐标来达到目标了。

但手搓点坐标实在太麻烦了一点,我就回到blender想看看通常用于控制形变的形态键是否可以导出来使用,然后发现确实可以,这样就方便了,只要控制形态键的数值就可以对模型的形态进行定量控制,以下是操作过程: 继续阅读[WebGL]使用javascript控制模型形变

[javascript]字符串映射到固定颜色

碰到一个需要给图表上数据打颜色的场景,我希望对于同一个名字的数据每次出来的颜色都是一样的,于是想了半分钟,搞出了这么个方案

function strToRGB(name){
	let sum=0;
	for(let s of name)sum+=s.codePointAt(0);
	return [sum%128,sum%126,sum%124];
}

原理是把每一个字符的unicode值累加起来,然后对这个和分别取3个余数。

除数选1到256都可以,选多少取决于希望结果出现在哪个范围,我这里为了让结果颜色偏暗,所以选了128左右的除数。

[Javascript]进制转换

由于coding gists即将关闭,所以把此代码片搬到博客

js的Number支持直接使用toString转换到最多36进制(0-9a-z),而此函数支持转换到由传入进制表定义的任何进制。

默认进制表为0-9a-zA-Z的62进制

/*
COPYRIGHT luojia@luojia.me
MIT LICENSE
*/
function conv(n,o,t,olist,tlist){//数,原进制,目标进制[,原数所用字符表,目标字符表]
	var dlist='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
		tnum=[],m,negative=((n+='').trim()[0]=='-'),decnum=0;
	olist||(olist=dlist);
	tlist||(tlist=dlist);
	if(negative)n=n.slice(1);
	for(var i=n.length;i--;)
		decnum+=olist.indexOf(n[i])*Math.pow(o,n.length-i-1);
	for(;decnum!=0;tnum.unshift(tlist[m])){
		m=decnum%t;
		decnum=Math.floor(decnum/t);
	}
	decnum&&tnum.unshift(tlist[decnum]);
	if(tnum.length===0)tnum.unshift(tlist[0]);
	return (negative?'-':'')+tnum.join('');
}


conv(1234,10,2)			//"10011010010"
conv(15,10,16)			//"f"
conv('ABC',16,10)		//"9846"

conv(3245670,10,10,null,'零一二三四五六七八九')		//"三二四五六七零"
conv('①②③',10,2,'〇①②③④⑤⑥⑦⑧⑨')					//"1111011"

 

[Javascript]获取触发message事件的源iframe

翻了翻message事件的属性,没找到可以直接获取事件源iframe的属性,想想也没毛病,毕竟事件也可以是其它窗口post过来的。于是想了个曲线方法。

先让发送源获取焦点,然后获取焦点元素。

window.addEventListener('message',function(msg){
	//做一些事来判断是不是某个iframe发送的消息
	msg.source.focus();
	var sourceFrame=document.activeElement;
});

如果不想影响焦点的话,可以遍历一遍所有的iframe

function findIframe(win){
	var fs=document.querySelectorAll('iframe');
	for(var is=fs.length;is--;){
		if(fs[is].contentWindow==win)
			return fs[is];
	}
}

window.addEventListener('message',function(msg){
	var iframe=findIframe(msg.source);//获取消息源
});

 

[Javascript]Object2HTML

把特定格式的js对象转换成HTML元素

https://github.com/JiaJiaJiang/Object2HTML

演示对象

{_:'div',
	child:[
		{_:'button',prop:{innerHTML:'poi'},event:{'click':function(){alert('niconiconi');}}},
		{_:'br'},
		{_:'video',prop:{src:'http://www.w3school.com.cn/example/html5/mov_bbb.mp4'},attr:{controls:true}}
	]
}

结果

 

[Javascript]判断是否为触摸操作模式

写了段代码可以用来识别用户的操作方式是否为触摸。

首先需要给元素加一个便于批量添加事件的on方法(我自认为这么写没什么毛病

(function(){
	function Extent_On_Event(){
		if(arguments[0] instanceof Array){
			var arg=arguments,es=arguments[0];
			es.forEach(function(e){
				arg[0]=e;
				this.addEventListener.apply(this,arg);
			});
		}else{
			this.addEventListener.apply(this,arguments);
		}
		return this;
	};
	(window.EventTarget||window.Node).prototype.on=Extent_On_Event;
	if(!window.on)Window.prototype.on=Extent_On_Event;
})();

然后定义判断触摸模式的代码

//touchMode
(function(){
	var isTouchMode=window.touchMode=('ontouchstart' in window);
	var mouseEventRec=0;
	function touchModeEvent(node){
		node.on(['touchstart','touchend','touchmove'],function(){
			mouseEventRec&&(mouseEventRec=0);
			if(isTouchMode)return;
			isTouchMode=window.touchMode=true;
			var e=new Event('touchModeChange');
			e.touchMode=isTouchMode;
			window.dispatchEvent(e);
		});
		node.on('mousemove',function(){
			if(!isTouchMode)return;
			mouseEventRec++;
			if(mouseEventRec>4){
				isTouchMode=window.touchMode=false;
				var e=new Event('touchModeChange');
				e.touchMode=isTouchMode;
				window.dispatchEvent(e);
			};
		});
	}
	touchModeEvent(window);
})();

这段代码的作用

  • 在`window` 对象上定义touchMode变量,值为boolean,为true时是触摸模式,为false时是非触摸模式(大概就是鼠标模式)。如果你不希望出现此变量,删除相关赋值代码即可。
  • 模式变动时在`window` 对象上触发`touchModeChange` 事件,event对象的touchMode属性为变动后的值。

在支持触摸的设备上,touchMode默认为true,移动鼠标后会转变为false。我认为这个地方是有点毛病的,但是不知道如何正确判断初始状态。

对于阻止以上代码中事件冒泡到window的元素,需要额外执行相关的监听代码以保证正确性。

[Javascript]base32编解码

用javascript写了个base32的编解码函数

const charTable=[
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
	'Y', 'Z', '2', '3', '4', '5', '6', '7',
	'='
],allowedPeddingCount=[6,4,3,1,0];

function str_split(str,length){
	if(typeof str !== 'string')return [];
	let a=[],i=0;
	length||(length=1);
	do{
		a.push(str.substr(i,length));
		i+=length;
	}while(i<str.length);
	return a;
}

const Base32={
	encode:function(str,padding){
		if(!str)return '';
		let binaryString='';
		for (let i = 0;i<str.length;i++) {
			let bin=str.charCodeAt(i).toString(2);
			binaryString+=('0'.repeat(8-bin.length)+bin);
		}
		let fiveBitBinaryArray=str_split(binaryString,5),base32='';
		for(let i=0;i<fiveBitBinaryArray.length;i++){
			let bin=fiveBitBinaryArray[i];
			base32+=charTable[parseInt(bin+'0'.repeat(5-bin.length),2)];
		}
		let x=binaryString.length%40;
		if (padding && x != 0) {
			if (x == 8)base32+=charTable[32].repeat(6);
			else if(x===16)base32+=charTable[32].repeat(4);
			else if(x===24)base32+=charTable[32].repeat(3);
			else if(x===32)base32+=charTable[32];
		}
		return base32;
	},
	decode:function(str){
		if(!str)return '';
		let paddingMatch=str.match(/\=+$/),
			paddingCharCount=paddingMatch?paddingMatch[0].length:0;
		if(allowedPeddingCount.indexOf(paddingCharCount)<0)return false;
		for (let i=0;i<4;i++){
			if (paddingCharCount===allowedPeddingCount[i] 
				&& str.substr(-(allowedPeddingCount[i]))!=charTable[32].repeat(allowedPeddingCount[i]))
				return false;
		}
		str=str.replace(/\=+$/,'');
		let binaryString = "";
		for (let i=0;i<str.length;i+=8) {
			let x='';
			if (charTable.indexOf(str[i])<0)return false;
			for (let j=0;j<8;j++) {
				let bin=charTable.indexOf(str[i+j]).toString(2);
				x+='0'.repeat(5-bin.length)+bin;
			}
			let eightBits=str_split(x,8);
			for (let z = 0; z < eightBits.length; z++) {
				let y,cd=parseInt(eightBits[z],2,10);
				binaryString+=((y=String.fromCharCode(cd))||cd==48)?y:"";
			}
		}
		return binaryString;
	}
}

[Node.js]拦截process.stdout和process.stderr

由于解决这个问题花了我一些时间,所以记录一下说不定可以帮到其他人。

process.stdout是一个getter,所以我们不能用普通的替换来换掉process.stdout来拦截写入它的数据。同时,process.stdout是一个Writable Stream,所以也不能简单地直接从它里面获取写入的数据。

 

一开始我花了不少时间来研究怎么可以从这个Writable Stream里读出数据,但是这似乎太麻烦了,然后我甚至想到了利用child process来拦截数据。最后发现其实很简单,我们只要重新定义这个Getter就可以了。

var stream=require('stream');


var rawStdout=process.stdout,//先拿到原来的stdout
	newStdout=new stream.PassThrough();//创建一个passthrough流,这是一种特殊的Transform流,会直接把写入的数据吐出来
process.__defineGetter__('stdout',function(){//重新定义process.stdout的Getter
	return newStdout;//返回我们的passthrough流
});

这样我们就成功拦截到标准输出了,要注意,这段代码必须放在有任何输出之前,一旦在之前有了内容输出,它就没用了。

然后我们就可以自己决定怎么处理stdout了比如:

newStdout.pipe(rawStdout);//内容输出到控制台

newStdout.pipe(文件的writable stream);//内容写入文件

newStdout.pipe(其它可写流);//随你怎么处理

 

 

同理,process.stderr也可以这样拦截